Merge origin/main into dev (second round)
Resolve 21 conflicts from latest main (provider profiles, Windows fixes, 0.1.55-rc.1). Fix all type errors and syntax issues from merge artifacts.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { platform } from "node:os";
|
||||
import { execCommand } from "@getpaseo/server";
|
||||
|
||||
export interface NodePathFromPidResult {
|
||||
nodePath: string | null;
|
||||
@@ -13,63 +13,37 @@ function normalizeError(error: unknown): string {
|
||||
return String(error);
|
||||
}
|
||||
|
||||
function resolveNodePathFromPidUnix(pid: number): NodePathFromPidResult {
|
||||
const result = spawnSync("ps", ["-o", "comm=", "-p", String(pid)], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return { nodePath: null, error: `ps failed: ${normalizeError(result.error)}` };
|
||||
async function resolveNodePathFromPidUnix(pid: number): Promise<NodePathFromPidResult> {
|
||||
try {
|
||||
const { stdout } = await execCommand("ps", ["-o", "comm=", "-p", String(pid)]);
|
||||
const resolved = stdout.trim();
|
||||
return resolved
|
||||
? { nodePath: resolved }
|
||||
: { nodePath: null, error: "ps returned an empty command path" };
|
||||
} catch (error) {
|
||||
return { nodePath: null, error: `ps failed: ${normalizeError(error)}` };
|
||||
}
|
||||
|
||||
if ((result.status ?? 1) !== 0) {
|
||||
const details = result.stderr?.trim();
|
||||
return {
|
||||
nodePath: null,
|
||||
error: details ? `ps failed: ${details}` : `ps exited with code ${result.status ?? 1}`,
|
||||
};
|
||||
}
|
||||
|
||||
const resolved = result.stdout.trim();
|
||||
return resolved
|
||||
? { nodePath: resolved }
|
||||
: { nodePath: null, error: "ps returned an empty command path" };
|
||||
}
|
||||
|
||||
function runProcessProbe(
|
||||
async function runProcessProbe(
|
||||
command: string,
|
||||
args: string[],
|
||||
): {
|
||||
): Promise<{
|
||||
resolved: string | null;
|
||||
error?: string;
|
||||
} {
|
||||
const result = spawnSync(command, args, {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return { resolved: null, error: `${command} failed: ${normalizeError(result.error)}` };
|
||||
}> {
|
||||
try {
|
||||
const { stdout } = await execCommand(command, args);
|
||||
const resolved = stdout.trim();
|
||||
return resolved
|
||||
? { resolved }
|
||||
: { resolved: null, error: `${command} returned no executable path` };
|
||||
} catch (error) {
|
||||
return { resolved: null, error: `${command} failed: ${normalizeError(error)}` };
|
||||
}
|
||||
|
||||
if ((result.status ?? 1) !== 0) {
|
||||
const details = result.stderr?.trim();
|
||||
return {
|
||||
resolved: null,
|
||||
error: details
|
||||
? `${command} failed: ${details}`
|
||||
: `${command} exited with code ${result.status ?? 1}`,
|
||||
};
|
||||
}
|
||||
|
||||
const resolved = result.stdout.trim();
|
||||
return resolved
|
||||
? { resolved }
|
||||
: { resolved: null, error: `${command} returned no executable path` };
|
||||
}
|
||||
|
||||
function resolveNodePathFromPidWindows(pid: number): NodePathFromPidResult {
|
||||
async function resolveNodePathFromPidWindows(pid: number): Promise<NodePathFromPidResult> {
|
||||
const probes: Array<{
|
||||
label: string;
|
||||
command: string;
|
||||
@@ -103,7 +77,7 @@ function resolveNodePathFromPidWindows(pid: number): NodePathFromPidResult {
|
||||
|
||||
const errors: string[] = [];
|
||||
for (const probe of probes) {
|
||||
const result = runProcessProbe(probe.command, probe.args);
|
||||
const result = await runProcessProbe(probe.command, probe.args);
|
||||
if (result.resolved) {
|
||||
const resolved = probe.parseValue ? probe.parseValue(result.resolved) : result.resolved;
|
||||
if (resolved) {
|
||||
@@ -123,8 +97,8 @@ function resolveNodePathFromPidWindows(pid: number): NodePathFromPidResult {
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveNodePathFromPid(pid: number): NodePathFromPidResult {
|
||||
export async function resolveNodePathFromPid(pid: number): Promise<NodePathFromPidResult> {
|
||||
return platform() === "win32"
|
||||
? resolveNodePathFromPidWindows(pid)
|
||||
: resolveNodePathFromPidUnix(pid);
|
||||
? await resolveNodePathFromPidWindows(pid)
|
||||
: await resolveNodePathFromPidUnix(pid);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { Command } from "commander";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createRequire } from "node:module";
|
||||
import { promisify } from "node:util";
|
||||
import { getOrCreateServerId, findExecutable, applyProviderEnv } from "@getpaseo/server";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
import {
|
||||
getOrCreateServerId,
|
||||
findExecutable,
|
||||
applyProviderEnv,
|
||||
execCommand,
|
||||
} from "@getpaseo/server";
|
||||
import { tryConnectToDaemon } from "../../utils/client.js";
|
||||
import type { CommandOptions, ListResult, OutputSchema } from "../../output/index.js";
|
||||
import { resolveLocalDaemonState, resolveTcpHostFromListen } from "./local-daemon.js";
|
||||
@@ -178,11 +179,9 @@ async function checkProviderBinary(
|
||||
}
|
||||
const env = applyProviderEnv(process.env);
|
||||
try {
|
||||
const { stdout } = await execFileAsync(binaryPath, ["--version"], {
|
||||
encoding: "utf8",
|
||||
const { stdout } = await execCommand(binaryPath, ["--version"], {
|
||||
timeout: 5000,
|
||||
env,
|
||||
windowsHide: true,
|
||||
});
|
||||
return { path: binaryPath, version: stdout.trim() || null };
|
||||
} catch {
|
||||
@@ -224,7 +223,7 @@ export async function runStatusCommand(
|
||||
if (!state.running) {
|
||||
daemonNode = "-";
|
||||
} else if (state.pidInfo?.pid) {
|
||||
const fromPid = resolveNodePathFromPid(state.pidInfo.pid);
|
||||
const fromPid = await resolveNodePathFromPid(state.pidInfo.pid);
|
||||
daemonNode = fromPid.nodePath ?? `unknown (${fromPid.error ?? "could not resolve from PID"})`;
|
||||
} else {
|
||||
daemonNode = "unknown (no PID available)";
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { Command } from "commander";
|
||||
import type { CommandOptions, ListResult, OutputSchema } from "../../output/index.js";
|
||||
import { AGENT_PROVIDER_DEFINITIONS } from "@getpaseo/server";
|
||||
import { tryConnectToDaemon } from "../../utils/client.js";
|
||||
|
||||
/** Provider list item for display */
|
||||
export interface ProviderListItem {
|
||||
provider: string;
|
||||
label: string;
|
||||
status: string;
|
||||
defaultMode: string;
|
||||
modes: string;
|
||||
@@ -13,16 +15,22 @@ export interface ProviderListItem {
|
||||
/** Derive provider list from the manifest — single source of truth */
|
||||
const PROVIDERS: ProviderListItem[] = AGENT_PROVIDER_DEFINITIONS.map((def) => ({
|
||||
provider: def.id,
|
||||
label: def.label,
|
||||
status: "available",
|
||||
defaultMode: def.defaultModeId ?? "-",
|
||||
modes: def.modes.length > 0 ? def.modes.map((m) => m.label).join(", ") : "-",
|
||||
}));
|
||||
|
||||
function getStaticProviders(): ProviderListItem[] {
|
||||
return PROVIDERS;
|
||||
}
|
||||
|
||||
/** Schema for provider ls output */
|
||||
export const providerLsSchema: OutputSchema<ProviderListItem> = {
|
||||
idField: "provider",
|
||||
columns: [
|
||||
{ header: "PROVIDER", field: "provider", width: 12 },
|
||||
{ header: "LABEL", field: "label", width: 16 },
|
||||
{
|
||||
header: "STATUS",
|
||||
field: "status",
|
||||
@@ -45,13 +53,39 @@ export interface ProviderLsOptions extends CommandOptions {
|
||||
}
|
||||
|
||||
export async function runLsCommand(
|
||||
_options: ProviderLsOptions,
|
||||
options: ProviderLsOptions,
|
||||
_command: Command,
|
||||
): Promise<ProviderLsResult> {
|
||||
// Provider data is static - no daemon connection needed
|
||||
return {
|
||||
type: "list",
|
||||
data: PROVIDERS,
|
||||
schema: providerLsSchema,
|
||||
};
|
||||
const client = await tryConnectToDaemon({ host: options.host });
|
||||
|
||||
if (!client) {
|
||||
return {
|
||||
type: "list",
|
||||
data: getStaticProviders(),
|
||||
schema: providerLsSchema,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const snapshot = await client.getProvidersSnapshot();
|
||||
return {
|
||||
type: "list",
|
||||
data: snapshot.entries.map((entry) => ({
|
||||
provider: entry.provider,
|
||||
label: entry.label ?? entry.provider,
|
||||
status: entry.status === "ready" ? "available" : entry.status,
|
||||
defaultMode: entry.defaultModeId ?? "default",
|
||||
modes: (entry.modes ?? []).map((mode) => mode.label).join(", "),
|
||||
})),
|
||||
schema: providerLsSchema,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
type: "list",
|
||||
data: getStaticProviders(),
|
||||
schema: providerLsSchema,
|
||||
};
|
||||
} finally {
|
||||
await client.close().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user