@agentconnect.md/daemon 1.0.0-rc.268 → 1.0.0-rc.269

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.
@@ -1,4 +1,4 @@
1
- import { o as loadConfig, t as discoverAgents } from "./load-agents-CW9Q6eYo.js";
1
+ import { o as loadConfig, t as discoverAgents } from "./load-agents-BcaxKtWB.js";
2
2
  //#region src/cli/agent-list.ts
3
3
  async function runAgentList(opts) {
4
4
  const out = opts.out ?? process.stdout;
@@ -18,4 +18,4 @@ async function runAgentList(opts) {
18
18
  //#endregion
19
19
  export { runAgentList };
20
20
 
21
- //# sourceMappingURL=agent-list-csC2r5tB.js.map
21
+ //# sourceMappingURL=agent-list-Bobe1W08.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-list-csC2r5tB.js","names":[],"sources":["../src/cli/agent-list.ts"],"sourcesContent":["import { loadConfig } from '../config/load-config.js'\nimport { discoverAgents } from '../agents/load-agents.js'\n\nexport interface RunAgentListOpts {\n agentsDir?: string\n configPath?: string\n root?: string\n out?: NodeJS.WritableStream\n}\n\n// Read-only view of the local agent directory. Mutating agents (add/remove/\n// enable/disable) is intentionally not a CLI command — agent business config is\n// owned by the Control Plane (agent/upsert + register/ok roster, see\n// cp/cp-agent-registry.ts) or by hand-editing agent.json; the daemon only\n// reconciles what it finds on disk. This lists what that discovery sees.\nexport async function runAgentList(opts: RunAgentListOpts): Promise<void> {\n const out = opts.out ?? process.stdout\n const cfg = loadConfig({\n root: opts.root,\n configPath: opts.configPath,\n optional: true,\n overrides: { agentsDir: opts.agentsDir }\n })\n const agentsDir = cfg.agentsDir!\n const discovered = discoverAgents(agentsDir)\n if (discovered.length === 0) {\n out.write(`no agents found under ${agentsDir}\\n`)\n return\n }\n for (const { agent, dir } of discovered) {\n out.write(`${agent.id}\\t${agent.status}\\t${agent.runtime}\\t${agent.name}\\t${dir}\\n`)\n }\n}\n"],"mappings":";;AAeA,eAAsB,aAAa,MAAuC;CACxE,MAAM,MAAM,KAAK,OAAO,QAAQ;CAOhC,MAAM,YANM,WAAW;EACrB,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,UAAU;EACV,WAAW,EAAE,WAAW,KAAK,UAAU;CACzC,CACoB,CAAC,CAAC;CACtB,MAAM,aAAa,eAAe,SAAS;CAC3C,IAAI,WAAW,WAAW,GAAG;EAC3B,IAAI,MAAM,yBAAyB,UAAU,GAAG;EAChD;CACF;CACA,KAAK,MAAM,EAAE,OAAO,SAAS,YAC3B,IAAI,MAAM,GAAG,MAAM,GAAG,IAAI,MAAM,OAAO,IAAI,MAAM,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG;AAEvF"}
1
+ {"version":3,"file":"agent-list-Bobe1W08.js","names":[],"sources":["../src/cli/agent-list.ts"],"sourcesContent":["import { loadConfig } from '../config/load-config.js'\nimport { discoverAgents } from '../agents/load-agents.js'\n\nexport interface RunAgentListOpts {\n agentsDir?: string\n configPath?: string\n root?: string\n out?: NodeJS.WritableStream\n}\n\n// Read-only view of the local agent directory. Mutating agents (add/remove/\n// enable/disable) is intentionally not a CLI command — agent business config is\n// owned by the Control Plane (agent/upsert + register/ok roster, see\n// cp/cp-agent-registry.ts) or by hand-editing agent.json; the daemon only\n// reconciles what it finds on disk. This lists what that discovery sees.\nexport async function runAgentList(opts: RunAgentListOpts): Promise<void> {\n const out = opts.out ?? process.stdout\n const cfg = loadConfig({\n root: opts.root,\n configPath: opts.configPath,\n optional: true,\n overrides: { agentsDir: opts.agentsDir }\n })\n const agentsDir = cfg.agentsDir!\n const discovered = discoverAgents(agentsDir)\n if (discovered.length === 0) {\n out.write(`no agents found under ${agentsDir}\\n`)\n return\n }\n for (const { agent, dir } of discovered) {\n out.write(`${agent.id}\\t${agent.status}\\t${agent.runtime}\\t${agent.name}\\t${dir}\\n`)\n }\n}\n"],"mappings":";;AAeA,eAAsB,aAAa,MAAuC;CACxE,MAAM,MAAM,KAAK,OAAO,QAAQ;CAOhC,MAAM,YANM,WAAW;EACrB,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,UAAU;EACV,WAAW,EAAE,WAAW,KAAK,UAAU;CACzC,CACoB,CAAC,CAAC;CACtB,MAAM,aAAa,eAAe,SAAS;CAC3C,IAAI,WAAW,WAAW,GAAG;EAC3B,IAAI,MAAM,yBAAyB,UAAU,GAAG;EAChD;CACF;CACA,KAAK,MAAM,EAAE,OAAO,SAAS,YAC3B,IAAI,MAAM,GAAG,MAAM,GAAG,IAAI,MAAM,OAAO,IAAI,MAAM,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG;AAEvF"}
@@ -1,6 +1,6 @@
1
- import { a as agentChildEnv, i as selectAgent, o as loadConfig } from "./load-agents-CW9Q6eYo.js";
1
+ import { a as agentChildEnv, i as selectAgent, o as loadConfig } from "./load-agents-BcaxKtWB.js";
2
2
  import { l as resolveRoot } from "./paths-DEcUO8lC.js";
3
- import { S as resolveRuntimes, d as memoryProviderFor, i as prepareWorkspace, t as AcpHost } from "./acp-host-Ckkd4YgQ.js";
3
+ import { S as resolveRuntimes, d as memoryProviderFor, i as prepareWorkspace, t as AcpHost } from "./acp-host-htcdepjb.js";
4
4
  import { createInterface } from "node:readline";
5
5
  //#region src/cli/chat.ts
6
6
  function renderUpdate(out) {
@@ -79,4 +79,4 @@ async function runChat(opts) {
79
79
  //#endregion
80
80
  export { renderUpdate, runChat };
81
81
 
82
- //# sourceMappingURL=chat-CfaizQM0.js.map
82
+ //# sourceMappingURL=chat-GmwygspN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-CfaizQM0.js","names":[],"sources":["../src/cli/chat.ts"],"sourcesContent":["import { createInterface } from 'node:readline'\nimport type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk'\nimport { loadConfig } from '../config/load-config.js'\nimport { resolveRuntimes } from '../runtimes/registry.js'\nimport { selectAgent } from '../agents/load-agents.js'\nimport { agentChildEnv } from '../agents/agent-env.js'\nimport { memoryProviderFor } from '../agents/memory-provider.js'\nimport { prepareWorkspace } from '../workspace/workspace-manager.js'\nimport { AcpHost } from '../acp/acp-host.js'\nimport { resolveRoot } from '../paths.js'\n\nexport function renderUpdate(out: NodeJS.WritableStream) {\n return (_sessionId: string, update: SessionUpdate): void => {\n if (update.sessionUpdate === 'agent_message_chunk' && update.content.type === 'text') {\n out.write(update.content.text)\n } else if (update.sessionUpdate === 'tool_call') {\n const title = ('title' in update && update.title) || update.toolCallId || 'tool'\n out.write(`\\n[tool] ${title}\\n`)\n }\n }\n}\n\nexport interface RunChatOpts {\n agentsDir?: string\n agentName?: string\n message?: string\n configPath?: string\n root?: string\n out?: NodeJS.WritableStream\n input?: NodeJS.ReadableStream\n}\n\nexport async function runChat(opts: RunChatOpts): Promise<void> {\n const out = opts.out ?? process.stdout\n const root = resolveRoot(opts.root)\n const cfg = loadConfig({\n root: opts.root,\n configPath: opts.configPath,\n optional: true,\n overrides: { agentsDir: opts.agentsDir }\n })\n const agent = selectAgent(cfg.agentsDir!, opts.agentName)\n\n const runtimes = await resolveRuntimes(cfg, root, { neededRuntimes: [agent.runtime], mode: 'cache-first' })\n const runtime = runtimes[agent.runtime]\n if (!runtime) {\n const available = Object.keys(runtimes).sort().join(', ') || '(none)'\n throw new Error(`runtime \"${agent.runtime}\" not found. Available: ${available}`)\n }\n\n const host = new AcpHost(runtime, {\n onUpdate: renderUpdate(out),\n // Memory-backend env for parity with the daemon (managed disables the runtime's\n // own memory; native redirects it under the agent root).\n env: { ...agentChildEnv(agent), ...memoryProviderFor(agent, runtime).runtimeEnv() },\n configPrefs: {\n model: agent.runtimeOverrides?.model,\n permissionMode: agent.permissionMode,\n reasoningEffort: agent.reasoningEffort,\n fastMode: agent.fastMode\n }\n })\n await host.start()\n try {\n const cwd = await prepareWorkspace(agent)\n const sessionId = await host.newSession(cwd)\n\n const send = async (text: string) => {\n const blocks: ContentBlock[] = [{ type: 'text', text }]\n await host.prompt(sessionId, blocks)\n out.write('\\n')\n }\n\n if (typeof opts.message === 'string' && opts.message.length > 0) {\n await send(opts.message)\n return\n }\n\n const rl = createInterface({ input: opts.input ?? process.stdin })\n out.write(`Chatting with ${agent.name} (${agent.id}). Type .exit to quit.\\n> `)\n try {\n for await (const line of rl) {\n const text = line.trim()\n if (text === '.exit' || text === '.quit') break\n if (text.length > 0) await send(text)\n out.write('> ')\n }\n } finally {\n rl.close()\n }\n } finally {\n await host.stop()\n }\n}\n"],"mappings":";;;;;AAWA,SAAgB,aAAa,KAA4B;CACvD,QAAQ,YAAoB,WAAgC;EAC1D,IAAI,OAAO,kBAAkB,yBAAyB,OAAO,QAAQ,SAAS,QAC5E,IAAI,MAAM,OAAO,QAAQ,IAAI;OACxB,IAAI,OAAO,kBAAkB,aAAa;GAC/C,MAAM,QAAS,WAAW,UAAU,OAAO,SAAU,OAAO,cAAc;GAC1E,IAAI,MAAM,YAAY,MAAM,GAAG;EACjC;CACF;AACF;AAYA,eAAsB,QAAQ,MAAkC;CAC9D,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,OAAO,YAAY,KAAK,IAAI;CAClC,MAAM,MAAM,WAAW;EACrB,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,UAAU;EACV,WAAW,EAAE,WAAW,KAAK,UAAU;CACzC,CAAC;CACD,MAAM,QAAQ,YAAY,IAAI,WAAY,KAAK,SAAS;CAExD,MAAM,WAAW,MAAM,gBAAgB,KAAK,MAAM;EAAE,gBAAgB,CAAC,MAAM,OAAO;EAAG,MAAM;CAAc,CAAC;CAC1G,MAAM,UAAU,SAAS,MAAM;CAC/B,IAAI,CAAC,SAAS;EACZ,MAAM,YAAY,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK;EAC7D,MAAM,IAAI,MAAM,YAAY,MAAM,QAAQ,0BAA0B,WAAW;CACjF;CAEA,MAAM,OAAO,IAAI,QAAQ,SAAS;EAChC,UAAU,aAAa,GAAG;EAG1B,KAAK;GAAE,GAAG,cAAc,KAAK;GAAG,GAAG,kBAAkB,OAAO,OAAO,CAAC,CAAC,WAAW;EAAE;EAClF,aAAa;GACX,OAAO,MAAM,kBAAkB;GAC/B,gBAAgB,MAAM;GACtB,iBAAiB,MAAM;GACvB,UAAU,MAAM;EAClB;CACF,CAAC;CACD,MAAM,KAAK,MAAM;CACjB,IAAI;EACF,MAAM,MAAM,MAAM,iBAAiB,KAAK;EACxC,MAAM,YAAY,MAAM,KAAK,WAAW,GAAG;EAE3C,MAAM,OAAO,OAAO,SAAiB;GACnC,MAAM,SAAyB,CAAC;IAAE,MAAM;IAAQ;GAAK,CAAC;GACtD,MAAM,KAAK,OAAO,WAAW,MAAM;GACnC,IAAI,MAAM,IAAI;EAChB;EAEA,IAAI,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,SAAS,GAAG;GAC/D,MAAM,KAAK,KAAK,OAAO;GACvB;EACF;EAEA,MAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK,SAAS,QAAQ,MAAM,CAAC;EACjE,IAAI,MAAM,iBAAiB,MAAM,KAAK,IAAI,MAAM,GAAG,2BAA2B;EAC9E,IAAI;GACF,WAAW,MAAM,QAAQ,IAAI;IAC3B,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,SAAS,WAAW,SAAS,SAAS;IAC1C,IAAI,KAAK,SAAS,GAAG,MAAM,KAAK,IAAI;IACpC,IAAI,MAAM,IAAI;GAChB;EACF,UAAU;GACR,GAAG,MAAM;EACX;CACF,UAAU;EACR,MAAM,KAAK,KAAK;CAClB;AACF"}
1
+ {"version":3,"file":"chat-GmwygspN.js","names":[],"sources":["../src/cli/chat.ts"],"sourcesContent":["import { createInterface } from 'node:readline'\nimport type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk'\nimport { loadConfig } from '../config/load-config.js'\nimport { resolveRuntimes } from '../runtimes/registry.js'\nimport { selectAgent } from '../agents/load-agents.js'\nimport { agentChildEnv } from '../agents/agent-env.js'\nimport { memoryProviderFor } from '../agents/memory-provider.js'\nimport { prepareWorkspace } from '../workspace/workspace-manager.js'\nimport { AcpHost } from '../acp/acp-host.js'\nimport { resolveRoot } from '../paths.js'\n\nexport function renderUpdate(out: NodeJS.WritableStream) {\n return (_sessionId: string, update: SessionUpdate): void => {\n if (update.sessionUpdate === 'agent_message_chunk' && update.content.type === 'text') {\n out.write(update.content.text)\n } else if (update.sessionUpdate === 'tool_call') {\n const title = ('title' in update && update.title) || update.toolCallId || 'tool'\n out.write(`\\n[tool] ${title}\\n`)\n }\n }\n}\n\nexport interface RunChatOpts {\n agentsDir?: string\n agentName?: string\n message?: string\n configPath?: string\n root?: string\n out?: NodeJS.WritableStream\n input?: NodeJS.ReadableStream\n}\n\nexport async function runChat(opts: RunChatOpts): Promise<void> {\n const out = opts.out ?? process.stdout\n const root = resolveRoot(opts.root)\n const cfg = loadConfig({\n root: opts.root,\n configPath: opts.configPath,\n optional: true,\n overrides: { agentsDir: opts.agentsDir }\n })\n const agent = selectAgent(cfg.agentsDir!, opts.agentName)\n\n const runtimes = await resolveRuntimes(cfg, root, { neededRuntimes: [agent.runtime], mode: 'cache-first' })\n const runtime = runtimes[agent.runtime]\n if (!runtime) {\n const available = Object.keys(runtimes).sort().join(', ') || '(none)'\n throw new Error(`runtime \"${agent.runtime}\" not found. Available: ${available}`)\n }\n\n const host = new AcpHost(runtime, {\n onUpdate: renderUpdate(out),\n // Memory-backend env for parity with the daemon (managed disables the runtime's\n // own memory; native redirects it under the agent root).\n env: { ...agentChildEnv(agent), ...memoryProviderFor(agent, runtime).runtimeEnv() },\n configPrefs: {\n model: agent.runtimeOverrides?.model,\n permissionMode: agent.permissionMode,\n reasoningEffort: agent.reasoningEffort,\n fastMode: agent.fastMode\n }\n })\n await host.start()\n try {\n const cwd = await prepareWorkspace(agent)\n const sessionId = await host.newSession(cwd)\n\n const send = async (text: string) => {\n const blocks: ContentBlock[] = [{ type: 'text', text }]\n await host.prompt(sessionId, blocks)\n out.write('\\n')\n }\n\n if (typeof opts.message === 'string' && opts.message.length > 0) {\n await send(opts.message)\n return\n }\n\n const rl = createInterface({ input: opts.input ?? process.stdin })\n out.write(`Chatting with ${agent.name} (${agent.id}). Type .exit to quit.\\n> `)\n try {\n for await (const line of rl) {\n const text = line.trim()\n if (text === '.exit' || text === '.quit') break\n if (text.length > 0) await send(text)\n out.write('> ')\n }\n } finally {\n rl.close()\n }\n } finally {\n await host.stop()\n }\n}\n"],"mappings":";;;;;AAWA,SAAgB,aAAa,KAA4B;CACvD,QAAQ,YAAoB,WAAgC;EAC1D,IAAI,OAAO,kBAAkB,yBAAyB,OAAO,QAAQ,SAAS,QAC5E,IAAI,MAAM,OAAO,QAAQ,IAAI;OACxB,IAAI,OAAO,kBAAkB,aAAa;GAC/C,MAAM,QAAS,WAAW,UAAU,OAAO,SAAU,OAAO,cAAc;GAC1E,IAAI,MAAM,YAAY,MAAM,GAAG;EACjC;CACF;AACF;AAYA,eAAsB,QAAQ,MAAkC;CAC9D,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,OAAO,YAAY,KAAK,IAAI;CAClC,MAAM,MAAM,WAAW;EACrB,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,UAAU;EACV,WAAW,EAAE,WAAW,KAAK,UAAU;CACzC,CAAC;CACD,MAAM,QAAQ,YAAY,IAAI,WAAY,KAAK,SAAS;CAExD,MAAM,WAAW,MAAM,gBAAgB,KAAK,MAAM;EAAE,gBAAgB,CAAC,MAAM,OAAO;EAAG,MAAM;CAAc,CAAC;CAC1G,MAAM,UAAU,SAAS,MAAM;CAC/B,IAAI,CAAC,SAAS;EACZ,MAAM,YAAY,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK;EAC7D,MAAM,IAAI,MAAM,YAAY,MAAM,QAAQ,0BAA0B,WAAW;CACjF;CAEA,MAAM,OAAO,IAAI,QAAQ,SAAS;EAChC,UAAU,aAAa,GAAG;EAG1B,KAAK;GAAE,GAAG,cAAc,KAAK;GAAG,GAAG,kBAAkB,OAAO,OAAO,CAAC,CAAC,WAAW;EAAE;EAClF,aAAa;GACX,OAAO,MAAM,kBAAkB;GAC/B,gBAAgB,MAAM;GACtB,iBAAiB,MAAM;GACvB,UAAU,MAAM;EAClB;CACF,CAAC;CACD,MAAM,KAAK,MAAM;CACjB,IAAI;EACF,MAAM,MAAM,MAAM,iBAAiB,KAAK;EACxC,MAAM,YAAY,MAAM,KAAK,WAAW,GAAG;EAE3C,MAAM,OAAO,OAAO,SAAiB;GACnC,MAAM,SAAyB,CAAC;IAAE,MAAM;IAAQ;GAAK,CAAC;GACtD,MAAM,KAAK,OAAO,WAAW,MAAM;GACnC,IAAI,MAAM,IAAI;EAChB;EAEA,IAAI,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,SAAS,GAAG;GAC/D,MAAM,KAAK,KAAK,OAAO;GACvB;EACF;EAEA,MAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK,SAAS,QAAQ,MAAM,CAAC;EACjE,IAAI,MAAM,iBAAiB,MAAM,KAAK,IAAI,MAAM,GAAG,2BAA2B;EAC9E,IAAI;GACF,WAAW,MAAM,QAAQ,IAAI;IAC3B,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,SAAS,WAAW,SAAS,SAAS;IAC1C,IAAI,KAAK,SAAS,GAAG,MAAM,KAAK,IAAI;IACpC,IAAI,MAAM,IAAI;GAChB;EACF,UAAU;GACR,GAAG,MAAM;EACX;CACF,UAAU;EACR,MAAM,KAAK,KAAK;CAClB;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { l as resolveRoot } from "./paths-DEcUO8lC.js";
2
- import { n as gitcredSocketPath } from "./gitcred-server-Dcsq6QJe.js";
2
+ import { n as gitcredSocketPath } from "./gitcred-server-CMC80O_1.js";
3
3
  import { createConnection } from "node:net";
4
4
  //#region src/cli/git-credential.ts
5
5
  /**
@@ -96,4 +96,4 @@ function ipc(msg) {
96
96
  //#endregion
97
97
  export { runGitCredential };
98
98
 
99
- //# sourceMappingURL=git-credential-BfkyzKn2.js.map
99
+ //# sourceMappingURL=git-credential-n8fSKCLK.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-credential-BfkyzKn2.js","names":[],"sources":["../src/cli/git-credential.ts"],"sourcesContent":["/**\n * `agentconnect git-credential --agent <id>` — the git credential helper\n * (docs/designs/github-app-git-credentials.md §本地 helper 通道).\n *\n * Speaks git's credential-helper protocol on stdin/stdout and proxies to the\n * daemon's gitcred.sock; the token exists only on the reply pipe. Actions:\n * get → username=x-access-token / password=<token> (host must be github.com)\n * erase → forward to the daemon (invalidate the cached token git just had rejected)\n * store → no-op\n *\n * Exit style: on ANY failure print a human-actionable line to stderr and exit 1\n * — since #251 surfaces turn failures to end users, this text is user-facing.\n * NEVER print or log the token outside the protocol response.\n */\nimport { createConnection } from 'node:net'\nimport { resolveRoot } from '../paths.js'\nimport { gitcredSocketPath } from '../cp/gitcred-server.js'\n\ninterface HelperInput {\n protocol?: string\n host?: string\n path?: string\n password?: string\n}\n\nexport async function runGitCredential(action: string, agentId: string): Promise<void> {\n if (action === 'store') return // git offers the accepted credential back — nothing to do\n\n const input = parseStdin(await readStdin())\n\n if (action === 'erase') {\n await ipc({ op: 'erase', agentId, password: input.password }).catch(() => undefined) // best-effort\n return\n }\n if (action !== 'get') return // unknown actions are ignored per the helper contract\n\n if (input.host !== undefined && input.host.toLowerCase() !== 'github.com') {\n // Not ours — stay silent so git can try other helpers / fail cleanly.\n return\n }\n\n const res = await ipc({ op: 'get', agentId })\n if (!res.ok || !res.username || !res.password) {\n process.stderr.write(\n `agentconnect: no git credentials for agent ${agentId}: ${res.error ?? 'unknown error'}\\n` +\n `(the daemon must be running and connected to the control plane)\\n`\n )\n process.exitCode = 1\n return\n }\n // Path check is diagnostics-only (requires useHttpPath=true to even arrive):\n // token scope enforces the real boundary at GitHub. Tolerate case + .git +\n // LFS-ish subpaths; on a hard mismatch, hint at the rename story.\n if (input.path !== undefined) {\n const want = normalizeRepoPath(res.repoFullName ?? '')\n const got = normalizeRepoPath(input.path)\n if (want && got && got !== want && !got.startsWith(`${want}/`)) {\n process.stderr.write(\n `agentconnect: note — git asked for ${got} but this agent's repo is ${want} ` +\n `(repo renamed? recreate the agent to re-point it)\\n`\n )\n }\n }\n process.stdout.write(`username=${res.username}\\npassword=${res.password}\\n`)\n}\n\nfunction normalizeRepoPath(p: string): string {\n return p\n .replace(/^\\/+/, '')\n .replace(/\\.git$/i, '')\n .toLowerCase()\n}\n\nfunction parseStdin(text: string): HelperInput {\n const out: Record<string, string> = {}\n for (const line of text.split('\\n')) {\n const eq = line.indexOf('=')\n if (eq > 0) out[line.slice(0, eq)] = line.slice(eq + 1)\n }\n return out\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let buf = ''\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', (c) => (buf += c))\n process.stdin.on('end', () => resolve(buf))\n process.stdin.on('error', () => resolve(buf))\n })\n}\n\ninterface IpcReply {\n ok: boolean\n username?: string\n password?: string\n repoFullName?: string\n error?: string\n}\n\nfunction ipc(msg: unknown): Promise<IpcReply> {\n const path = gitcredSocketPath(resolveRoot())\n return new Promise((resolve) => {\n const sock = createConnection(path)\n let buf = ''\n const fail = (error: string) => resolve({ ok: false, error })\n sock.setTimeout(15_000, () => {\n sock.destroy()\n fail('daemon did not answer in time')\n })\n sock.on('connect', () => sock.write(JSON.stringify(msg) + '\\n'))\n sock.on('data', (c) => {\n buf += c.toString('utf8')\n const nl = buf.indexOf('\\n')\n if (nl === -1) return\n sock.destroy()\n try {\n resolve(JSON.parse(buf.slice(0, nl)) as IpcReply)\n } catch {\n fail('malformed daemon reply')\n }\n })\n sock.on('error', (e) => fail(`cannot reach the daemon socket at ${path}: ${e.message}`))\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyBA,eAAsB,iBAAiB,QAAgB,SAAgC;CACrF,IAAI,WAAW,SAAS;CAExB,MAAM,QAAQ,WAAW,MAAM,UAAU,CAAC;CAE1C,IAAI,WAAW,SAAS;EACtB,MAAM,IAAI;GAAE,IAAI;GAAS;GAAS,UAAU,MAAM;EAAS,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS;EACnF;CACF;CACA,IAAI,WAAW,OAAO;CAEtB,IAAI,MAAM,SAAS,KAAA,KAAa,MAAM,KAAK,YAAY,MAAM,cAE3D;CAGF,MAAM,MAAM,MAAM,IAAI;EAAE,IAAI;EAAO;CAAQ,CAAC;CAC5C,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,YAAY,CAAC,IAAI,UAAU;EAC7C,QAAQ,OAAO,MACb,8CAA8C,QAAQ,IAAI,IAAI,SAAS,gBAAgB,oEAEzF;EACA,QAAQ,WAAW;EACnB;CACF;CAIA,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,OAAO,kBAAkB,IAAI,gBAAgB,EAAE;EACrD,MAAM,MAAM,kBAAkB,MAAM,IAAI;EACxC,IAAI,QAAQ,OAAO,QAAQ,QAAQ,CAAC,IAAI,WAAW,GAAG,KAAK,EAAE,GAC3D,QAAQ,OAAO,MACb,sCAAsC,IAAI,4BAA4B,KAAK,qDAE7E;CAEJ;CACA,QAAQ,OAAO,MAAM,YAAY,IAAI,SAAS,aAAa,IAAI,SAAS,GAAG;AAC7E;AAEA,SAAS,kBAAkB,GAAmB;CAC5C,OAAO,EACJ,QAAQ,QAAQ,EAAE,CAAC,CACnB,QAAQ,WAAW,EAAE,CAAC,CACtB,YAAY;AACjB;AAEA,SAAS,WAAW,MAA2B;CAC7C,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;EACnC,MAAM,KAAK,KAAK,QAAQ,GAAG;EAC3B,IAAI,KAAK,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,OAAO;AACT;AAEA,SAAS,YAA6B;CACpC,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,MAAM;EACV,QAAQ,MAAM,YAAY,MAAM;EAChC,QAAQ,MAAM,GAAG,SAAS,MAAO,OAAO,CAAE;EAC1C,QAAQ,MAAM,GAAG,aAAa,QAAQ,GAAG,CAAC;EAC1C,QAAQ,MAAM,GAAG,eAAe,QAAQ,GAAG,CAAC;CAC9C,CAAC;AACH;AAUA,SAAS,IAAI,KAAiC;CAC5C,MAAM,OAAO,kBAAkB,YAAY,CAAC;CAC5C,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,OAAO,iBAAiB,IAAI;EAClC,IAAI,MAAM;EACV,MAAM,QAAQ,UAAkB,QAAQ;GAAE,IAAI;GAAO;EAAM,CAAC;EAC5D,KAAK,WAAW,YAAc;GAC5B,KAAK,QAAQ;GACb,KAAK,+BAA+B;EACtC,CAAC;EACD,KAAK,GAAG,iBAAiB,KAAK,MAAM,KAAK,UAAU,GAAG,IAAI,IAAI,CAAC;EAC/D,KAAK,GAAG,SAAS,MAAM;GACrB,OAAO,EAAE,SAAS,MAAM;GACxB,MAAM,KAAK,IAAI,QAAQ,IAAI;GAC3B,IAAI,OAAO,IAAI;GACf,KAAK,QAAQ;GACb,IAAI;IACF,QAAQ,KAAK,MAAM,IAAI,MAAM,GAAG,EAAE,CAAC,CAAa;GAClD,QAAQ;IACN,KAAK,wBAAwB;GAC/B;EACF,CAAC;EACD,KAAK,GAAG,UAAU,MAAM,KAAK,qCAAqC,KAAK,IAAI,EAAE,SAAS,CAAC;CACzF,CAAC;AACH"}
1
+ {"version":3,"file":"git-credential-n8fSKCLK.js","names":[],"sources":["../src/cli/git-credential.ts"],"sourcesContent":["/**\n * `agentconnect git-credential --agent <id>` — the git credential helper\n * (docs/designs/github-app-git-credentials.md §本地 helper 通道).\n *\n * Speaks git's credential-helper protocol on stdin/stdout and proxies to the\n * daemon's gitcred.sock; the token exists only on the reply pipe. Actions:\n * get → username=x-access-token / password=<token> (host must be github.com)\n * erase → forward to the daemon (invalidate the cached token git just had rejected)\n * store → no-op\n *\n * Exit style: on ANY failure print a human-actionable line to stderr and exit 1\n * — since #251 surfaces turn failures to end users, this text is user-facing.\n * NEVER print or log the token outside the protocol response.\n */\nimport { createConnection } from 'node:net'\nimport { resolveRoot } from '../paths.js'\nimport { gitcredSocketPath } from '../cp/gitcred-server.js'\n\ninterface HelperInput {\n protocol?: string\n host?: string\n path?: string\n password?: string\n}\n\nexport async function runGitCredential(action: string, agentId: string): Promise<void> {\n if (action === 'store') return // git offers the accepted credential back — nothing to do\n\n const input = parseStdin(await readStdin())\n\n if (action === 'erase') {\n await ipc({ op: 'erase', agentId, password: input.password }).catch(() => undefined) // best-effort\n return\n }\n if (action !== 'get') return // unknown actions are ignored per the helper contract\n\n if (input.host !== undefined && input.host.toLowerCase() !== 'github.com') {\n // Not ours — stay silent so git can try other helpers / fail cleanly.\n return\n }\n\n const res = await ipc({ op: 'get', agentId })\n if (!res.ok || !res.username || !res.password) {\n process.stderr.write(\n `agentconnect: no git credentials for agent ${agentId}: ${res.error ?? 'unknown error'}\\n` +\n `(the daemon must be running and connected to the control plane)\\n`\n )\n process.exitCode = 1\n return\n }\n // Path check is diagnostics-only (requires useHttpPath=true to even arrive):\n // token scope enforces the real boundary at GitHub. Tolerate case + .git +\n // LFS-ish subpaths; on a hard mismatch, hint at the rename story.\n if (input.path !== undefined) {\n const want = normalizeRepoPath(res.repoFullName ?? '')\n const got = normalizeRepoPath(input.path)\n if (want && got && got !== want && !got.startsWith(`${want}/`)) {\n process.stderr.write(\n `agentconnect: note — git asked for ${got} but this agent's repo is ${want} ` +\n `(repo renamed? recreate the agent to re-point it)\\n`\n )\n }\n }\n process.stdout.write(`username=${res.username}\\npassword=${res.password}\\n`)\n}\n\nfunction normalizeRepoPath(p: string): string {\n return p\n .replace(/^\\/+/, '')\n .replace(/\\.git$/i, '')\n .toLowerCase()\n}\n\nfunction parseStdin(text: string): HelperInput {\n const out: Record<string, string> = {}\n for (const line of text.split('\\n')) {\n const eq = line.indexOf('=')\n if (eq > 0) out[line.slice(0, eq)] = line.slice(eq + 1)\n }\n return out\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let buf = ''\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', (c) => (buf += c))\n process.stdin.on('end', () => resolve(buf))\n process.stdin.on('error', () => resolve(buf))\n })\n}\n\ninterface IpcReply {\n ok: boolean\n username?: string\n password?: string\n repoFullName?: string\n error?: string\n}\n\nfunction ipc(msg: unknown): Promise<IpcReply> {\n const path = gitcredSocketPath(resolveRoot())\n return new Promise((resolve) => {\n const sock = createConnection(path)\n let buf = ''\n const fail = (error: string) => resolve({ ok: false, error })\n sock.setTimeout(15_000, () => {\n sock.destroy()\n fail('daemon did not answer in time')\n })\n sock.on('connect', () => sock.write(JSON.stringify(msg) + '\\n'))\n sock.on('data', (c) => {\n buf += c.toString('utf8')\n const nl = buf.indexOf('\\n')\n if (nl === -1) return\n sock.destroy()\n try {\n resolve(JSON.parse(buf.slice(0, nl)) as IpcReply)\n } catch {\n fail('malformed daemon reply')\n }\n })\n sock.on('error', (e) => fail(`cannot reach the daemon socket at ${path}: ${e.message}`))\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyBA,eAAsB,iBAAiB,QAAgB,SAAgC;CACrF,IAAI,WAAW,SAAS;CAExB,MAAM,QAAQ,WAAW,MAAM,UAAU,CAAC;CAE1C,IAAI,WAAW,SAAS;EACtB,MAAM,IAAI;GAAE,IAAI;GAAS;GAAS,UAAU,MAAM;EAAS,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS;EACnF;CACF;CACA,IAAI,WAAW,OAAO;CAEtB,IAAI,MAAM,SAAS,KAAA,KAAa,MAAM,KAAK,YAAY,MAAM,cAE3D;CAGF,MAAM,MAAM,MAAM,IAAI;EAAE,IAAI;EAAO;CAAQ,CAAC;CAC5C,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,YAAY,CAAC,IAAI,UAAU;EAC7C,QAAQ,OAAO,MACb,8CAA8C,QAAQ,IAAI,IAAI,SAAS,gBAAgB,oEAEzF;EACA,QAAQ,WAAW;EACnB;CACF;CAIA,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,OAAO,kBAAkB,IAAI,gBAAgB,EAAE;EACrD,MAAM,MAAM,kBAAkB,MAAM,IAAI;EACxC,IAAI,QAAQ,OAAO,QAAQ,QAAQ,CAAC,IAAI,WAAW,GAAG,KAAK,EAAE,GAC3D,QAAQ,OAAO,MACb,sCAAsC,IAAI,4BAA4B,KAAK,qDAE7E;CAEJ;CACA,QAAQ,OAAO,MAAM,YAAY,IAAI,SAAS,aAAa,IAAI,SAAS,GAAG;AAC7E;AAEA,SAAS,kBAAkB,GAAmB;CAC5C,OAAO,EACJ,QAAQ,QAAQ,EAAE,CAAC,CACnB,QAAQ,WAAW,EAAE,CAAC,CACtB,YAAY;AACjB;AAEA,SAAS,WAAW,MAA2B;CAC7C,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;EACnC,MAAM,KAAK,KAAK,QAAQ,GAAG;EAC3B,IAAI,KAAK,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,MAAM,KAAK,CAAC;CACxD;CACA,OAAO;AACT;AAEA,SAAS,YAA6B;CACpC,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,MAAM;EACV,QAAQ,MAAM,YAAY,MAAM;EAChC,QAAQ,MAAM,GAAG,SAAS,MAAO,OAAO,CAAE;EAC1C,QAAQ,MAAM,GAAG,aAAa,QAAQ,GAAG,CAAC;EAC1C,QAAQ,MAAM,GAAG,eAAe,QAAQ,GAAG,CAAC;CAC9C,CAAC;AACH;AAUA,SAAS,IAAI,KAAiC;CAC5C,MAAM,OAAO,kBAAkB,YAAY,CAAC;CAC5C,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,OAAO,iBAAiB,IAAI;EAClC,IAAI,MAAM;EACV,MAAM,QAAQ,UAAkB,QAAQ;GAAE,IAAI;GAAO;EAAM,CAAC;EAC5D,KAAK,WAAW,YAAc;GAC5B,KAAK,QAAQ;GACb,KAAK,+BAA+B;EACtC,CAAC;EACD,KAAK,GAAG,iBAAiB,KAAK,MAAM,KAAK,UAAU,GAAG,IAAI,IAAI,CAAC;EAC/D,KAAK,GAAG,SAAS,MAAM;GACrB,OAAO,EAAE,SAAS,MAAM;GACxB,MAAM,KAAK,IAAI,QAAQ,IAAI;GAC3B,IAAI,OAAO,IAAI;GACf,KAAK,QAAQ;GACb,IAAI;IACF,QAAQ,KAAK,MAAM,IAAI,MAAM,GAAG,EAAE,CAAC,CAAa;GAClD,QAAQ;IACN,KAAK,wBAAwB;GAC/B;EACF,CAAC;EACD,KAAK,GAAG,UAAU,MAAM,KAAK,qCAAqC,KAAK,IAAI,EAAE,SAAS,CAAC;CACzF,CAAC;AACH"}
@@ -5,6 +5,18 @@ import { createServer } from "node:net";
5
5
  //#region src/cp/git-credential.ts
6
6
  /** Hand out only while more than this remains (nests under the CP's 15min floor). */
7
7
  const HANDOUT_MIN_MS = 600 * 1e3;
8
+ /** The widened scope set behind `GH_TOKEN` (P2.5 回写): git contents plus the
9
+ * issue/PR scopes `gh issue comment` / `gh pr comment` need. The CP clamps
10
+ * every capability to the agent's gitAccess — a read-only workspace gets a
11
+ * read-only token, never a write scope. */
12
+ const GH_TOKEN_CAPABILITIES = [
13
+ "contents",
14
+ "issues",
15
+ "pull_requests"
16
+ ];
17
+ /** Cache-key prefix separating the GH_TOKEN grant from the plain git one —
18
+ * different capability sets are different tokens with separate lifecycles. */
19
+ const GH_KEY = (agentId) => `gh\u0000${agentId}`;
8
20
  var GitCredUnavailableError = class extends Error {
9
21
  terminal;
10
22
  constructor(message, terminal) {
@@ -26,13 +38,33 @@ var GitCredentialCache = class {
26
38
  }
27
39
  /** A live credential for the agent's workspace repo — cached, coalesced, or freshly pulled. */
28
40
  async get(agentId, reason) {
29
- if (this.denied.has(agentId)) throw new GitCredUnavailableError("control plane denied git credentials for this agent", true);
30
- const cached = this.entries.get(agentId);
41
+ return this.getKeyed(agentId, agentId, {
42
+ agentId,
43
+ reason
44
+ });
45
+ }
46
+ /**
47
+ * The widened GH_TOKEN grant (P2.5 回写) — same cache/coalesce/degradation
48
+ * discipline as {@link get}, under its own key: a distinct capability set is
49
+ * a distinct token. Injected into the agent's runtime env at host spawn;
50
+ * expiry (≤1h, non-renewable) means write-back goes stale until the next
51
+ * host spawn — the documented MVP limitation.
52
+ */
53
+ async getGhToken(agentId) {
54
+ return this.getKeyed(GH_KEY(agentId), agentId, {
55
+ agentId,
56
+ reason: "helper",
57
+ capabilities: [...GH_TOKEN_CAPABILITIES]
58
+ });
59
+ }
60
+ async getKeyed(key, agentId, payload) {
61
+ if (this.denied.has(key)) throw new GitCredUnavailableError("control plane denied git credentials for this agent", true);
62
+ const cached = this.entries.get(key);
31
63
  if (cached && cached.expiresAtMono - this.monoNow() > HANDOUT_MIN_MS) return cached;
32
- let pending = this.inflight.get(agentId);
64
+ let pending = this.inflight.get(key);
33
65
  if (!pending) {
34
- pending = this.fetch(agentId, reason).finally(() => this.inflight.delete(agentId));
35
- this.inflight.set(agentId, pending);
66
+ pending = this.fetch(key, payload).finally(() => this.inflight.delete(key));
67
+ this.inflight.set(key, pending);
36
68
  }
37
69
  try {
38
70
  return await pending;
@@ -56,22 +88,21 @@ var GitCredentialCache = class {
56
88
  if (presentedPassword !== void 0 && entry.token !== presentedPassword) return;
57
89
  this.entries.delete(agentId);
58
90
  }
59
- /** agent/remove: drop the orphaned bearer token with the agent dir. */
91
+ /** agent/remove: drop the orphaned bearer tokens with the agent dir. */
60
92
  remove(agentId) {
61
- this.entries.delete(agentId);
62
- this.denied.delete(agentId);
93
+ for (const key of [agentId, GH_KEY(agentId)]) {
94
+ this.entries.delete(key);
95
+ this.denied.delete(key);
96
+ }
63
97
  }
64
- async fetch(agentId, reason) {
98
+ async fetch(key, payload) {
65
99
  let grant;
66
100
  try {
67
- grant = await this.deps.request({
68
- agentId,
69
- reason
70
- });
101
+ grant = await this.deps.request(payload);
71
102
  } catch (e) {
72
103
  if (e.code === "SCOPE_DENIED") {
73
- this.denied.add(agentId);
74
- this.entries.delete(agentId);
104
+ this.denied.add(key);
105
+ this.entries.delete(key);
75
106
  throw new GitCredUnavailableError(e.message, true);
76
107
  }
77
108
  throw new GitCredUnavailableError(e.message, false);
@@ -83,12 +114,13 @@ var GitCredentialCache = class {
83
114
  access: grant.access,
84
115
  expiresAtMono: this.monoNow() + grant.ttlSec * 1e3
85
116
  };
86
- this.entries.set(agentId, entry);
117
+ this.entries.set(key, entry);
87
118
  return entry;
88
119
  }
89
120
  /** A replicated spec change may re-enable a previously denied agent. */
90
121
  clearDenied(agentId) {
91
122
  this.denied.delete(agentId);
123
+ this.denied.delete(GH_KEY(agentId));
92
124
  }
93
125
  };
94
126
  //#endregion
@@ -233,4 +265,4 @@ function writeGitcredShim(root, cliEntry) {
233
265
  //#endregion
234
266
  export { GitCredentialCache as i, gitcredSocketPath as n, writeGitcredShim as r, GitCredServer as t };
235
267
 
236
- //# sourceMappingURL=gitcred-server-Dcsq6QJe.js.map
268
+ //# sourceMappingURL=gitcred-server-CMC80O_1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitcred-server-CMC80O_1.js","names":[],"sources":["../src/cp/git-credential.ts","../src/cp/gitcred-server.ts"],"sourcesContent":["/**\n * GitCredentialCache — daemon-side memory for CP-minted git credentials\n * (docs/designs/github-app-git-credentials.md §daemon 侧).\n *\n * Tokens live ONLY here (and transiently on helper stdout pipes): never disk,\n * never argv, never a log line. Expiry is tracked on the MONOTONIC clock as\n * `receivedAt + ttlSec` — a skewed wall clock must never resurrect a dead\n * token (`expiresAt` in the grant is observability only).\n *\n * Threshold nesting: entries are handed out only while >10min remain (the CP\n * serves grants with ≥15min, so a fresh grant always clears this). Per-agent\n * in-flight coalescing (the `cloneInFlight` pattern) keeps a cold cache from\n * fanning N concurrent helper/pull/prefetch calls into N WS REQs.\n *\n * Degradation: outside READY the fetch fails fast (see CpClient.requestGitCred)\n * and an UNEXPIRED cached token keeps serving — CP downtime only breaks remote\n * git once the hour runs out.\n */\nimport type { GitCredCapability, GitCredGrant } from '@agentconnect.md/protocol'\n\n/** Hand out only while more than this remains (nests under the CP's 15min floor). */\nconst HANDOUT_MIN_MS = 10 * 60 * 1000\n\n/** The widened scope set behind `GH_TOKEN` (P2.5 回写): git contents plus the\n * issue/PR scopes `gh issue comment` / `gh pr comment` need. The CP clamps\n * every capability to the agent's gitAccess — a read-only workspace gets a\n * read-only token, never a write scope. */\nconst GH_TOKEN_CAPABILITIES: readonly GitCredCapability[] = ['contents', 'issues', 'pull_requests']\n\n/** Cache-key prefix separating the GH_TOKEN grant from the plain git one —\n * different capability sets are different tokens with separate lifecycles. */\nconst GH_KEY = (agentId: string): string => `gh\\u0000${agentId}`\n\nexport class GitCredUnavailableError extends Error {\n constructor(\n message: string,\n /** Terminal (SCOPE_DENIED): stop asking for this agent until config changes. */\n readonly terminal: boolean\n ) {\n super(message)\n this.name = 'GitCredUnavailableError'\n }\n}\n\ninterface Entry {\n username: string\n token: string\n repoFullName: string\n access: 'read' | 'write'\n /** Monotonic deadline (ms on the injected monotonic clock). */\n expiresAtMono: number\n}\n\nexport interface GitCredentialCacheDeps {\n /** D→C `gitcred/request` — throws WireError-shaped errors (`code`, `retryable`). */\n request: (payload: {\n agentId: string\n reason?: 'clone' | 'fetch' | 'pull' | 'push' | 'helper'\n capabilities?: GitCredCapability[]\n }) => Promise<GitCredGrant>\n log: { warn: (msg: string) => void }\n /** Monotonic ms; injectable for tests. */\n monoNow?: () => number\n}\n\nexport class GitCredentialCache {\n private readonly entries = new Map<string, Entry>()\n private readonly inflight = new Map<string, Promise<Entry>>()\n /** Agents the CP told us to stop asking for (SCOPE_DENIED — moved/mode-off). */\n private readonly denied = new Set<string>()\n private readonly monoNow: () => number\n\n constructor(private readonly deps: GitCredentialCacheDeps) {\n this.monoNow = deps.monoNow ?? (() => performance.now())\n }\n\n /** A live credential for the agent's workspace repo — cached, coalesced, or freshly pulled. */\n async get(agentId: string, reason: 'clone' | 'fetch' | 'pull' | 'push' | 'helper'): Promise<Entry> {\n return this.getKeyed(agentId, agentId, { agentId, reason })\n }\n\n /**\n * The widened GH_TOKEN grant (P2.5 回写) — same cache/coalesce/degradation\n * discipline as {@link get}, under its own key: a distinct capability set is\n * a distinct token. Injected into the agent's runtime env at host spawn;\n * expiry (≤1h, non-renewable) means write-back goes stale until the next\n * host spawn — the documented MVP limitation.\n */\n async getGhToken(agentId: string): Promise<Entry> {\n return this.getKeyed(GH_KEY(agentId), agentId, {\n agentId,\n reason: 'helper',\n capabilities: [...GH_TOKEN_CAPABILITIES]\n })\n }\n\n private async getKeyed(\n key: string,\n agentId: string,\n payload: Parameters<GitCredentialCacheDeps['request']>[0]\n ): Promise<Entry> {\n if (this.denied.has(key)) {\n throw new GitCredUnavailableError('control plane denied git credentials for this agent', true)\n }\n const cached = this.entries.get(key)\n if (cached && cached.expiresAtMono - this.monoNow() > HANDOUT_MIN_MS) return cached\n\n let pending = this.inflight.get(key)\n if (!pending) {\n pending = this.fetch(key, payload).finally(() => this.inflight.delete(key))\n this.inflight.set(key, pending)\n }\n try {\n return await pending\n } catch (e) {\n // CP unreachable / transient: an UNEXPIRED cached token still serves.\n if (cached && cached.expiresAtMono > this.monoNow()) {\n this.deps.log.warn(`gitcred: refresh failed for agent ${agentId} — serving cached token`)\n return cached\n }\n throw e\n }\n }\n\n /**\n * Invalidate on rejection. Git calls the helper's `erase` with the failing\n * credential when the remote refuses it (GitHub revokes instantly on\n * uninstall/suspend) — matching on the presented password keeps a racing\n * fresh grant from being wiped by a stale erase.\n */\n invalidate(agentId: string, presentedPassword?: string): void {\n const entry = this.entries.get(agentId)\n if (!entry) return\n if (presentedPassword !== undefined && entry.token !== presentedPassword) return\n this.entries.delete(agentId)\n }\n\n /** agent/remove: drop the orphaned bearer tokens with the agent dir. */\n remove(agentId: string): void {\n for (const key of [agentId, GH_KEY(agentId)]) {\n this.entries.delete(key)\n this.denied.delete(key)\n }\n }\n\n private async fetch(key: string, payload: Parameters<GitCredentialCacheDeps['request']>[0]): Promise<Entry> {\n let grant: GitCredGrant\n try {\n grant = await this.deps.request(payload)\n } catch (e) {\n const code = (e as { code?: string }).code\n if (code === 'SCOPE_DENIED') {\n // Terminal: the agent moved daemons or left github-app mode. Stop\n // asking until the CP replicates a new spec (which clears the flag).\n this.denied.add(key)\n this.entries.delete(key)\n throw new GitCredUnavailableError((e as Error).message, true)\n }\n throw new GitCredUnavailableError((e as Error).message, false)\n }\n const entry: Entry = {\n username: grant.username,\n token: grant.token,\n repoFullName: grant.repoFullName,\n access: grant.access,\n expiresAtMono: this.monoNow() + grant.ttlSec * 1000\n }\n this.entries.set(key, entry)\n return entry\n }\n\n /** A replicated spec change may re-enable a previously denied agent. */\n clearDenied(agentId: string): void {\n this.denied.delete(agentId)\n this.denied.delete(GH_KEY(agentId))\n }\n}\n","/**\n * gitcred.sock — the local credential channel for agent-run git\n * (docs/designs/github-app-git-credentials.md §本地 helper 通道).\n *\n * A tiny newline-delimited-JSON server over a unix socket (0700 dir + 0600\n * socket, stale-socket cleanup — the `mcp/control-server.ts` pattern). The\n * `agentconnect git-credential` CLI subcommand connects per git invocation:\n * { op: 'get', agentId } → { ok, username, password } | { ok:false, error }\n * { op: 'erase', agentId, password? } → { ok: true }\n *\n * Trust model (design §信任模型 #1): same host + same OS user = ONE trust\n * domain. `agentId` here is routing, not authorization — any same-user process\n * can already read every workspace on this daemon. Tokens transit the socket\n * and helper stdout only; nothing lands on disk.\n *\n * Alongside the socket the daemon (re)writes two SECRET-FREE files per boot:\n * the shim `run/git-credential-helper.sh` (pins the current node + CLI path so\n * `.git/config` survives daemon upgrades) and per-agent gitconfig includes for\n * the session-env channel (see workspace/git-injection.ts).\n */\nimport { createServer, type Server, type Socket } from 'node:net'\nimport { chmodSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join } from 'node:path'\nimport { GitCredentialCache, GitCredUnavailableError } from './git-credential.js'\n\nexport function gitcredSocketPath(root: string): string {\n return join(root, 'run', 'gitcred.sock')\n}\n\nexport function gitcredShimPath(root: string): string {\n return join(root, 'run', 'git-credential-helper.sh')\n}\n\ninterface GitCredIpcRequest {\n op: 'get' | 'erase'\n agentId: string\n password?: string\n}\n\nexport class GitCredServer {\n private server?: Server\n\n constructor(\n private readonly cache: GitCredentialCache,\n private readonly path: string,\n private readonly log: { info: (m: string) => void; warn: (m: string) => void }\n ) {}\n\n start(): void {\n const dir = dirname(this.path)\n mkdirSync(dir, { recursive: true, mode: 0o700 })\n try {\n chmodSync(dir, 0o700) // defeat a loose umask; best-effort on non-POSIX\n } catch {\n /* best-effort */\n }\n rmSync(this.path, { force: true }) // a stale socket from a crash makes listen() throw\n\n const server = createServer((sock) => this.serve(sock))\n server.listen(this.path, () => {\n try {\n chmodSync(this.path, 0o600)\n } catch {\n /* best-effort */\n }\n this.log.info(`gitcred: helper socket at ${this.path}`)\n })\n server.on('error', (e) => this.log.warn(`gitcred: socket error: ${e.message}`))\n this.server = server\n }\n\n stop(): void {\n this.server?.close()\n rmSync(this.path, { force: true })\n }\n\n private serve(sock: Socket): void {\n let buf = ''\n sock.on('data', (chunk) => {\n buf += chunk.toString('utf8')\n const nl = buf.indexOf('\\n')\n if (nl === -1) return\n const line = buf.slice(0, nl)\n buf = buf.slice(nl + 1)\n void this.handle(line, sock)\n })\n sock.on('error', () => sock.destroy())\n }\n\n private async handle(line: string, sock: Socket): Promise<void> {\n const reply = (msg: unknown) => {\n sock.write(JSON.stringify(msg) + '\\n')\n sock.end()\n }\n let req: GitCredIpcRequest\n try {\n req = JSON.parse(line) as GitCredIpcRequest\n } catch {\n return reply({ ok: false, error: 'malformed request' })\n }\n if (req.op === 'erase') {\n // Git presents the rejected credential — GitHub revokes instantly on\n // uninstall/suspend, and this is how the daemon cache learns.\n this.cache.invalidate(req.agentId, req.password)\n return reply({ ok: true })\n }\n if (req.op !== 'get' || typeof req.agentId !== 'string') {\n return reply({ ok: false, error: 'unsupported op' })\n }\n try {\n const cred = await this.cache.get(req.agentId, 'helper')\n return reply({ ok: true, username: cred.username, password: cred.token })\n } catch (e) {\n const msg =\n e instanceof GitCredUnavailableError ? e.message : `git credentials unavailable: ${(e as Error).message}`\n return reply({ ok: false, error: msg })\n }\n }\n}\n\n/**\n * (Re)write the secret-free shim `.git/config` helper lines exec through. The\n * absolute node + CLI paths are re-pinned every daemon boot, so repo configs\n * keep working across upgrades/relocations. Quoted throughout — a home dir\n * with a space (macOS \"/Users/John Smith/…\") must not word-split.\n */\nexport function writeGitcredShim(root: string, cliEntry: string): string {\n const shim = gitcredShimPath(root)\n mkdirSync(dirname(shim), { recursive: true, mode: 0o700 })\n const q = (v: string) => `'${v.replaceAll(\"'\", \"'\\\\''\")}'`\n // Production runs the built dist (a .js entry node executes directly). A dev\n // daemon runs under tsx with a .ts argv[1] — route the shim through the tsx\n // CLI then, or plain `node entry.ts` would die resolving .js-suffixed imports.\n const argv = [q(process.execPath)]\n if (cliEntry.endsWith('.ts')) {\n const req = createRequire(import.meta.url)\n argv.push(q(req.resolve('tsx/cli')))\n }\n argv.push(q(cliEntry))\n const body = [\n '#!/bin/sh',\n '# agentconnect git credential helper shim — regenerated on daemon start; NO secrets.',\n `AGENTCONNECT_ROOT=${q(root)} \\\\`,\n ` exec ${argv.join(' ')} git-credential \"$@\"`,\n ''\n ].join('\\n')\n writeFileSync(shim, body, { mode: 0o755 })\n return shim\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -68492,9 +68492,9 @@ const telemetry = startDaemonOpenTelemetry({ serviceVersion: DAEMON_VERSION });
68492
68492
  const [{ Command }, { fileURLToPath }, { runChat }, { runLogin }, { runForeground }, { resolveController }, { acquireSingletonLock }, { resolveRoot }] = await Promise.all([
68493
68493
  import("./commander-B5QVEFzH.js"),
68494
68494
  import("node:url"),
68495
- import("./chat-CfaizQM0.js"),
68496
- import("./login-DQncbOrL.js"),
68497
- import("./run-foreground-C96zWzzD.js"),
68495
+ import("./chat-GmwygspN.js"),
68496
+ import("./login-BSddEKOO.js"),
68497
+ import("./run-foreground-c6btHIVL.js"),
68498
68498
  import("./service-6w0WKhdx.js"),
68499
68499
  import("./lock-CA9Bm8_s.js"),
68500
68500
  import("./paths-CcRUjFjy.js")
@@ -68646,13 +68646,13 @@ program.command("login").description("Interactive onboarding: test the Control P
68646
68646
  }
68647
68647
  });
68648
68648
  program.command("git-credential").description("Git credential helper backed by the local daemon (github-app workspaces)").argument("<agentId>", "agent whose workspace credentials to serve").argument("<action>", "git credential action: get | store | erase").action(async (agentId, action) => {
68649
- const { runGitCredential } = await import("./git-credential-BfkyzKn2.js");
68649
+ const { runGitCredential } = await import("./git-credential-n8fSKCLK.js");
68650
68650
  await runGitCredential(action, agentId);
68651
68651
  });
68652
68652
  program.command("agent").description("Inspect local agent directories").command("list").description("List agents discovered under --agents-dir (id, status, runtime, name, dir)").action(async () => {
68653
68653
  const opts = program.opts();
68654
68654
  try {
68655
- const { runAgentList } = await import("./agent-list-csC2r5tB.js");
68655
+ const { runAgentList } = await import("./agent-list-Bobe1W08.js");
68656
68656
  await runAgentList({
68657
68657
  agentsDir: opts.agentsDir,
68658
68658
  configPath: opts.config,
@@ -638,6 +638,19 @@ const HookReport = object({
638
638
  * left its grant set — recoverable only by an operator), `RATE_LIMITED`,
639
639
  * `INTERNAL`.
640
640
  */
641
+ /**
642
+ * Token capability classes (webhook-triggers-and-github-events.md P2.5 回写).
643
+ * `contents` is the git data plane (the pre-capabilities behavior); `issues` /
644
+ * `pull_requests` buy the agent `gh` write-back (issue/PR comments). Every
645
+ * capability is minted at the agent's `gitAccess` level — a read-only agent
646
+ * gets read-only issues/PR scopes, never write (安全边界: the workspace's
647
+ * blast-radius cap applies to ALL capability classes uniformly).
648
+ */
649
+ const GitCredCapability = _enum([
650
+ "contents",
651
+ "issues",
652
+ "pull_requests"
653
+ ]);
641
654
  const GitCredRequest = object({
642
655
  agentId: string().uuid(),
643
656
  reason: _enum([
@@ -646,7 +659,8 @@ const GitCredRequest = object({
646
659
  "pull",
647
660
  "push",
648
661
  "helper"
649
- ]).optional()
662
+ ]).optional(),
663
+ capabilities: array(GitCredCapability).nonempty().optional()
650
664
  });
651
665
  const GitCredGrant = object({
652
666
  username: literal("x-access-token"),
@@ -1573,6 +1587,10 @@ const RcRunReport = object({
1573
1587
  status: _enum(["accepted", "failed"]),
1574
1588
  reason: string().optional()
1575
1589
  });
1590
+ const RcGithubInstallation = object({
1591
+ installationId: string().min(1),
1592
+ action: string().min(1)
1593
+ });
1576
1594
  const RcDaemonRevoke = object({ daemonId: string().uuid() });
1577
1595
  const SHARED_CONFIG_ACTION_ID = "ac_shared_channel_config";
1578
1596
  const AttributedRoute = object({
@@ -1606,6 +1624,7 @@ const RELAY_CP_SCHEMAS = {
1606
1624
  "rc/hook-assign": RcHookAssign,
1607
1625
  "rc/hook-remove": RcHookRemove,
1608
1626
  "rc/run-report": RcRunReport,
1627
+ "rc/github-installation": RcGithubInstallation,
1609
1628
  "rc/daemon-revoke": RcDaemonRevoke,
1610
1629
  "rc/bot-assign": object({
1611
1630
  botId: string().uuid(),
@@ -1662,6 +1681,7 @@ discriminatedUnion("type", [
1662
1681
  frameSchema("rc/hook-assign", RELAY_CP_SCHEMAS["rc/hook-assign"]),
1663
1682
  frameSchema("rc/hook-remove", RELAY_CP_SCHEMAS["rc/hook-remove"]),
1664
1683
  frameSchema("rc/run-report", RELAY_CP_SCHEMAS["rc/run-report"]),
1684
+ frameSchema("rc/github-installation", RELAY_CP_SCHEMAS["rc/github-installation"]),
1665
1685
  frameSchema("rc/daemon-revoke", RELAY_CP_SCHEMAS["rc/daemon-revoke"]),
1666
1686
  frameSchema("rc/bot-assign", RELAY_CP_SCHEMAS["rc/bot-assign"]),
1667
1687
  frameSchema("rc/bot-unassign", RELAY_CP_SCHEMAS["rc/bot-unassign"]),
@@ -2282,4 +2302,4 @@ function selectAgent(agentsDir, name) {
2282
2302
  //#endregion
2283
2303
  export { ZodIssueCode as C, RESERVED_MCP_SERVER_NAME as S, buildEnvelope as _, agentChildEnv as a, MAX_FRAME_BYTES as b, persistRelays as c, normalizeGitUrl as d, RELAY_DAEMON_SUBPROTOCOL as f, SHARED_CONFIG_ACTION_ID as g, decodeRelayDaemonFrame as h, selectAgent as i, ConfigSchema as l, buildRelayDaemonFrame as m, findAgentFiles as n, loadConfig as o, RELAY_DAEMON_WS_PATH as p, loadAgents as r, persistDaemonId as s, discoverAgents as t, isFrame as u, decodeEnvelope as v, MEMORY_INDEX as x, encode as y };
2284
2304
 
2285
- //# sourceMappingURL=load-agents-CW9Q6eYo.js.map
2305
+ //# sourceMappingURL=load-agents-BcaxKtWB.js.map