refactor: dedupe runtime and helper flows

This commit is contained in:
Peter Steinberger
2026-03-02 12:53:19 +00:00
parent 5d3f066bbd
commit b02b94673f
17 changed files with 819 additions and 610 deletions

View File

@@ -89,6 +89,45 @@ function formatBindingConflicts(
);
}
function resolveParsedBindingsOrExit(params: {
runtime: RuntimeEnv;
cfg: NonNullable<Awaited<ReturnType<typeof requireValidConfig>>>;
agentId: string;
bindValues: string[] | undefined;
emptyMessage: string;
}): ReturnType<typeof parseBindingSpecs> | null {
const specs = (params.bindValues ?? []).map((value) => value.trim()).filter(Boolean);
if (specs.length === 0) {
params.runtime.error(params.emptyMessage);
params.runtime.exit(1);
return null;
}
const parsed = parseBindingSpecs({ agentId: params.agentId, specs, config: params.cfg });
if (parsed.errors.length > 0) {
params.runtime.error(parsed.errors.join("\n"));
params.runtime.exit(1);
return null;
}
return parsed;
}
function emitJsonPayload(params: {
runtime: RuntimeEnv;
json: boolean | undefined;
payload: unknown;
conflictCount?: number;
}): boolean {
if (!params.json) {
return false;
}
params.runtime.log(JSON.stringify(params.payload, null, 2));
if ((params.conflictCount ?? 0) > 0) {
params.runtime.exit(1);
}
return true;
}
export async function agentsBindingsCommand(
opts: AgentsBindingsListOptions,
runtime: RuntimeEnv = defaultRuntime,
@@ -157,17 +196,14 @@ export async function agentsBindCommand(
return;
}
const specs = (opts.bind ?? []).map((value) => value.trim()).filter(Boolean);
if (specs.length === 0) {
runtime.error("Provide at least one --bind <channel[:accountId]>.");
runtime.exit(1);
return;
}
const parsed = parseBindingSpecs({ agentId, specs, config: cfg });
if (parsed.errors.length > 0) {
runtime.error(parsed.errors.join("\n"));
runtime.exit(1);
const parsed = resolveParsedBindingsOrExit({
runtime,
cfg,
agentId,
bindValues: opts.bind,
emptyMessage: "Provide at least one --bind <channel[:accountId]>.",
});
if (!parsed) {
return;
}
@@ -186,11 +222,9 @@ export async function agentsBindCommand(
skipped: result.skipped.map(describeBinding),
conflicts: formatBindingConflicts(result.conflicts),
};
if (opts.json) {
runtime.log(JSON.stringify(payload, null, 2));
if (result.conflicts.length > 0) {
runtime.exit(1);
}
if (
emitJsonPayload({ runtime, json: opts.json, payload, conflictCount: result.conflicts.length })
) {
return;
}
@@ -267,25 +301,21 @@ export async function agentsUnbindCommand(
missing: [] as string[],
conflicts: [] as string[],
};
if (opts.json) {
runtime.log(JSON.stringify(payload, null, 2));
if (emitJsonPayload({ runtime, json: opts.json, payload })) {
return;
}
runtime.log(`Removed ${removed.length} binding(s) for "${agentId}".`);
return;
}
const specs = (opts.bind ?? []).map((value) => value.trim()).filter(Boolean);
if (specs.length === 0) {
runtime.error("Provide at least one --bind <channel[:accountId]> or use --all.");
runtime.exit(1);
return;
}
const parsed = parseBindingSpecs({ agentId, specs, config: cfg });
if (parsed.errors.length > 0) {
runtime.error(parsed.errors.join("\n"));
runtime.exit(1);
const parsed = resolveParsedBindingsOrExit({
runtime,
cfg,
agentId,
bindValues: opts.bind,
emptyMessage: "Provide at least one --bind <channel[:accountId]> or use --all.",
});
if (!parsed) {
return;
}
@@ -303,11 +333,9 @@ export async function agentsUnbindCommand(
missing: result.missing.map(describeBinding),
conflicts: formatBindingConflicts(result.conflicts),
};
if (opts.json) {
runtime.log(JSON.stringify(payload, null, 2));
if (result.conflicts.length > 0) {
runtime.exit(1);
}
if (
emitJsonPayload({ runtime, json: opts.json, payload, conflictCount: result.conflicts.length })
) {
return;
}

View File

@@ -28,6 +28,13 @@ type MemoryPluginStatus = {
type DeferredResult<T> = { ok: true; value: T } | { ok: false; error: unknown };
type GatewayProbeSnapshot = {
gatewayConnection: ReturnType<typeof buildGatewayConnectionDetails>;
remoteUrlMissing: boolean;
gatewayMode: "local" | "remote";
gatewayProbe: Awaited<ReturnType<typeof probeGateway>> | null;
};
function deferResult<T>(promise: Promise<T>): Promise<DeferredResult<T>> {
return promise.then(
(value) => ({ ok: true, value }),
@@ -54,6 +61,43 @@ function resolveMemoryPluginStatus(cfg: ReturnType<typeof loadConfig>): MemoryPl
return { enabled: true, slot: raw || "memory-core" };
}
async function resolveGatewayProbeSnapshot(params: {
cfg: ReturnType<typeof loadConfig>;
opts: { timeoutMs?: number; all?: boolean };
}): Promise<GatewayProbeSnapshot> {
const gatewayConnection = buildGatewayConnectionDetails();
const isRemoteMode = params.cfg.gateway?.mode === "remote";
const remoteUrlRaw =
typeof params.cfg.gateway?.remote?.url === "string" ? params.cfg.gateway.remote.url : "";
const remoteUrlMissing = isRemoteMode && !remoteUrlRaw.trim();
const gatewayMode = isRemoteMode ? "remote" : "local";
const gatewayProbe = remoteUrlMissing
? null
: await probeGateway({
url: gatewayConnection.url,
auth: resolveGatewayProbeAuth(params.cfg),
timeoutMs: Math.min(params.opts.all ? 5000 : 2500, params.opts.timeoutMs ?? 10_000),
}).catch(() => null);
return { gatewayConnection, remoteUrlMissing, gatewayMode, gatewayProbe };
}
async function resolveChannelsStatus(params: {
gatewayReachable: boolean;
opts: { timeoutMs?: number; all?: boolean };
}) {
if (!params.gatewayReachable) {
return null;
}
return await callGateway({
method: "channels.status",
params: {
probe: false,
timeoutMs: Math.min(8000, params.opts.timeoutMs ?? 10_000),
},
timeoutMs: Math.min(params.opts.all ? 5000 : 2500, params.opts.timeoutMs ?? 10_000),
}).catch(() => null);
}
export type StatusScanResult = {
cfg: ReturnType<typeof loadConfig>;
osSummary: ReturnType<typeof resolveOsSummary>;
@@ -123,20 +167,9 @@ async function scanStatusJsonFast(opts: {
runExec(cmd, args, { timeoutMs: 1200, maxBuffer: 200_000 }),
).catch(() => null);
const gatewayConnection = buildGatewayConnectionDetails();
const isRemoteMode = cfg.gateway?.mode === "remote";
const remoteUrlRaw = typeof cfg.gateway?.remote?.url === "string" ? cfg.gateway.remote.url : "";
const remoteUrlMissing = isRemoteMode && !remoteUrlRaw.trim();
const gatewayMode = isRemoteMode ? "remote" : "local";
const gatewayProbePromise = remoteUrlMissing
? Promise.resolve<Awaited<ReturnType<typeof probeGateway>> | null>(null)
: probeGateway({
url: gatewayConnection.url,
auth: resolveGatewayProbeAuth(cfg),
timeoutMs: Math.min(opts.all ? 5000 : 2500, opts.timeoutMs ?? 10_000),
}).catch(() => null);
const gatewayProbePromise = resolveGatewayProbeSnapshot({ cfg, opts });
const [tailscaleDns, update, agentStatus, gatewayProbe, summary] = await Promise.all([
const [tailscaleDns, update, agentStatus, gatewaySnapshot, summary] = await Promise.all([
tailscaleDnsPromise,
updatePromise,
agentStatusPromise,
@@ -148,20 +181,12 @@ async function scanStatusJsonFast(opts: {
? `https://${tailscaleDns}${normalizeControlUiBasePath(cfg.gateway?.controlUi?.basePath)}`
: null;
const { gatewayConnection, remoteUrlMissing, gatewayMode, gatewayProbe } = gatewaySnapshot;
const gatewayReachable = gatewayProbe?.ok === true;
const gatewaySelf = gatewayProbe?.presence
? pickGatewaySelfPresence(gatewayProbe.presence)
: null;
const channelsStatusPromise = gatewayReachable
? callGateway({
method: "channels.status",
params: {
probe: false,
timeoutMs: Math.min(8000, opts.timeoutMs ?? 10_000),
},
timeoutMs: Math.min(opts.all ? 5000 : 2500, opts.timeoutMs ?? 10_000),
}).catch(() => null)
: Promise.resolve(null);
const channelsStatusPromise = resolveChannelsStatus({ gatewayReachable, opts });
const memoryPlugin = resolveMemoryPluginStatus(cfg);
const memoryPromise = resolveMemoryStatusSnapshot({ cfg, agentStatus, memoryPlugin });
const [channelsStatus, memory] = await Promise.all([channelsStatusPromise, memoryPromise]);
@@ -246,19 +271,8 @@ export async function scanStatus(
progress.tick();
progress.setLabel("Probing gateway…");
const gatewayConnection = buildGatewayConnectionDetails();
const isRemoteMode = cfg.gateway?.mode === "remote";
const remoteUrlRaw =
typeof cfg.gateway?.remote?.url === "string" ? cfg.gateway.remote.url : "";
const remoteUrlMissing = isRemoteMode && !remoteUrlRaw.trim();
const gatewayMode = isRemoteMode ? "remote" : "local";
const gatewayProbe = remoteUrlMissing
? null
: await probeGateway({
url: gatewayConnection.url,
auth: resolveGatewayProbeAuth(cfg),
timeoutMs: Math.min(opts.all ? 5000 : 2500, opts.timeoutMs ?? 10_000),
}).catch(() => null);
const { gatewayConnection, remoteUrlMissing, gatewayMode, gatewayProbe } =
await resolveGatewayProbeSnapshot({ cfg, opts });
const gatewayReachable = gatewayProbe?.ok === true;
const gatewaySelf = gatewayProbe?.presence
? pickGatewaySelfPresence(gatewayProbe.presence)
@@ -266,16 +280,7 @@ export async function scanStatus(
progress.tick();
progress.setLabel("Querying channel status…");
const channelsStatus = gatewayReachable
? await callGateway({
method: "channels.status",
params: {
probe: false,
timeoutMs: Math.min(8000, opts.timeoutMs ?? 10_000),
},
timeoutMs: Math.min(opts.all ? 5000 : 2500, opts.timeoutMs ?? 10_000),
}).catch(() => null)
: null;
const channelsStatus = await resolveChannelsStatus({ gatewayReachable, opts });
const channelIssues = channelsStatus ? collectChannelStatusIssues(channelsStatus) : [];
progress.tick();