@agentconnect.md/daemon 1.0.0-rc.22 → 1.0.0-rc.221
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-zyZ_j0yY.js +10570 -0
- package/dist/acp-host-zyZ_j0yY.js.map +1 -0
- package/dist/agent-list-Vx-p1X3q.js +21 -0
- package/dist/agent-list-Vx-p1X3q.js.map +1 -0
- package/dist/bridge-DoUrmBw9.js +9904 -0
- package/dist/bridge-DoUrmBw9.js.map +1 -0
- package/dist/chat-CqfkPkqK.js +79 -0
- package/dist/chat-CqfkPkqK.js.map +1 -0
- package/dist/commander-B5QVEFzH.js +2965 -0
- package/dist/commander-B5QVEFzH.js.map +1 -0
- package/dist/esm-DrGCKNjx.js +1516 -0
- package/dist/esm-DrGCKNjx.js.map +1 -0
- package/dist/execAsync-C-9b-eWN.js +12 -0
- package/dist/execAsync-C-9b-eWN.js.map +1 -0
- package/dist/getMachineId-bsd-CfqSzACM.js +29 -0
- package/dist/getMachineId-bsd-CfqSzACM.js.map +1 -0
- package/dist/getMachineId-darwin-CDKafrUs.js +26 -0
- package/dist/getMachineId-darwin-CDKafrUs.js.map +1 -0
- package/dist/getMachineId-linux-DG3AvXtg.js +22 -0
- package/dist/getMachineId-linux-DG3AvXtg.js.map +1 -0
- package/dist/getMachineId-unsupported-BIPpTXqb.js +17 -0
- package/dist/getMachineId-unsupported-BIPpTXqb.js.map +1 -0
- package/dist/getMachineId-win-DNO3uByP.js +28 -0
- package/dist/getMachineId-win-DNO3uByP.js.map +1 -0
- package/dist/git-credential-BfkyzKn2.js +99 -0
- package/dist/git-credential-BfkyzKn2.js.map +1 -0
- package/dist/gitcred-server-Dcsq6QJe.js +236 -0
- package/dist/gitcred-server-Dcsq6QJe.js.map +1 -0
- package/dist/index.js +67364 -79446
- package/dist/index.js.map +1 -1
- package/dist/ipc-2Xbxe7uu.js +35 -0
- package/dist/ipc-2Xbxe7uu.js.map +1 -0
- package/dist/load-agents-C1m5Y9n_.js +317 -0
- package/dist/load-agents-C1m5Y9n_.js.map +1 -0
- package/dist/lock-CA9Bm8_s.js +66 -0
- package/dist/lock-CA9Bm8_s.js.map +1 -0
- package/dist/login-DUhkVhAr.js +229 -0
- package/dist/login-DUhkVhAr.js.map +1 -0
- package/dist/paths-CcRUjFjy.js +2 -0
- package/dist/paths-DEcUO8lC.js +43 -0
- package/dist/paths-DEcUO8lC.js.map +1 -0
- package/dist/rolldown-runtime-CE-6LUnI.js +44 -0
- package/dist/run-foreground-PSOzCSP8.js +2 -0
- package/dist/run-foreground-mvTzCScX.js +84591 -0
- package/dist/run-foreground-mvTzCScX.js.map +1 -0
- package/dist/schemas-DujcoO0q.js +4357 -0
- package/dist/schemas-DujcoO0q.js.map +1 -0
- package/dist/service-6w0WKhdx.js +2 -0
- package/dist/service-BQVw7wbz.js +245 -0
- package/dist/service-BQVw7wbz.js.map +1 -0
- package/dist/src-DDtlVutj.js +811 -0
- package/dist/src-DDtlVutj.js.map +1 -0
- package/dist/version-DGaRXjBK.js +7 -0
- package/dist/version-DGaRXjBK.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { chmodSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
//#region src/cp/git-credential.ts
|
|
6
|
+
/** Hand out only while more than this remains (nests under the CP's 15min floor). */
|
|
7
|
+
const HANDOUT_MIN_MS = 600 * 1e3;
|
|
8
|
+
var GitCredUnavailableError = class extends Error {
|
|
9
|
+
terminal;
|
|
10
|
+
constructor(message, terminal) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.terminal = terminal;
|
|
13
|
+
this.name = "GitCredUnavailableError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var GitCredentialCache = class {
|
|
17
|
+
deps;
|
|
18
|
+
entries = /* @__PURE__ */ new Map();
|
|
19
|
+
inflight = /* @__PURE__ */ new Map();
|
|
20
|
+
/** Agents the CP told us to stop asking for (SCOPE_DENIED — moved/mode-off). */
|
|
21
|
+
denied = /* @__PURE__ */ new Set();
|
|
22
|
+
monoNow;
|
|
23
|
+
constructor(deps) {
|
|
24
|
+
this.deps = deps;
|
|
25
|
+
this.monoNow = deps.monoNow ?? (() => performance.now());
|
|
26
|
+
}
|
|
27
|
+
/** A live credential for the agent's workspace repo — cached, coalesced, or freshly pulled. */
|
|
28
|
+
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);
|
|
31
|
+
if (cached && cached.expiresAtMono - this.monoNow() > HANDOUT_MIN_MS) return cached;
|
|
32
|
+
let pending = this.inflight.get(agentId);
|
|
33
|
+
if (!pending) {
|
|
34
|
+
pending = this.fetch(agentId, reason).finally(() => this.inflight.delete(agentId));
|
|
35
|
+
this.inflight.set(agentId, pending);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return await pending;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
if (cached && cached.expiresAtMono > this.monoNow()) {
|
|
41
|
+
this.deps.log.warn(`gitcred: refresh failed for agent ${agentId} — serving cached token`);
|
|
42
|
+
return cached;
|
|
43
|
+
}
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Invalidate on rejection. Git calls the helper's `erase` with the failing
|
|
49
|
+
* credential when the remote refuses it (GitHub revokes instantly on
|
|
50
|
+
* uninstall/suspend) — matching on the presented password keeps a racing
|
|
51
|
+
* fresh grant from being wiped by a stale erase.
|
|
52
|
+
*/
|
|
53
|
+
invalidate(agentId, presentedPassword) {
|
|
54
|
+
const entry = this.entries.get(agentId);
|
|
55
|
+
if (!entry) return;
|
|
56
|
+
if (presentedPassword !== void 0 && entry.token !== presentedPassword) return;
|
|
57
|
+
this.entries.delete(agentId);
|
|
58
|
+
}
|
|
59
|
+
/** agent/remove: drop the orphaned bearer token with the agent dir. */
|
|
60
|
+
remove(agentId) {
|
|
61
|
+
this.entries.delete(agentId);
|
|
62
|
+
this.denied.delete(agentId);
|
|
63
|
+
}
|
|
64
|
+
async fetch(agentId, reason) {
|
|
65
|
+
let grant;
|
|
66
|
+
try {
|
|
67
|
+
grant = await this.deps.request({
|
|
68
|
+
agentId,
|
|
69
|
+
reason
|
|
70
|
+
});
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (e.code === "SCOPE_DENIED") {
|
|
73
|
+
this.denied.add(agentId);
|
|
74
|
+
this.entries.delete(agentId);
|
|
75
|
+
throw new GitCredUnavailableError(e.message, true);
|
|
76
|
+
}
|
|
77
|
+
throw new GitCredUnavailableError(e.message, false);
|
|
78
|
+
}
|
|
79
|
+
const entry = {
|
|
80
|
+
username: grant.username,
|
|
81
|
+
token: grant.token,
|
|
82
|
+
repoFullName: grant.repoFullName,
|
|
83
|
+
access: grant.access,
|
|
84
|
+
expiresAtMono: this.monoNow() + grant.ttlSec * 1e3
|
|
85
|
+
};
|
|
86
|
+
this.entries.set(agentId, entry);
|
|
87
|
+
return entry;
|
|
88
|
+
}
|
|
89
|
+
/** A replicated spec change may re-enable a previously denied agent. */
|
|
90
|
+
clearDenied(agentId) {
|
|
91
|
+
this.denied.delete(agentId);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/cp/gitcred-server.ts
|
|
96
|
+
/**
|
|
97
|
+
* gitcred.sock — the local credential channel for agent-run git
|
|
98
|
+
* (docs/designs/github-app-git-credentials.md §本地 helper 通道).
|
|
99
|
+
*
|
|
100
|
+
* A tiny newline-delimited-JSON server over a unix socket (0700 dir + 0600
|
|
101
|
+
* socket, stale-socket cleanup — the `mcp/control-server.ts` pattern). The
|
|
102
|
+
* `agentconnect git-credential` CLI subcommand connects per git invocation:
|
|
103
|
+
* { op: 'get', agentId } → { ok, username, password } | { ok:false, error }
|
|
104
|
+
* { op: 'erase', agentId, password? } → { ok: true }
|
|
105
|
+
*
|
|
106
|
+
* Trust model (design §信任模型 #1): same host + same OS user = ONE trust
|
|
107
|
+
* domain. `agentId` here is routing, not authorization — any same-user process
|
|
108
|
+
* can already read every workspace on this daemon. Tokens transit the socket
|
|
109
|
+
* and helper stdout only; nothing lands on disk.
|
|
110
|
+
*
|
|
111
|
+
* Alongside the socket the daemon (re)writes two SECRET-FREE files per boot:
|
|
112
|
+
* the shim `run/git-credential-helper.sh` (pins the current node + CLI path so
|
|
113
|
+
* `.git/config` survives daemon upgrades) and per-agent gitconfig includes for
|
|
114
|
+
* the session-env channel (see workspace/git-injection.ts).
|
|
115
|
+
*/
|
|
116
|
+
function gitcredSocketPath(root) {
|
|
117
|
+
return join(root, "run", "gitcred.sock");
|
|
118
|
+
}
|
|
119
|
+
function gitcredShimPath(root) {
|
|
120
|
+
return join(root, "run", "git-credential-helper.sh");
|
|
121
|
+
}
|
|
122
|
+
var GitCredServer = class {
|
|
123
|
+
cache;
|
|
124
|
+
path;
|
|
125
|
+
log;
|
|
126
|
+
server;
|
|
127
|
+
constructor(cache, path, log) {
|
|
128
|
+
this.cache = cache;
|
|
129
|
+
this.path = path;
|
|
130
|
+
this.log = log;
|
|
131
|
+
}
|
|
132
|
+
start() {
|
|
133
|
+
const dir = dirname(this.path);
|
|
134
|
+
mkdirSync(dir, {
|
|
135
|
+
recursive: true,
|
|
136
|
+
mode: 448
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
chmodSync(dir, 448);
|
|
140
|
+
} catch {}
|
|
141
|
+
rmSync(this.path, { force: true });
|
|
142
|
+
const server = createServer((sock) => this.serve(sock));
|
|
143
|
+
server.listen(this.path, () => {
|
|
144
|
+
try {
|
|
145
|
+
chmodSync(this.path, 384);
|
|
146
|
+
} catch {}
|
|
147
|
+
this.log.info(`gitcred: helper socket at ${this.path}`);
|
|
148
|
+
});
|
|
149
|
+
server.on("error", (e) => this.log.warn(`gitcred: socket error: ${e.message}`));
|
|
150
|
+
this.server = server;
|
|
151
|
+
}
|
|
152
|
+
stop() {
|
|
153
|
+
this.server?.close();
|
|
154
|
+
rmSync(this.path, { force: true });
|
|
155
|
+
}
|
|
156
|
+
serve(sock) {
|
|
157
|
+
let buf = "";
|
|
158
|
+
sock.on("data", (chunk) => {
|
|
159
|
+
buf += chunk.toString("utf8");
|
|
160
|
+
const nl = buf.indexOf("\n");
|
|
161
|
+
if (nl === -1) return;
|
|
162
|
+
const line = buf.slice(0, nl);
|
|
163
|
+
buf = buf.slice(nl + 1);
|
|
164
|
+
this.handle(line, sock);
|
|
165
|
+
});
|
|
166
|
+
sock.on("error", () => sock.destroy());
|
|
167
|
+
}
|
|
168
|
+
async handle(line, sock) {
|
|
169
|
+
const reply = (msg) => {
|
|
170
|
+
sock.write(JSON.stringify(msg) + "\n");
|
|
171
|
+
sock.end();
|
|
172
|
+
};
|
|
173
|
+
let req;
|
|
174
|
+
try {
|
|
175
|
+
req = JSON.parse(line);
|
|
176
|
+
} catch {
|
|
177
|
+
return reply({
|
|
178
|
+
ok: false,
|
|
179
|
+
error: "malformed request"
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (req.op === "erase") {
|
|
183
|
+
this.cache.invalidate(req.agentId, req.password);
|
|
184
|
+
return reply({ ok: true });
|
|
185
|
+
}
|
|
186
|
+
if (req.op !== "get" || typeof req.agentId !== "string") return reply({
|
|
187
|
+
ok: false,
|
|
188
|
+
error: "unsupported op"
|
|
189
|
+
});
|
|
190
|
+
try {
|
|
191
|
+
const cred = await this.cache.get(req.agentId, "helper");
|
|
192
|
+
return reply({
|
|
193
|
+
ok: true,
|
|
194
|
+
username: cred.username,
|
|
195
|
+
password: cred.token
|
|
196
|
+
});
|
|
197
|
+
} catch (e) {
|
|
198
|
+
return reply({
|
|
199
|
+
ok: false,
|
|
200
|
+
error: e instanceof GitCredUnavailableError ? e.message : `git credentials unavailable: ${e.message}`
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* (Re)write the secret-free shim `.git/config` helper lines exec through. The
|
|
207
|
+
* absolute node + CLI paths are re-pinned every daemon boot, so repo configs
|
|
208
|
+
* keep working across upgrades/relocations. Quoted throughout — a home dir
|
|
209
|
+
* with a space (macOS "/Users/John Smith/…") must not word-split.
|
|
210
|
+
*/
|
|
211
|
+
function writeGitcredShim(root, cliEntry) {
|
|
212
|
+
const shim = gitcredShimPath(root);
|
|
213
|
+
mkdirSync(dirname(shim), {
|
|
214
|
+
recursive: true,
|
|
215
|
+
mode: 448
|
|
216
|
+
});
|
|
217
|
+
const q = (v) => `'${v.replaceAll("'", "'\\''")}'`;
|
|
218
|
+
const argv = [q(process.execPath)];
|
|
219
|
+
if (cliEntry.endsWith(".ts")) {
|
|
220
|
+
const req = createRequire(import.meta.url);
|
|
221
|
+
argv.push(q(req.resolve("tsx/cli")));
|
|
222
|
+
}
|
|
223
|
+
argv.push(q(cliEntry));
|
|
224
|
+
writeFileSync(shim, [
|
|
225
|
+
"#!/bin/sh",
|
|
226
|
+
"# agentconnect git credential helper shim — regenerated on daemon start; NO secrets.",
|
|
227
|
+
`AGENTCONNECT_ROOT=${q(root)} \\`,
|
|
228
|
+
` exec ${argv.join(" ")} git-credential "$@"`,
|
|
229
|
+
""
|
|
230
|
+
].join("\n"), { mode: 493 });
|
|
231
|
+
return shim;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
export { GitCredentialCache as i, gitcredSocketPath as n, writeGitcredShim as r, GitCredServer as t };
|
|
235
|
+
|
|
236
|
+
//# sourceMappingURL=gitcred-server-Dcsq6QJe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitcred-server-Dcsq6QJe.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 { 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\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 }) => 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 if (this.denied.has(agentId)) {\n throw new GitCredUnavailableError('control plane denied git credentials for this agent', true)\n }\n const cached = this.entries.get(agentId)\n if (cached && cached.expiresAtMono - this.monoNow() > HANDOUT_MIN_MS) return cached\n\n let pending = this.inflight.get(agentId)\n if (!pending) {\n pending = this.fetch(agentId, reason).finally(() => this.inflight.delete(agentId))\n this.inflight.set(agentId, 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 token with the agent dir. */\n remove(agentId: string): void {\n this.entries.delete(agentId)\n this.denied.delete(agentId)\n }\n\n private async fetch(agentId: string, reason: 'clone' | 'fetch' | 'pull' | 'push' | 'helper'): Promise<Entry> {\n let grant: GitCredGrant\n try {\n grant = await this.deps.request({ agentId, reason })\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(agentId)\n this.entries.delete(agentId)\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(agentId, 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 }\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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"}
|