@agentconnect.md/daemon 1.0.0-rc.286 → 1.0.0-rc.288
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-host-CZQVPKE-.js → acp-host-BlkeNrQB.js} +2 -2
- package/dist/{acp-host-CZQVPKE-.js.map → acp-host-BlkeNrQB.js.map} +1 -1
- package/dist/{agent-list-BgEiQL1D.js → agent-list-DS3NGUJO.js} +2 -2
- package/dist/{agent-list-BgEiQL1D.js.map → agent-list-DS3NGUJO.js.map} +1 -1
- package/dist/{chat-12bZUnhV.js → chat-Nq98n5LI.js} +3 -3
- package/dist/{chat-12bZUnhV.js.map → chat-Nq98n5LI.js.map} +1 -1
- package/dist/{git-credential-n8fSKCLK.js → git-credential-DLRF9bOx.js} +2 -2
- package/dist/{git-credential-n8fSKCLK.js.map → git-credential-DLRF9bOx.js.map} +1 -1
- package/dist/{gitcred-server-CMC80O_1.js → gitcred-server-Dhp-tyEI.js} +18 -1
- package/dist/gitcred-server-Dhp-tyEI.js.map +1 -0
- package/dist/index.js +5 -5
- package/dist/{load-agents-DLEvTgkd.js → load-agents-BLNSM987.js} +3 -2
- package/dist/load-agents-BLNSM987.js.map +1 -0
- package/dist/{login-CXPIehhv.js → login-B4KagoWu.js} +3 -3
- package/dist/{login-CXPIehhv.js.map → login-B4KagoWu.js.map} +1 -1
- package/dist/run-foreground-COW0zak6.js +2 -0
- package/dist/{run-foreground-ClL2ha-n.js → run-foreground-D4TMcwTn.js} +156 -12
- package/dist/{run-foreground-ClL2ha-n.js.map → run-foreground-D4TMcwTn.js.map} +1 -1
- package/package.json +1 -1
- package/dist/gitcred-server-CMC80O_1.js.map +0 -1
- package/dist/load-agents-DLEvTgkd.js.map +0 -1
- package/dist/run-foreground-CtYL7rRR.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-CXPIehhv.js","names":["realRunForeground"],"sources":["../src/cp/auth-probe.ts","../src/cli/login.ts"],"sourcesContent":["/**\n * `probeAuth` — a one-shot CP auth check used by `agentconnect login`. It dials\n * the CP, sends ONLY the `auth` frame (no `register`), and resolves with the\n * authoritative daemonId from `auth/ok`, or a human-readable failure reason on\n * a `4401` close, a correlated `error`, a dial failure, or a timeout. It always\n * closes the socket and never leaves a connection open.\n */\nimport { buildEnvelope, decodeEnvelope, encode, isFrame } from '@agentconnect.md/protocol'\nimport { ClientTransport, type Transport } from '@agentconnect.md/connection'\nimport { CP_SUBPROTOCOL, CP_WS_PATH } from './client.js'\n\nexport interface ProbeResult {\n ok: boolean\n daemonId?: string\n reason?: string\n}\n\nexport interface ProbeOpts {\n url: string\n token: string\n daemonId?: string\n agentVersion?: string\n timeoutMs?: number\n /** Injectable for tests; defaults to dialing the real WebSocket. */\n connect?: () => Promise<Transport>\n}\n\nexport async function probeAuth(opts: ProbeOpts): Promise<ProbeResult> {\n const timeoutMs = opts.timeoutMs ?? 10_000\n const dial = opts.connect ?? (() => ClientTransport.dial(opts.url, { subprotocol: CP_SUBPROTOCOL, path: CP_WS_PATH }))\n\n let transport: Transport\n try {\n transport = await dial()\n } catch (err) {\n return { ok: false, reason: `cannot reach ${opts.url}: ${(err as Error).message}` }\n }\n\n const authPayload: Record<string, unknown> = {\n apiKey: opts.token,\n agentVersion: opts.agentVersion ?? 'probe'\n }\n if (opts.daemonId) authPayload.daemonId = opts.daemonId\n const frame = buildEnvelope('auth', authPayload)\n\n return await new Promise<ProbeResult>((resolve) => {\n let settled = false\n const done = (r: ProbeResult): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n try {\n transport.close(1000, 'probe done')\n } catch {\n // socket may already be closed\n }\n resolve(r)\n }\n\n const timer = setTimeout(\n () => done({ ok: false, reason: `timed out after ${timeoutMs}ms waiting for auth/ok` }),\n timeoutMs\n )\n\n transport.onMessage((text) => {\n const decoded = decodeEnvelope(text)\n if (!decoded.ok) return\n const f = decoded.frame\n if (f.corr !== frame.id) return\n if (f.type === 'error') {\n const e = f.payload as { code: string; message: string }\n done({ ok: false, reason: `${e.code}: ${e.message}` })\n return\n }\n if (isFrame('auth/ok')(f)) {\n const ok = f.payload as { daemonId: string }\n done({ ok: true, daemonId: ok.daemonId })\n }\n })\n\n transport.onClose((code) => {\n if (code === 4401) done({ ok: false, reason: 'authentication failed (4401) — check the daemon token' })\n else done({ ok: false, reason: `connection closed (${code}) before auth/ok` })\n })\n\n transport.send(encode(frame))\n })\n}\n","/**\n * `agentconnect login` — interactive onboarding. It tests the Control Plane auth\n * flow, persists credentials only on success, then offers to install the OS\n * service. If installed it starts the service (`up`) and returns; otherwise it\n * runs the daemon in the foreground (the same path as `agentconnect run`).\n *\n * `persistCredentials` is the pure config-merge half (token-only onboarding:\n * `daemonId` is omitted unless given — the first connect adopts it from the\n * token's `sub`). `runLogin` is the orchestrator, with injectable deps for tests.\n */\nimport { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { ConfigSchema } from '../config/config-schema.js'\nimport { resolveRoot, configPath } from '../paths.js'\nimport { DAEMON_VERSION } from '../version.js'\nimport { probeAuth, type ProbeResult } from '../cp/auth-probe.js'\nimport { resolveController, type InstallOpts } from '../service/index.js'\nimport { runForeground as realRunForeground } from './run-foreground.js'\n\nexport interface PersistCredsOpts {\n cpUrl: string\n cpKey: string\n daemonId?: string\n root?: string\n configPath?: string\n}\n\n/** Merge url/token into config.json (validated), returning the written path. */\nexport function persistCredentials(opts: PersistCredsOpts): string {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n const raw: Record<string, unknown> = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n\n const cp = (raw.controlPlane as Record<string, unknown> | undefined) ?? {}\n raw.controlPlane = { ...cp, url: opts.cpUrl, key: opts.cpKey, enabled: true }\n if (opts.daemonId) raw.daemonId = opts.daemonId\n\n ConfigSchema.parse(raw) // fail before writing if the merge is invalid\n\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n return file\n}\n\nexport interface RunLoginOpts {\n cpUrl?: string\n cpKey?: string\n daemonId?: string\n root?: string\n configPath?: string\n /** The resolved CLI entry path. Must be supplied by the CLI entry point\n * (index.ts) so the correct bundle path is used rather than inferring it\n * from login.ts's own import.meta.url (which is the same bundle but the\n * `../` path calculation would be wrong). Falls back to import.meta.url\n * for the pure test path where login.ts is loaded standalone. */\n cliEntry?: string\n}\n\n/**\n * Build the InstallOpts for the OS service installer.\n *\n * Exported as a pure helper so it can be unit-tested independently of the\n * full `runLogin` orchestration. The caller (index.ts) passes `cliEntry` so\n * the correct bundle path always flows through; if omitted we fall back to\n * `import.meta.url` (correct only when login.ts is the entry, e.g. in tests).\n */\nexport function buildInstallOpts(opts: { root?: string; cliEntry?: string }): InstallOpts {\n return {\n execPath: process.execPath,\n cliEntry: opts.cliEntry ?? fileURLToPath(import.meta.url),\n includeRootEnv: resolveRoot(opts.root) !== resolveRoot(undefined)\n }\n}\n\nexport interface LoginDeps {\n probe: (o: { url: string; token: string }) => Promise<ProbeResult>\n installService: () => Promise<void>\n runForeground: () => Promise<void>\n out: NodeJS.WritableStream\n input: NodeJS.ReadableStream\n isTTY: boolean\n}\n\nfunction realDeps(opts: RunLoginOpts): LoginDeps {\n return {\n probe: (o) => probeAuth({ url: o.url, token: o.token, agentVersion: DAEMON_VERSION }),\n installService: async () => {\n const controller = resolveController({ root: opts.root })\n await controller.install(buildInstallOpts(opts))\n await controller.up()\n },\n runForeground: () =>\n realRunForeground({\n root: opts.root,\n overrides: { cpUrl: opts.cpUrl, cpKey: opts.cpKey, daemonId: opts.daemonId }\n }),\n out: process.stdout,\n input: process.stdin,\n isTTY: Boolean(process.stdin.isTTY)\n }\n}\n\n/** Read the next line from a readline interface via async iterator. */\nasync function nextLine(\n iter: AsyncIterableIterator<string>,\n prompt: string,\n out: NodeJS.WritableStream\n): Promise<string> {\n out.write(prompt)\n const { value, done } = await iter.next()\n if (done) throw new Error(`unexpected end of input waiting for: ${prompt}`)\n return (value as string).trim()\n}\n\nexport async function runLogin(opts: RunLoginOpts, partial: Partial<LoginDeps> = {}): Promise<void> {\n const deps: LoginDeps = { ...realDeps(opts), ...partial }\n const { out } = deps\n\n // ── Non-interactive: flags required; probe; persist. No prompts, no handoff. ──\n if (!deps.isTTY) {\n if (!opts.cpUrl) throw new Error('login requires --cp-url <url>')\n if (!opts.cpKey) throw new Error('login requires --cp-key <key>')\n const r = await deps.probe({ url: opts.cpUrl, token: opts.cpKey })\n if (!r.ok) throw new Error(`authentication failed: ${r.reason}`)\n persistCredentials({ ...opts, cpUrl: opts.cpUrl, cpKey: opts.cpKey })\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''} — credentials saved\\n`)\n return\n }\n\n // Both interactive handoffs read `<root>/config.json`: the installed service\n // runs `… run` (no `--config`), and the foreground `Daemon` loads by root. A\n // custom `--config` would persist credentials the daemon never reads, so the\n // install/run branch would look successful but start without them. Reject it;\n // `--root` is the knob that keeps persist + service + foreground in sync.\n if (opts.configPath) {\n throw new Error(\n 'interactive login does not support --config — use --root <dir> so the daemon and the installed service read the same config'\n )\n }\n\n // ── Interactive ──\n const rl = createInterface({ input: deps.input, output: out })\n const iter = rl[Symbol.asyncIterator]()\n let url = opts.cpUrl\n let token = opts.cpKey\n try {\n if (!url) url = await nextLine(iter, 'Control Plane URL: ', out)\n\n for (let attempt = 0; ; attempt++) {\n if (!token) token = await nextLine(iter, 'Daemon API key: ', out)\n out.write(`Testing connection to ${url}…\\n`)\n const r = await deps.probe({ url: url!, token: token! })\n if (r.ok) {\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''}\\n`)\n break\n }\n out.write(`✗ ${r.reason}\\n`)\n if (attempt >= 1) throw new Error('authentication failed')\n token = undefined // re-prompt on retry\n }\n\n persistCredentials({ ...opts, cpUrl: url!, cpKey: token! })\n\n const ans = (await nextLine(iter, 'Install AgentConnect as a background service? (y/N) ', out)).toLowerCase()\n if (ans === 'y' || ans === 'yes') {\n await deps.installService()\n out.write('Service installed and started. Manage it with `agentconnect up` / `down` / `status`.\\n')\n return\n }\n out.write('Starting in the foreground (Ctrl-C to stop)…\\n')\n } finally {\n rl.close()\n }\n\n await deps.runForeground()\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login-B4KagoWu.js","names":["realRunForeground"],"sources":["../src/cp/auth-probe.ts","../src/cli/login.ts"],"sourcesContent":["/**\n * `probeAuth` — a one-shot CP auth check used by `agentconnect login`. It dials\n * the CP, sends ONLY the `auth` frame (no `register`), and resolves with the\n * authoritative daemonId from `auth/ok`, or a human-readable failure reason on\n * a `4401` close, a correlated `error`, a dial failure, or a timeout. It always\n * closes the socket and never leaves a connection open.\n */\nimport { buildEnvelope, decodeEnvelope, encode, isFrame } from '@agentconnect.md/protocol'\nimport { ClientTransport, type Transport } from '@agentconnect.md/connection'\nimport { CP_SUBPROTOCOL, CP_WS_PATH } from './client.js'\n\nexport interface ProbeResult {\n ok: boolean\n daemonId?: string\n reason?: string\n}\n\nexport interface ProbeOpts {\n url: string\n token: string\n daemonId?: string\n agentVersion?: string\n timeoutMs?: number\n /** Injectable for tests; defaults to dialing the real WebSocket. */\n connect?: () => Promise<Transport>\n}\n\nexport async function probeAuth(opts: ProbeOpts): Promise<ProbeResult> {\n const timeoutMs = opts.timeoutMs ?? 10_000\n const dial = opts.connect ?? (() => ClientTransport.dial(opts.url, { subprotocol: CP_SUBPROTOCOL, path: CP_WS_PATH }))\n\n let transport: Transport\n try {\n transport = await dial()\n } catch (err) {\n return { ok: false, reason: `cannot reach ${opts.url}: ${(err as Error).message}` }\n }\n\n const authPayload: Record<string, unknown> = {\n apiKey: opts.token,\n agentVersion: opts.agentVersion ?? 'probe'\n }\n if (opts.daemonId) authPayload.daemonId = opts.daemonId\n const frame = buildEnvelope('auth', authPayload)\n\n return await new Promise<ProbeResult>((resolve) => {\n let settled = false\n const done = (r: ProbeResult): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n try {\n transport.close(1000, 'probe done')\n } catch {\n // socket may already be closed\n }\n resolve(r)\n }\n\n const timer = setTimeout(\n () => done({ ok: false, reason: `timed out after ${timeoutMs}ms waiting for auth/ok` }),\n timeoutMs\n )\n\n transport.onMessage((text) => {\n const decoded = decodeEnvelope(text)\n if (!decoded.ok) return\n const f = decoded.frame\n if (f.corr !== frame.id) return\n if (f.type === 'error') {\n const e = f.payload as { code: string; message: string }\n done({ ok: false, reason: `${e.code}: ${e.message}` })\n return\n }\n if (isFrame('auth/ok')(f)) {\n const ok = f.payload as { daemonId: string }\n done({ ok: true, daemonId: ok.daemonId })\n }\n })\n\n transport.onClose((code) => {\n if (code === 4401) done({ ok: false, reason: 'authentication failed (4401) — check the daemon token' })\n else done({ ok: false, reason: `connection closed (${code}) before auth/ok` })\n })\n\n transport.send(encode(frame))\n })\n}\n","/**\n * `agentconnect login` — interactive onboarding. It tests the Control Plane auth\n * flow, persists credentials only on success, then offers to install the OS\n * service. If installed it starts the service (`up`) and returns; otherwise it\n * runs the daemon in the foreground (the same path as `agentconnect run`).\n *\n * `persistCredentials` is the pure config-merge half (token-only onboarding:\n * `daemonId` is omitted unless given — the first connect adopts it from the\n * token's `sub`). `runLogin` is the orchestrator, with injectable deps for tests.\n */\nimport { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { ConfigSchema } from '../config/config-schema.js'\nimport { resolveRoot, configPath } from '../paths.js'\nimport { DAEMON_VERSION } from '../version.js'\nimport { probeAuth, type ProbeResult } from '../cp/auth-probe.js'\nimport { resolveController, type InstallOpts } from '../service/index.js'\nimport { runForeground as realRunForeground } from './run-foreground.js'\n\nexport interface PersistCredsOpts {\n cpUrl: string\n cpKey: string\n daemonId?: string\n root?: string\n configPath?: string\n}\n\n/** Merge url/token into config.json (validated), returning the written path. */\nexport function persistCredentials(opts: PersistCredsOpts): string {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n const raw: Record<string, unknown> = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n\n const cp = (raw.controlPlane as Record<string, unknown> | undefined) ?? {}\n raw.controlPlane = { ...cp, url: opts.cpUrl, key: opts.cpKey, enabled: true }\n if (opts.daemonId) raw.daemonId = opts.daemonId\n\n ConfigSchema.parse(raw) // fail before writing if the merge is invalid\n\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n return file\n}\n\nexport interface RunLoginOpts {\n cpUrl?: string\n cpKey?: string\n daemonId?: string\n root?: string\n configPath?: string\n /** The resolved CLI entry path. Must be supplied by the CLI entry point\n * (index.ts) so the correct bundle path is used rather than inferring it\n * from login.ts's own import.meta.url (which is the same bundle but the\n * `../` path calculation would be wrong). Falls back to import.meta.url\n * for the pure test path where login.ts is loaded standalone. */\n cliEntry?: string\n}\n\n/**\n * Build the InstallOpts for the OS service installer.\n *\n * Exported as a pure helper so it can be unit-tested independently of the\n * full `runLogin` orchestration. The caller (index.ts) passes `cliEntry` so\n * the correct bundle path always flows through; if omitted we fall back to\n * `import.meta.url` (correct only when login.ts is the entry, e.g. in tests).\n */\nexport function buildInstallOpts(opts: { root?: string; cliEntry?: string }): InstallOpts {\n return {\n execPath: process.execPath,\n cliEntry: opts.cliEntry ?? fileURLToPath(import.meta.url),\n includeRootEnv: resolveRoot(opts.root) !== resolveRoot(undefined)\n }\n}\n\nexport interface LoginDeps {\n probe: (o: { url: string; token: string }) => Promise<ProbeResult>\n installService: () => Promise<void>\n runForeground: () => Promise<void>\n out: NodeJS.WritableStream\n input: NodeJS.ReadableStream\n isTTY: boolean\n}\n\nfunction realDeps(opts: RunLoginOpts): LoginDeps {\n return {\n probe: (o) => probeAuth({ url: o.url, token: o.token, agentVersion: DAEMON_VERSION }),\n installService: async () => {\n const controller = resolveController({ root: opts.root })\n await controller.install(buildInstallOpts(opts))\n await controller.up()\n },\n runForeground: () =>\n realRunForeground({\n root: opts.root,\n overrides: { cpUrl: opts.cpUrl, cpKey: opts.cpKey, daemonId: opts.daemonId }\n }),\n out: process.stdout,\n input: process.stdin,\n isTTY: Boolean(process.stdin.isTTY)\n }\n}\n\n/** Read the next line from a readline interface via async iterator. */\nasync function nextLine(\n iter: AsyncIterableIterator<string>,\n prompt: string,\n out: NodeJS.WritableStream\n): Promise<string> {\n out.write(prompt)\n const { value, done } = await iter.next()\n if (done) throw new Error(`unexpected end of input waiting for: ${prompt}`)\n return (value as string).trim()\n}\n\nexport async function runLogin(opts: RunLoginOpts, partial: Partial<LoginDeps> = {}): Promise<void> {\n const deps: LoginDeps = { ...realDeps(opts), ...partial }\n const { out } = deps\n\n // ── Non-interactive: flags required; probe; persist. No prompts, no handoff. ──\n if (!deps.isTTY) {\n if (!opts.cpUrl) throw new Error('login requires --cp-url <url>')\n if (!opts.cpKey) throw new Error('login requires --cp-key <key>')\n const r = await deps.probe({ url: opts.cpUrl, token: opts.cpKey })\n if (!r.ok) throw new Error(`authentication failed: ${r.reason}`)\n persistCredentials({ ...opts, cpUrl: opts.cpUrl, cpKey: opts.cpKey })\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''} — credentials saved\\n`)\n return\n }\n\n // Both interactive handoffs read `<root>/config.json`: the installed service\n // runs `… run` (no `--config`), and the foreground `Daemon` loads by root. A\n // custom `--config` would persist credentials the daemon never reads, so the\n // install/run branch would look successful but start without them. Reject it;\n // `--root` is the knob that keeps persist + service + foreground in sync.\n if (opts.configPath) {\n throw new Error(\n 'interactive login does not support --config — use --root <dir> so the daemon and the installed service read the same config'\n )\n }\n\n // ── Interactive ──\n const rl = createInterface({ input: deps.input, output: out })\n const iter = rl[Symbol.asyncIterator]()\n let url = opts.cpUrl\n let token = opts.cpKey\n try {\n if (!url) url = await nextLine(iter, 'Control Plane URL: ', out)\n\n for (let attempt = 0; ; attempt++) {\n if (!token) token = await nextLine(iter, 'Daemon API key: ', out)\n out.write(`Testing connection to ${url}…\\n`)\n const r = await deps.probe({ url: url!, token: token! })\n if (r.ok) {\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''}\\n`)\n break\n }\n out.write(`✗ ${r.reason}\\n`)\n if (attempt >= 1) throw new Error('authentication failed')\n token = undefined // re-prompt on retry\n }\n\n persistCredentials({ ...opts, cpUrl: url!, cpKey: token! })\n\n const ans = (await nextLine(iter, 'Install AgentConnect as a background service? (y/N) ', out)).toLowerCase()\n if (ans === 'y' || ans === 'yes') {\n await deps.installService()\n out.write('Service installed and started. Manage it with `agentconnect up` / `down` / `status`.\\n')\n return\n }\n out.write('Starting in the foreground (Ctrl-C to stop)…\\n')\n } finally {\n rl.close()\n }\n\n await deps.runForeground()\n}\n"],"mappings":"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"}
|
|
@@ -3,10 +3,10 @@ import { n as require_ms, t as require_src$3 } from "./src-DDtlVutj.js";
|
|
|
3
3
|
import { n as init_esm_shims, t as __dirname } from "./esm-shims-DxGcm4Hw.js";
|
|
4
4
|
import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
|
|
5
5
|
import { C as _coercedNumber, T as NEVER, a as array, d as literal, f as looseObject, i as any, m as object, o as boolean, p as number$1, t as ZodNumber, v as string, x as url$4 } from "./schemas-CQkO9hC5.js";
|
|
6
|
-
import { C as ZodIssueCode, S as RESERVED_MCP_SERVER_NAME, _ as buildEnvelope, a as agentChildEnv, b as MAX_FRAME_BYTES, c as persistRelays, d as normalizeGitUrl, f as RELAY_DAEMON_SUBPROTOCOL, g as SHARED_CONFIG_ACTION_ID, h as decodeRelayDaemonFrame, i as selectAgent, m as buildRelayDaemonFrame, n as findAgentFiles, o as loadConfig, p as RELAY_DAEMON_WS_PATH, r as loadAgents, s as persistDaemonId, v as decodeEnvelope, y as encode } from "./load-agents-
|
|
6
|
+
import { C as ZodIssueCode, S as RESERVED_MCP_SERVER_NAME, _ as buildEnvelope, a as agentChildEnv, b as MAX_FRAME_BYTES, c as persistRelays, d as normalizeGitUrl, f as RELAY_DAEMON_SUBPROTOCOL, g as SHARED_CONFIG_ACTION_ID, h as decodeRelayDaemonFrame, i as selectAgent, m as buildRelayDaemonFrame, n as findAgentFiles, o as loadConfig, p as RELAY_DAEMON_WS_PATH, r as loadAgents, s as persistDaemonId, v as decodeEnvelope, y as encode } from "./load-agents-BLNSM987.js";
|
|
7
7
|
import { l as resolveRoot, o as mcpSocketPath, u as statePath } from "./paths-DEcUO8lC.js";
|
|
8
|
-
import { S as resolveRuntimes, _ as writeMemoryFile, a as initGitInjection, b as cachedRuntimeNames, c as simpleGit, d as memoryProviderFor, f as MemoryConflictError, g as resolveInMemoryDir, h as listMemory, i as prepareWorkspace, l as createMemoryProvider, m as MemoryTooLargeError, n as installedRuntimes, o as probeGitVersion, p as MemoryPathError, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as memoryKindOf, v as MEMORY_TOOL_NAMES, x as cachedRuntimeVersions, y as toolsForIntegrations } from "./acp-host-
|
|
9
|
-
import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-
|
|
8
|
+
import { S as resolveRuntimes, _ as writeMemoryFile, a as initGitInjection, b as cachedRuntimeNames, c as simpleGit, d as memoryProviderFor, f as MemoryConflictError, g as resolveInMemoryDir, h as listMemory, i as prepareWorkspace, l as createMemoryProvider, m as MemoryTooLargeError, n as installedRuntimes, o as probeGitVersion, p as MemoryPathError, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as memoryKindOf, v as MEMORY_TOOL_NAMES, x as cachedRuntimeVersions, y as toolsForIntegrations } from "./acp-host-BlkeNrQB.js";
|
|
9
|
+
import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-Dhp-tyEI.js";
|
|
10
10
|
import { B as ToolListChangedNotificationSchema, D as ListPromptsResultSchema, E as ListChangedOptionsBaseSchema, F as PromptListChangedNotificationSchema, G as isZ4Schema, H as isJSONRPCRequest, I as ReadResourceResultSchema, J as encodeFrame, K as safeParse, L as ResourceListChangedNotificationSchema, M as ListToolsResultSchema, O as ListResourceTemplatesResultSchema, P as McpError, R as SUPPORTED_PROTOCOL_VERSIONS, S as InitializeResultSchema, T as LATEST_PROTOCOL_VERSION, U as isJSONRPCResultResponse, V as isInitializedNotification, W as getObjectShape, _ as ElicitResultSchema, a as AjvJsonSchemaValidator, b as GetPromptResultSchema, c as mergeCapabilities, d as CompleteResultSchema, f as CreateMessageRequestSchema, g as ElicitRequestSchema, h as CreateTaskResultSchema, i as assertToolsCallTaskCapability, k as ListResourcesResultSchema, m as CreateMessageResultWithToolsSchema, n as serializeMessage, o as require_fast_deep_equal, p as CreateMessageResultSchema, q as decodeFrames, r as assertClientRequestTaskCapability, s as Protocol, t as ReadBuffer, u as CallToolResultSchema, v as EmptyResultSchema, w as JSONRPCMessageSchema, y as ErrorCode } from "./stdio-DnS8WcXi.js";
|
|
11
11
|
import { URL as URL$1 } from "url";
|
|
12
12
|
import * as tls from "tls";
|
|
@@ -197192,6 +197192,14 @@ function githubSubjectLine(c) {
|
|
|
197192
197192
|
function neutralizeDelimiters(body) {
|
|
197193
197193
|
return body.split("\n").map((line) => line.trimStart().startsWith("----- ") ? `\\${line}` : line).join("\n");
|
|
197194
197194
|
}
|
|
197195
|
+
/** Trailing instruction for github fires on a NUMBERED thread (issue/PR): the
|
|
197196
|
+
* daemon streams the agent's reply back as a GitHub comment automatically, so
|
|
197197
|
+
* the agent must NOT post its own — a manual `gh issue comment` would
|
|
197198
|
+
* double-post. Only emitted when the poster will actually run (a thread number
|
|
197199
|
+
* exists); push fires have no thread, so they never see this. */
|
|
197200
|
+
function githubReplyHint(c) {
|
|
197201
|
+
return ["", `Your reply is posted back to ${`${c.repo ?? "this thread"}#${c.number}`} automatically as a GitHub comment (it streams as you write, so keep it a single self-contained reply). Do NOT run \`gh issue comment\` / \`gh pr comment\` yourself — that would double-post. You may still use \`gh\` for READ-only inspection (viewing the thread, diffs, files).`].join("\n");
|
|
197202
|
+
}
|
|
197195
197203
|
/** The github-kind turn text: a trusted metadata header + the FENCED excerpt.
|
|
197196
197204
|
* The title rides the header (relay-sanitized to one capped line) — it is
|
|
197197
197205
|
* still attacker-authored, so keep it quoted and short, never instructional
|
|
@@ -197202,7 +197210,8 @@ function buildGithubHookText(c) {
|
|
|
197202
197210
|
`From: ${c.senderLogin ?? "unknown"}${c.authorAssociation ? ` (${c.authorAssociation})` : ""}${c.labels?.length ? ` · labels: ${c.labels.join(", ")}` : ""}`,
|
|
197203
197211
|
...c.htmlUrl ? [c.htmlUrl] : []
|
|
197204
197212
|
].join("\n");
|
|
197205
|
-
|
|
197213
|
+
const tail = c.number !== void 0 ? githubReplyHint(c) : "";
|
|
197214
|
+
if (!c.bodyExcerpt) return head + tail;
|
|
197206
197215
|
return [
|
|
197207
197216
|
head,
|
|
197208
197217
|
"",
|
|
@@ -197210,7 +197219,7 @@ function buildGithubHookText(c) {
|
|
|
197210
197219
|
neutralizeDelimiters(c.bodyExcerpt),
|
|
197211
197220
|
UNTRUSTED_CONTENT_END,
|
|
197212
197221
|
...c.truncated ? ["(body truncated — pull the full thread yourself, e.g. `gh issue view <number> --comments`)"] : []
|
|
197213
|
-
].join("\n");
|
|
197222
|
+
].join("\n") + tail;
|
|
197214
197223
|
}
|
|
197215
197224
|
/** The turn text: the caller's payload-borne message (+ leftover fields as context). */
|
|
197216
197225
|
function buildHookText(msg) {
|
|
@@ -197282,6 +197291,118 @@ function buildHookMessage(msg, traceId) {
|
|
|
197282
197291
|
};
|
|
197283
197292
|
}
|
|
197284
197293
|
//#endregion
|
|
197294
|
+
//#region src/github/poster.ts
|
|
197295
|
+
const DEFAULT_EDIT_INTERVAL_MS = 2e3;
|
|
197296
|
+
/** GitHub caps comment bodies at 65536 chars — truncate with a marker well below. */
|
|
197297
|
+
const MAX_COMMENT_CHARS = 6e4;
|
|
197298
|
+
/** Consecutive API failures before the stream gives up for this turn. */
|
|
197299
|
+
const MAX_FAILURES = 3;
|
|
197300
|
+
var GithubCommentStream = class {
|
|
197301
|
+
deps;
|
|
197302
|
+
repo;
|
|
197303
|
+
issueNumber;
|
|
197304
|
+
buffer = "";
|
|
197305
|
+
posted = "";
|
|
197306
|
+
commentId = null;
|
|
197307
|
+
inFlight = Promise.resolve();
|
|
197308
|
+
busy = false;
|
|
197309
|
+
timer = null;
|
|
197310
|
+
hasSent = false;
|
|
197311
|
+
lastSendAt = 0;
|
|
197312
|
+
failures = 0;
|
|
197313
|
+
finished = false;
|
|
197314
|
+
sched;
|
|
197315
|
+
editIntervalMs;
|
|
197316
|
+
constructor(deps, repo, issueNumber) {
|
|
197317
|
+
this.deps = deps;
|
|
197318
|
+
this.repo = repo;
|
|
197319
|
+
this.issueNumber = issueNumber;
|
|
197320
|
+
this.sched = deps.scheduler ?? {
|
|
197321
|
+
now: () => Date.now(),
|
|
197322
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
197323
|
+
clearTimeout: (h) => clearTimeout(h)
|
|
197324
|
+
};
|
|
197325
|
+
this.editIntervalMs = deps.minEditIntervalMs ?? DEFAULT_EDIT_INTERVAL_MS;
|
|
197326
|
+
}
|
|
197327
|
+
/** Fold one agent-output chunk in; schedules the create/edit as needed. Never throws. */
|
|
197328
|
+
append(text) {
|
|
197329
|
+
if (this.finished || this.failures >= MAX_FAILURES || !text) return;
|
|
197330
|
+
this.buffer += text;
|
|
197331
|
+
this.schedule();
|
|
197332
|
+
}
|
|
197333
|
+
/**
|
|
197334
|
+
* Turn over: flush whatever is left as the final body. Resolves once the
|
|
197335
|
+
* last write settled (or was abandoned); never rejects. Idempotent.
|
|
197336
|
+
*/
|
|
197337
|
+
async finish() {
|
|
197338
|
+
if (this.finished) return;
|
|
197339
|
+
this.finished = true;
|
|
197340
|
+
if (this.timer !== null) {
|
|
197341
|
+
this.sched.clearTimeout(this.timer);
|
|
197342
|
+
this.timer = null;
|
|
197343
|
+
}
|
|
197344
|
+
await this.inFlight;
|
|
197345
|
+
if (this.buffer !== this.posted && this.buffer.trim() && this.failures < MAX_FAILURES) await this.send();
|
|
197346
|
+
await this.inFlight;
|
|
197347
|
+
}
|
|
197348
|
+
schedule() {
|
|
197349
|
+
if (this.busy || this.timer !== null) return;
|
|
197350
|
+
if (!this.hasSent) {
|
|
197351
|
+
this.inFlight = this.send();
|
|
197352
|
+
return;
|
|
197353
|
+
}
|
|
197354
|
+
const wait = Math.max(0, this.lastSendAt + this.editIntervalMs - this.sched.now());
|
|
197355
|
+
this.timer = this.sched.setTimeout(() => {
|
|
197356
|
+
this.timer = null;
|
|
197357
|
+
if (this.finished) return;
|
|
197358
|
+
this.inFlight = this.send();
|
|
197359
|
+
}, wait);
|
|
197360
|
+
}
|
|
197361
|
+
/** One create-or-edit round; merges everything buffered at send time. */
|
|
197362
|
+
async send() {
|
|
197363
|
+
if (this.busy) return;
|
|
197364
|
+
this.busy = true;
|
|
197365
|
+
const snapshot = this.buffer;
|
|
197366
|
+
const body = this.render(snapshot);
|
|
197367
|
+
try {
|
|
197368
|
+
const token = await this.deps.token();
|
|
197369
|
+
if (this.commentId === null) {
|
|
197370
|
+
const created = await this.request("POST", `/repos/${this.repo}/issues/${this.issueNumber}/comments`, token, body);
|
|
197371
|
+
this.commentId = created.id;
|
|
197372
|
+
this.deps.log.debug(`github poster: comment ${created.id} opened on ${this.repo}#${this.issueNumber}`);
|
|
197373
|
+
} else await this.request("PATCH", `/repos/${this.repo}/issues/comments/${this.commentId}`, token, body);
|
|
197374
|
+
this.posted = snapshot;
|
|
197375
|
+
this.failures = 0;
|
|
197376
|
+
} catch (err) {
|
|
197377
|
+
this.failures += 1;
|
|
197378
|
+
this.deps.log.warn(`github poster: ${this.commentId === null ? "create" : "edit"} failed on ${this.repo}#${this.issueNumber} (${err.message?.slice(0, 200)})${this.failures >= MAX_FAILURES ? " — giving up for this turn" : ""}`);
|
|
197379
|
+
} finally {
|
|
197380
|
+
this.hasSent = true;
|
|
197381
|
+
this.lastSendAt = this.sched.now();
|
|
197382
|
+
this.busy = false;
|
|
197383
|
+
if (!this.finished && this.buffer !== this.posted && this.failures < MAX_FAILURES) this.schedule();
|
|
197384
|
+
}
|
|
197385
|
+
}
|
|
197386
|
+
render(text) {
|
|
197387
|
+
if (text.length <= MAX_COMMENT_CHARS) return text;
|
|
197388
|
+
return `${text.slice(0, MAX_COMMENT_CHARS)}\n\n_(truncated — see the session transcript for the full reply)_`;
|
|
197389
|
+
}
|
|
197390
|
+
async request(method, path, token, body) {
|
|
197391
|
+
const res = await (this.deps.fetchImpl ?? fetch)(`${this.deps.baseUrl ?? "https://api.github.com"}${path}`, {
|
|
197392
|
+
method,
|
|
197393
|
+
headers: {
|
|
197394
|
+
authorization: `Bearer ${token}`,
|
|
197395
|
+
accept: "application/vnd.github+json",
|
|
197396
|
+
"content-type": "application/json",
|
|
197397
|
+
"x-github-api-version": "2022-11-28"
|
|
197398
|
+
},
|
|
197399
|
+
body: JSON.stringify({ body })
|
|
197400
|
+
});
|
|
197401
|
+
if (!res.ok) throw new Error(`GitHub ${method} ${res.status}`);
|
|
197402
|
+
return await res.json();
|
|
197403
|
+
}
|
|
197404
|
+
};
|
|
197405
|
+
//#endregion
|
|
197285
197406
|
//#region src/runtimes/runtime-prober.ts
|
|
197286
197407
|
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
197287
197408
|
const DEFAULT_CONCURRENCY = 3;
|
|
@@ -215846,8 +215967,13 @@ var Daemon = class Daemon {
|
|
|
215846
215967
|
});
|
|
215847
215968
|
};
|
|
215848
215969
|
const nmsg = buildHookMessage(msg, randomUUID());
|
|
215970
|
+
const c = msg.context;
|
|
215971
|
+
const githubReply = c?.source === "github" && c.repo && c.number !== void 0 ? {
|
|
215972
|
+
repo: c.repo,
|
|
215973
|
+
number: c.number
|
|
215974
|
+
} : void 0;
|
|
215849
215975
|
try {
|
|
215850
|
-
const sessionId = await this.fireTrigger(msg.agentId, nmsg, msg.target, hookAnchorText(msg), `hook "${msg.hookId}"
|
|
215976
|
+
const sessionId = await this.fireTrigger(msg.agentId, nmsg, msg.target, hookAnchorText(msg), `hook "${msg.hookId}"`, githubReply);
|
|
215851
215977
|
if (sessionId !== null) report("success", { sessionId });
|
|
215852
215978
|
else report("failed", { reason: "dropped" });
|
|
215853
215979
|
} catch (err) {
|
|
@@ -216407,7 +216533,7 @@ var Daemon = class Daemon {
|
|
|
216407
216533
|
...error ? { error } : {}
|
|
216408
216534
|
});
|
|
216409
216535
|
}
|
|
216410
|
-
dispatch(agentId, msg, integrationId, webchat, callMeta, opts) {
|
|
216536
|
+
dispatch(agentId, msg, integrationId, webchat, callMeta, opts, githubReply) {
|
|
216411
216537
|
return new Promise((resolve, reject) => {
|
|
216412
216538
|
if (this.draining || this.drainingAgents.has(agentId)) {
|
|
216413
216539
|
this.log.debug(`dispatch: skipped for agent "${agentId}" (draining)`);
|
|
@@ -216427,6 +216553,7 @@ var Daemon = class Daemon {
|
|
|
216427
216553
|
...webchat ? { webchat } : {},
|
|
216428
216554
|
...callMeta ? { callMeta } : {},
|
|
216429
216555
|
...opts?.isQueueCmd ? { isQueueCmd: true } : {},
|
|
216556
|
+
...githubReply ? { githubReply } : {},
|
|
216430
216557
|
resolve,
|
|
216431
216558
|
reject
|
|
216432
216559
|
};
|
|
@@ -216502,7 +216629,7 @@ var Daemon = class Daemon {
|
|
|
216502
216629
|
this.inflight.delete(key);
|
|
216503
216630
|
}
|
|
216504
216631
|
async dispatchOne(entry, key) {
|
|
216505
|
-
const { agentId, msg, integrationId, webchat, callMeta } = entry;
|
|
216632
|
+
const { agentId, msg, integrationId, webchat, callMeta, githubReply } = entry;
|
|
216506
216633
|
if (callMeta?.correlationId !== void 0) this.recordWorkerReport(key, callMeta, msg.text);
|
|
216507
216634
|
const agent = this.agents.get(agentId);
|
|
216508
216635
|
const rec = new TranscriptRecorder();
|
|
@@ -216557,7 +216684,8 @@ var Daemon = class Daemon {
|
|
|
216557
216684
|
...webchat,
|
|
216558
216685
|
index: 0,
|
|
216559
216686
|
replyText: ""
|
|
216560
|
-
} } : {}
|
|
216687
|
+
} } : {},
|
|
216688
|
+
...githubReply ? { github: { stream: this.makeGithubReplyStream(agentId, githubReply) } } : {}
|
|
216561
216689
|
};
|
|
216562
216690
|
this.pending.set(sessionId, p);
|
|
216563
216691
|
if (callMeta) this.activeTurnCallMeta.set(key, callMeta);
|
|
@@ -216647,6 +216775,7 @@ var Daemon = class Daemon {
|
|
|
216647
216775
|
thread: statusThread
|
|
216648
216776
|
});
|
|
216649
216777
|
this.clearCancelBackstop(sessionId);
|
|
216778
|
+
if (p.github) p.github.stream.finish().catch((err) => this.log.warn(`github poster: final flush failed (${formatErr(err)})`));
|
|
216650
216779
|
p.resolveDone();
|
|
216651
216780
|
}
|
|
216652
216781
|
return sessionId;
|
|
@@ -217288,6 +217417,10 @@ var Daemon = class Daemon {
|
|
|
217288
217417
|
for (const action of p.conv.onUpdate(update)) this.enqueueApply(p, action);
|
|
217289
217418
|
this.armIdle(p);
|
|
217290
217419
|
}
|
|
217420
|
+
if (p.github && update?.sessionUpdate === "agent_message_chunk" && update.content?.type === "text") {
|
|
217421
|
+
const text = update.content.text ?? "";
|
|
217422
|
+
if (text) p.github.stream.append(text);
|
|
217423
|
+
}
|
|
217291
217424
|
for (const ev of p.rec.onUpdate(update)) this.recordEvent(p.agentId, p.channel, p.statusThread, ev);
|
|
217292
217425
|
}
|
|
217293
217426
|
/**
|
|
@@ -217764,7 +217897,7 @@ var Daemon = class Daemon {
|
|
|
217764
217897
|
* still runs; a `headless` msg additionally suppresses all platform output
|
|
217765
217898
|
* in dispatch. Resolves with dispatch's sessionId (null = gate-dropped).
|
|
217766
217899
|
*/
|
|
217767
|
-
async fireTrigger(agentId, msg, target, anchorText, label) {
|
|
217900
|
+
async fireTrigger(agentId, msg, target, anchorText, label, githubReply) {
|
|
217768
217901
|
if (target?.channel) {
|
|
217769
217902
|
const conn = this.replyConnFor(agentId, target.integrationId);
|
|
217770
217903
|
if (!conn) this.log.warn(`${label}: agent "${agentId}" has no live platform connection — running without output`);
|
|
@@ -217778,7 +217911,18 @@ var Daemon = class Daemon {
|
|
|
217778
217911
|
this.log.warn(`${label}: failed to post trigger to ${target.channel} (${formatErr(err)}) — running without anchor`);
|
|
217779
217912
|
}
|
|
217780
217913
|
}
|
|
217781
|
-
return this.dispatch(agentId, msg, target?.integrationId);
|
|
217914
|
+
return this.dispatch(agentId, msg, target?.integrationId, void 0, void 0, void 0, githubReply);
|
|
217915
|
+
}
|
|
217916
|
+
/** Build the per-turn GitHub comment stream, tokened via the repo-targeted
|
|
217917
|
+
* gitcred mint (issues/PR write, no contents — never enters agent env). */
|
|
217918
|
+
makeGithubReplyStream(agentId, ref) {
|
|
217919
|
+
return new GithubCommentStream({
|
|
217920
|
+
token: async () => (await this.gitCreds.getPostToken(agentId, ref.repo)).token,
|
|
217921
|
+
log: {
|
|
217922
|
+
warn: (m) => this.log.warn(m),
|
|
217923
|
+
debug: (m) => this.log.debug(m)
|
|
217924
|
+
}
|
|
217925
|
+
}, ref.repo, ref.number);
|
|
217782
217926
|
}
|
|
217783
217927
|
/**
|
|
217784
217928
|
* A cron fired for `agentId` (§8.6 bypass). The fire is stamped into D11
|
|
@@ -218064,4 +218208,4 @@ async function runForeground(opts, deps = {}) {
|
|
|
218064
218208
|
//#endregion
|
|
218065
218209
|
export { ClientTransport as i, CP_SUBPROTOCOL as n, CP_WS_PATH as r, runForeground as t };
|
|
218066
218210
|
|
|
218067
|
-
//# sourceMappingURL=run-foreground-
|
|
218211
|
+
//# sourceMappingURL=run-foreground-D4TMcwTn.js.map
|