@agentconnect.md/daemon 1.0.0-rc.298 → 1.0.0-rc.299
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/index.js +2 -2
- package/dist/{login-ycknK_Ag.js → login-BP4bFP-d.js} +2 -2
- package/dist/{login-ycknK_Ag.js.map → login-BP4bFP-d.js.map} +1 -1
- package/dist/{run-foreground-qa22G1Nt.js → run-foreground-B8sw7WKS.js} +65 -131
- package/dist/{run-foreground-qa22G1Nt.js.map → run-foreground-B8sw7WKS.js.map} +1 -1
- package/dist/run-foreground-jOExu3YR.js +2 -0
- package/package.json +1 -1
- package/dist/run-foreground-B5Uw1HWo.js +0 -2
package/dist/index.js
CHANGED
|
@@ -68493,8 +68493,8 @@ const [{ Command }, { fileURLToPath }, { runChat }, { runLogin }, { runForegroun
|
|
|
68493
68493
|
import("./commander-B5QVEFzH.js"),
|
|
68494
68494
|
import("node:url"),
|
|
68495
68495
|
import("./chat-D8QIIHor.js"),
|
|
68496
|
-
import("./login-
|
|
68497
|
-
import("./run-foreground-
|
|
68496
|
+
import("./login-BP4bFP-d.js"),
|
|
68497
|
+
import("./run-foreground-jOExu3YR.js"),
|
|
68498
68498
|
import("./service-6w0WKhdx.js"),
|
|
68499
68499
|
import("./lock-CA9Bm8_s.js"),
|
|
68500
68500
|
import("./paths-CcRUjFjy.js")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
|
|
2
2
|
import { d as encode, l as buildEnvelope, t as isFrame, u as decodeEnvelope } from "./dist-Cq1Kimg_.js";
|
|
3
|
-
import { i as ClientTransport, t as runForeground } from "./run-foreground-
|
|
3
|
+
import { i as ClientTransport, t as runForeground } from "./run-foreground-B8sw7WKS.js";
|
|
4
4
|
import { l as ConfigSchema } from "./load-agents-gTqVkOwl.js";
|
|
5
5
|
import { l as resolveRoot, t as configPath } from "./paths-DEcUO8lC.js";
|
|
6
6
|
import { n as resolveController } from "./service-BQVw7wbz.js";
|
|
@@ -229,4 +229,4 @@ async function runLogin(opts, partial = {}) {
|
|
|
229
229
|
//#endregion
|
|
230
230
|
export { buildInstallOpts, persistCredentials, runLogin };
|
|
231
231
|
|
|
232
|
-
//# sourceMappingURL=login-
|
|
232
|
+
//# sourceMappingURL=login-BP4bFP-d.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-ycknK_Ag.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-BP4bFP-d.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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"}
|
|
@@ -197445,12 +197445,9 @@ function buildHookMessage(msg, traceId) {
|
|
|
197445
197445
|
}
|
|
197446
197446
|
//#endregion
|
|
197447
197447
|
//#region src/github/poster.ts
|
|
197448
|
-
const DEFAULT_EDIT_INTERVAL_MS = 2e3;
|
|
197449
197448
|
const DEFAULT_FINALIZE_TIMEOUT_MS = 15e3;
|
|
197450
197449
|
/** GitHub caps comment bodies at 65536 chars — truncate with a marker well below. */
|
|
197451
197450
|
const MAX_COMMENT_CHARS = 6e4;
|
|
197452
|
-
/** Consecutive API failures before the stream gives up for this turn. */
|
|
197453
|
-
const MAX_FAILURES = 3;
|
|
197454
197451
|
const TRUNCATION_MARKER = "\n\n_(truncated — see the session transcript for the full reply)_";
|
|
197455
197452
|
const LEGACY_BOUNDARIES = /* @__PURE__ */ new Set([
|
|
197456
197453
|
"agent_thought_chunk",
|
|
@@ -197477,17 +197474,17 @@ var GithubReplyCollector = class {
|
|
|
197477
197474
|
lastCommentarySeen = -1;
|
|
197478
197475
|
legacyRuns = [];
|
|
197479
197476
|
legacyCurrent;
|
|
197480
|
-
/** Consume one raw ACP update
|
|
197477
|
+
/** Consume one raw ACP update; the completed answer is selected by finalText(). */
|
|
197481
197478
|
onUpdate(update) {
|
|
197482
197479
|
const u = record(update);
|
|
197483
|
-
if (!u) return
|
|
197480
|
+
if (!u) return;
|
|
197484
197481
|
const kind = typeof u.sessionUpdate === "string" ? u.sessionUpdate : void 0;
|
|
197485
197482
|
if (kind !== "agent_message_chunk") {
|
|
197486
197483
|
if (kind && LEGACY_BOUNDARIES.has(kind)) this.commitLegacyBoundary();
|
|
197487
197484
|
return;
|
|
197488
197485
|
}
|
|
197489
197486
|
const content = record(u.content);
|
|
197490
|
-
if (content?.type !== "text" || typeof content.text !== "string") return
|
|
197487
|
+
if (content?.type !== "text" || typeof content.text !== "string") return;
|
|
197491
197488
|
const text = content.text;
|
|
197492
197489
|
const seen = this.nextOrder++;
|
|
197493
197490
|
const codex = record(record(u._meta)?.codex);
|
|
@@ -197531,7 +197528,6 @@ var GithubReplyCollector = class {
|
|
|
197531
197528
|
this.legacyCurrent.text += text;
|
|
197532
197529
|
this.legacyCurrent.lastSeen = seen;
|
|
197533
197530
|
}
|
|
197534
|
-
return this.explicitFinalText();
|
|
197535
197531
|
}
|
|
197536
197532
|
finalText(completed = true) {
|
|
197537
197533
|
const explicit = this.explicitFinalText();
|
|
@@ -197634,27 +197630,14 @@ function truncatedMarkdownPrefix(text, budget) {
|
|
|
197634
197630
|
contentBudget = nextBudget;
|
|
197635
197631
|
}
|
|
197636
197632
|
}
|
|
197637
|
-
var
|
|
197633
|
+
var GithubFinalPoster = class {
|
|
197638
197634
|
deps;
|
|
197639
197635
|
repo;
|
|
197640
197636
|
issueNumber;
|
|
197641
|
-
buffer = "";
|
|
197642
|
-
/** Rendered body actually on GitHub. Comparing rendered output avoids no-op
|
|
197643
|
-
* PATCHes once an overlong reply has already filled the truncation budget. */
|
|
197644
|
-
postedBody = null;
|
|
197645
|
-
commentId = null;
|
|
197646
|
-
inFlight = Promise.resolve();
|
|
197647
|
-
busy = false;
|
|
197648
|
-
timer = null;
|
|
197649
|
-
hasSent = false;
|
|
197650
|
-
lastSendAt = 0;
|
|
197651
|
-
failures = 0;
|
|
197652
|
-
finished = false;
|
|
197653
197637
|
abandoned = false;
|
|
197654
|
-
|
|
197638
|
+
publishPromise;
|
|
197655
197639
|
abort = new AbortController();
|
|
197656
197640
|
sched;
|
|
197657
|
-
editIntervalMs;
|
|
197658
197641
|
finalizeTimeoutMs;
|
|
197659
197642
|
footer;
|
|
197660
197643
|
constructor(deps, repo, issueNumber, attribution) {
|
|
@@ -197666,110 +197649,67 @@ var GithubCommentStream = class {
|
|
|
197666
197649
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
197667
197650
|
clearTimeout: (h) => clearTimeout(h)
|
|
197668
197651
|
};
|
|
197669
|
-
this.editIntervalMs = deps.minEditIntervalMs ?? DEFAULT_EDIT_INTERVAL_MS;
|
|
197670
197652
|
this.finalizeTimeoutMs = deps.finalizeTimeoutMs ?? DEFAULT_FINALIZE_TIMEOUT_MS;
|
|
197671
197653
|
this.footer = attributionFooter(attribution);
|
|
197672
197654
|
}
|
|
197673
|
-
/** Fold one agent-output chunk in; schedules the create/edit as needed. Never throws. */
|
|
197674
|
-
append(text) {
|
|
197675
|
-
if (this.finished || this.abandoned || this.failures >= MAX_FAILURES || !text) return;
|
|
197676
|
-
this.buffer += text;
|
|
197677
|
-
this.schedule();
|
|
197678
|
-
}
|
|
197679
|
-
/** Replace the streamed final-answer body with its complete value (messageId-safe). */
|
|
197680
|
-
replace(text) {
|
|
197681
|
-
if (this.finished || this.abandoned || this.failures >= MAX_FAILURES || !text) return;
|
|
197682
|
-
this.buffer = text;
|
|
197683
|
-
this.schedule();
|
|
197684
|
-
}
|
|
197685
197655
|
/**
|
|
197686
|
-
*
|
|
197687
|
-
*
|
|
197656
|
+
* Publish the completed turn's final body exactly once. Resolves once the POST
|
|
197657
|
+
* settles (or the bounded wait expires); never rejects. Idempotent: the first
|
|
197658
|
+
* call wins, including an empty final.
|
|
197688
197659
|
*/
|
|
197689
|
-
|
|
197690
|
-
if (!this.
|
|
197691
|
-
return this.
|
|
197660
|
+
publish(finalBody) {
|
|
197661
|
+
if (!this.publishPromise) this.publishPromise = this.publishOnce(finalBody);
|
|
197662
|
+
return this.publishPromise;
|
|
197692
197663
|
}
|
|
197693
|
-
async
|
|
197694
|
-
if (finalBody
|
|
197695
|
-
this.finished = true;
|
|
197696
|
-
if (this.timer !== null) {
|
|
197697
|
-
this.sched.clearTimeout(this.timer);
|
|
197698
|
-
this.timer = null;
|
|
197699
|
-
}
|
|
197700
|
-
if (!this.buffer.trim() && !this.busy) return;
|
|
197664
|
+
async publishOnce(finalBody) {
|
|
197665
|
+
if (!finalBody?.trim()) return;
|
|
197701
197666
|
let deadlineHandle;
|
|
197702
197667
|
try {
|
|
197668
|
+
const deadlineAt = this.sched.now() + this.finalizeTimeoutMs;
|
|
197703
197669
|
const deadline = new Promise((resolve) => {
|
|
197704
|
-
|
|
197705
|
-
this.
|
|
197706
|
-
|
|
197707
|
-
try {
|
|
197708
|
-
this.deps.log.warn(`github poster: final flush timed out on ${this.repo}#${this.issueNumber}`);
|
|
197709
|
-
} catch {} finally {
|
|
197670
|
+
try {
|
|
197671
|
+
deadlineHandle = this.sched.setTimeout(() => {
|
|
197672
|
+
this.abandonTimedOut();
|
|
197710
197673
|
resolve();
|
|
197711
|
-
}
|
|
197712
|
-
}
|
|
197713
|
-
|
|
197714
|
-
|
|
197715
|
-
|
|
197716
|
-
if (this.abandoned) return;
|
|
197717
|
-
if ((this.commentId !== null || !this.renderedCurrent()) && this.buffer.trim() && this.failures < MAX_FAILURES) {
|
|
197718
|
-
this.inFlight = this.send();
|
|
197719
|
-
await this.inFlight;
|
|
197674
|
+
}, this.finalizeTimeoutMs);
|
|
197675
|
+
} catch (err) {
|
|
197676
|
+
this.abandon();
|
|
197677
|
+
this.safeWarn(`github poster: publish deadline failed on ${this.repo}#${this.issueNumber} (${String(err)})`);
|
|
197678
|
+
resolve();
|
|
197720
197679
|
}
|
|
197721
|
-
})
|
|
197722
|
-
await Promise.race([
|
|
197680
|
+
});
|
|
197681
|
+
await Promise.race([this.post(finalBody, deadlineAt), deadline]);
|
|
197723
197682
|
} catch (err) {
|
|
197724
|
-
|
|
197725
|
-
this.deps.log.warn(`github poster: final flush failed on ${this.repo}#${this.issueNumber} (${String(err)})`);
|
|
197726
|
-
} catch {}
|
|
197683
|
+
if (!this.abandoned) this.safeWarn(`github poster: create failed on ${this.repo}#${this.issueNumber} (${String(err)})`);
|
|
197727
197684
|
} finally {
|
|
197728
197685
|
if (deadlineHandle !== void 0) try {
|
|
197729
197686
|
this.sched.clearTimeout(deadlineHandle);
|
|
197730
197687
|
} catch {}
|
|
197731
197688
|
}
|
|
197732
197689
|
}
|
|
197733
|
-
|
|
197734
|
-
|
|
197735
|
-
|
|
197736
|
-
if (
|
|
197737
|
-
|
|
197690
|
+
async post(text, deadlineAt) {
|
|
197691
|
+
const body = this.render(text);
|
|
197692
|
+
const token = await this.deps.token();
|
|
197693
|
+
if (this.abandoned) return;
|
|
197694
|
+
if (this.sched.now() >= deadlineAt) {
|
|
197695
|
+
this.abandonTimedOut();
|
|
197738
197696
|
return;
|
|
197739
197697
|
}
|
|
197740
|
-
const
|
|
197741
|
-
|
|
197742
|
-
|
|
197743
|
-
|
|
197744
|
-
|
|
197745
|
-
|
|
197746
|
-
|
|
197747
|
-
|
|
197748
|
-
|
|
197749
|
-
|
|
197750
|
-
|
|
197751
|
-
const snapshot = this.buffer;
|
|
197698
|
+
const res = await (this.deps.fetchImpl ?? fetch)(`${this.deps.baseUrl ?? "https://api.github.com"}/repos/${this.repo}/issues/${this.issueNumber}/comments`, {
|
|
197699
|
+
method: "POST",
|
|
197700
|
+
headers: {
|
|
197701
|
+
authorization: `Bearer ${token}`,
|
|
197702
|
+
accept: "application/vnd.github+json",
|
|
197703
|
+
"content-type": "application/json",
|
|
197704
|
+
"x-github-api-version": "2022-11-28"
|
|
197705
|
+
},
|
|
197706
|
+
signal: this.abort.signal,
|
|
197707
|
+
body: JSON.stringify({ body })
|
|
197708
|
+
});
|
|
197752
197709
|
try {
|
|
197753
|
-
|
|
197754
|
-
|
|
197755
|
-
|
|
197756
|
-
if (this.commentId === null) {
|
|
197757
|
-
const created = await this.request("POST", `/repos/${this.repo}/issues/${this.issueNumber}/comments`, token, body);
|
|
197758
|
-
this.commentId = created.id;
|
|
197759
|
-
this.deps.log.debug(`github poster: comment ${created.id} opened on ${this.repo}#${this.issueNumber}`);
|
|
197760
|
-
} else await this.request("PATCH", `/repos/${this.repo}/issues/comments/${this.commentId}`, token, body);
|
|
197761
|
-
this.postedBody = body;
|
|
197762
|
-
this.failures = 0;
|
|
197763
|
-
} catch (err) {
|
|
197764
|
-
if (this.abandoned) return;
|
|
197765
|
-
this.failures += 1;
|
|
197766
|
-
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" : ""}`);
|
|
197767
|
-
} finally {
|
|
197768
|
-
this.hasSent = true;
|
|
197769
|
-
this.lastSendAt = this.sched.now();
|
|
197770
|
-
this.busy = false;
|
|
197771
|
-
if (!this.finished && !this.renderedCurrent() && this.failures < MAX_FAILURES) this.schedule();
|
|
197772
|
-
}
|
|
197710
|
+
await res.body?.cancel();
|
|
197711
|
+
} catch {}
|
|
197712
|
+
if (!res.ok) throw new Error(`GitHub POST ${res.status}`);
|
|
197773
197713
|
}
|
|
197774
197714
|
render(text) {
|
|
197775
197715
|
if (text.length + this.footer.length <= MAX_COMMENT_CHARS) {
|
|
@@ -197779,23 +197719,20 @@ var GithubCommentStream = class {
|
|
|
197779
197719
|
const suffix = TRUNCATION_MARKER + this.footer;
|
|
197780
197720
|
return `${truncatedMarkdownPrefix(text, Math.max(0, MAX_COMMENT_CHARS - suffix.length))}${suffix}`;
|
|
197781
197721
|
}
|
|
197782
|
-
|
|
197783
|
-
|
|
197722
|
+
safeWarn(message) {
|
|
197723
|
+
try {
|
|
197724
|
+
this.deps.log.warn(message);
|
|
197725
|
+
} catch {}
|
|
197784
197726
|
}
|
|
197785
|
-
|
|
197786
|
-
|
|
197787
|
-
|
|
197788
|
-
|
|
197789
|
-
|
|
197790
|
-
|
|
197791
|
-
|
|
197792
|
-
|
|
197793
|
-
|
|
197794
|
-
signal: this.abort.signal,
|
|
197795
|
-
body: JSON.stringify({ body })
|
|
197796
|
-
});
|
|
197797
|
-
if (!res.ok) throw new Error(`GitHub ${method} ${res.status}`);
|
|
197798
|
-
return await res.json();
|
|
197727
|
+
abandon() {
|
|
197728
|
+
if (this.abandoned) return false;
|
|
197729
|
+
this.abandoned = true;
|
|
197730
|
+
this.abort.abort();
|
|
197731
|
+
return true;
|
|
197732
|
+
}
|
|
197733
|
+
abandonTimedOut() {
|
|
197734
|
+
if (!this.abandon()) return;
|
|
197735
|
+
this.safeWarn(`github poster: final publish timed out on ${this.repo}#${this.issueNumber}`);
|
|
197799
197736
|
}
|
|
197800
197737
|
};
|
|
197801
197738
|
//#endregion
|
|
@@ -217611,7 +217548,7 @@ var Daemon = class Daemon {
|
|
|
217611
217548
|
kind: "text",
|
|
217612
217549
|
text: githubFinal
|
|
217613
217550
|
});
|
|
217614
|
-
if (p.github) await p.github.
|
|
217551
|
+
if (p.github) await p.github.poster.publish(githubFinal).catch((err) => this.log.warn(`github poster: final publish failed (${formatErr(err)})`));
|
|
217615
217552
|
this.pending.delete(sessionId);
|
|
217616
217553
|
this.store.setSessionState(key, "idle", this.clock.now());
|
|
217617
217554
|
this.emitSessionMetadataSnapshot({
|
|
@@ -218962,20 +218899,17 @@ var Daemon = class Daemon {
|
|
|
218962
218899
|
}
|
|
218963
218900
|
return this.dispatch(agentId, msg, target?.integrationId, void 0, void 0, void 0, githubReply);
|
|
218964
218901
|
}
|
|
218965
|
-
/** Build the per-turn GitHub final-answer selector and
|
|
218902
|
+
/** Build the per-turn GitHub final-answer selector and poster, tokened
|
|
218966
218903
|
* via the repo-targeted gitcred mint (issues/PR write, no contents — never
|
|
218967
|
-
* enters agent env). Attribution is fixed at turn creation so
|
|
218968
|
-
* the
|
|
218904
|
+
* enters agent env). Attribution is fixed at turn creation so the completed
|
|
218905
|
+
* comment carries the agent/session footer. */
|
|
218969
218906
|
makeGithubReply(agentId, ref, sessionId) {
|
|
218970
218907
|
const agent = this.agents.get(agentId);
|
|
218971
218908
|
return {
|
|
218972
218909
|
collector: new GithubReplyCollector(),
|
|
218973
|
-
|
|
218910
|
+
poster: new GithubFinalPoster({
|
|
218974
218911
|
token: async () => (await this.gitCreds.getPostToken(agentId, ref.repo)).token,
|
|
218975
|
-
log: {
|
|
218976
|
-
warn: (m) => this.log.warn(m),
|
|
218977
|
-
debug: (m) => this.log.debug(m)
|
|
218978
|
-
}
|
|
218912
|
+
log: { warn: (m) => this.log.warn(m) }
|
|
218979
218913
|
}, ref.repo, ref.number, {
|
|
218980
218914
|
agentName: agent?.name ?? agentId,
|
|
218981
218915
|
agentUrl: this.agentLink(agentId),
|
|
@@ -219269,4 +219203,4 @@ async function runForeground(opts, deps = {}) {
|
|
|
219269
219203
|
//#endregion
|
|
219270
219204
|
export { ClientTransport as i, CP_SUBPROTOCOL as n, CP_WS_PATH as r, runForeground as t };
|
|
219271
219205
|
|
|
219272
|
-
//# sourceMappingURL=run-foreground-
|
|
219206
|
+
//# sourceMappingURL=run-foreground-B8sw7WKS.js.map
|