@victor-software-house/pi-acp 0.7.0 → 0.8.0
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/{client-CTg5Oiz5.mjs → auto-spawn-CQ_aaNZA.mjs} +2 -32
- package/dist/auto-spawn-CQ_aaNZA.mjs.map +1 -0
- package/dist/client-P4T6wITz.mjs +35 -0
- package/dist/client-P4T6wITz.mjs.map +1 -0
- package/dist/{daemon-irIzm1zJ.mjs → daemon-D76_nP59.mjs} +174 -25
- package/dist/daemon-D76_nP59.mjs.map +1 -0
- package/dist/{in-process-DcAV6Sgx.mjs → in-process-Byo-O30j.mjs} +2 -2
- package/dist/{in-process-DcAV6Sgx.mjs.map → in-process-Byo-O30j.mjs.map} +1 -1
- package/dist/index.mjs +9 -3
- package/dist/index.mjs.map +1 -1
- package/dist/operator-DURoHk8w.mjs +85 -0
- package/dist/operator-DURoHk8w.mjs.map +1 -0
- package/dist/{serve-DLukbpF4.mjs → serve-DmuHYqF-.mjs} +3 -3
- package/dist/{serve-DLukbpF4.mjs.map → serve-DmuHYqF-.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/client-CTg5Oiz5.mjs.map +0 -1
- package/dist/daemon-irIzm1zJ.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-CTg5Oiz5.mjs","names":["delay"],"sources":["../src/client/auto-spawn.ts","../src/client/index.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { connect, type Socket } from \"node:net\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport { socketPath } from \"@pi-acp/daemon/socket\";\n\nconst POLL_INTERVAL_MS = 50;\n\nexport async function tryConnect(): Promise<Socket | null> {\n\tconst path = socketPath();\n\treturn await new Promise<Socket | null>((resolve) => {\n\t\tconst sock = connect(path);\n\t\tconst onConnect = (): void => {\n\t\t\tsock.off(\"error\", onError);\n\t\t\tresolve(sock);\n\t\t};\n\t\tconst onError = (): void => {\n\t\t\tsock.off(\"connect\", onConnect);\n\t\t\ttry {\n\t\t\t\tsock.destroy();\n\t\t\t} catch {\n\t\t\t\t/* best-effort */\n\t\t\t}\n\t\t\tresolve(null);\n\t\t};\n\t\tsock.once(\"connect\", onConnect);\n\t\tsock.once(\"error\", onError);\n\t});\n}\n\nexport async function waitForSocket(timeoutMs: number): Promise<Socket | null> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() < deadline) {\n\t\tconst sock = await tryConnect();\n\t\tif (sock) return sock;\n\t\tawait delay(POLL_INTERVAL_MS);\n\t}\n\treturn null;\n}\n\n/**\n * Fork pi-acp --daemon detached so the daemon outlives this process.\n *\n * Note: we resolve the entry-point script via `process.argv[1]` so the same\n * bin / dev entry is reused without the client needing to know its own path.\n */\nexport function autoSpawnDaemon(): void {\n\tconst entry = process.argv[1];\n\tif (entry === undefined) {\n\t\tthrow new Error(\"pi-acp: cannot resolve entry script for daemon spawn\");\n\t}\n\tconst child = spawn(process.execPath, [entry, \"--daemon\"], {\n\t\tdetached: true,\n\t\tstdio: \"ignore\",\n\t\tenv: process.env,\n\t});\n\tchild.unref();\n}\n","/**\n * Thin-client entry point. Connects to (or auto-spawns) the daemon, then\n * forwards stdio in both directions.\n */\n\nimport type { Socket } from \"node:net\";\nimport { autoSpawnDaemon, tryConnect, waitForSocket } from \"@pi-acp/client/auto-spawn\";\n\nconst CONNECT_TIMEOUT_MS = 3000;\n\nexport async function runClient(): Promise<void> {\n\tlet socket: Socket | null = await tryConnect();\n\tif (socket === null) {\n\t\tautoSpawnDaemon();\n\t\tsocket = await waitForSocket(CONNECT_TIMEOUT_MS);\n\t}\n\tif (socket === null) {\n\t\tprocess.stderr.write(\n\t\t\t\"pi-acp: failed to connect to daemon socket within 3s. Try `pi-acp --daemon` manually or set PI_ACP_NO_DAEMON=1.\\n\",\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\t// Wire both pipes synchronously before yielding. The daemon won't send\n\t// frames until it receives an initialize request, so there's no window\n\t// where socket->stdout drops bytes — but don't reorder these.\n\tprocess.stdin.pipe(socket);\n\tsocket.pipe(process.stdout);\n\n\tlet exiting = false;\n\tconst exitOnce = (code: number): void => {\n\t\tif (exiting) return;\n\t\texiting = true;\n\t\tprocess.exit(code);\n\t};\n\n\tsocket.on(\"close\", () => exitOnce(0));\n\tsocket.on(\"error\", (err) => {\n\t\tprocess.stderr.write(`pi-acp: socket error: ${err.message}\\n`);\n\t\texitOnce(1);\n\t});\n\tprocess.on(\"SIGINT\", () => socket?.destroy());\n\tprocess.on(\"SIGTERM\", () => socket?.destroy());\n\tprocess.stdout.on(\"error\", () => exitOnce(0));\n}\n"],"mappings":";;;;;;AAMA,MAAM,mBAAmB;AAEzB,eAAsB,aAAqC;CAC1D,MAAM,OAAO,YAAY;AACzB,QAAO,MAAM,IAAI,SAAwB,YAAY;EACpD,MAAM,OAAO,QAAQ,KAAK;EAC1B,MAAM,kBAAwB;AAC7B,QAAK,IAAI,SAAS,QAAQ;AAC1B,WAAQ,KAAK;;EAEd,MAAM,gBAAsB;AAC3B,QAAK,IAAI,WAAW,UAAU;AAC9B,OAAI;AACH,SAAK,SAAS;WACP;AAGR,WAAQ,KAAK;;AAEd,OAAK,KAAK,WAAW,UAAU;AAC/B,OAAK,KAAK,SAAS,QAAQ;GAC1B;;AAGH,eAAsB,cAAc,WAA2C;CAC9E,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,QAAO,KAAK,KAAK,GAAG,UAAU;EAC7B,MAAM,OAAO,MAAM,YAAY;AAC/B,MAAI,KAAM,QAAO;AACjB,QAAMA,WAAM,iBAAiB;;AAE9B,QAAO;;;;;;;;AASR,SAAgB,kBAAwB;CACvC,MAAM,QAAQ,QAAQ,KAAK;AAC3B,KAAI,UAAU,KAAA,EACb,OAAM,IAAI,MAAM,uDAAuD;AAE1D,OAAM,QAAQ,UAAU,CAAC,OAAO,WAAW,EAAE;EAC1D,UAAU;EACV,OAAO;EACP,KAAK,QAAQ;EACb,CACI,CAAC,OAAO;;;;AChDd,MAAM,qBAAqB;AAE3B,eAAsB,YAA2B;CAChD,IAAI,SAAwB,MAAM,YAAY;AAC9C,KAAI,WAAW,MAAM;AACpB,mBAAiB;AACjB,WAAS,MAAM,cAAc,mBAAmB;;AAEjD,KAAI,WAAW,MAAM;AACpB,UAAQ,OAAO,MACd,oHACA;AACD,UAAQ,KAAK,EAAE;;AAMhB,SAAQ,MAAM,KAAK,OAAO;AAC1B,QAAO,KAAK,QAAQ,OAAO;CAE3B,IAAI,UAAU;CACd,MAAM,YAAY,SAAuB;AACxC,MAAI,QAAS;AACb,YAAU;AACV,UAAQ,KAAK,KAAK;;AAGnB,QAAO,GAAG,eAAe,SAAS,EAAE,CAAC;AACrC,QAAO,GAAG,UAAU,QAAQ;AAC3B,UAAQ,OAAO,MAAM,yBAAyB,IAAI,QAAQ,IAAI;AAC9D,WAAS,EAAE;GACV;AACF,SAAQ,GAAG,gBAAgB,QAAQ,SAAS,CAAC;AAC7C,SAAQ,GAAG,iBAAiB,QAAQ,SAAS,CAAC;AAC9C,SAAQ,OAAO,GAAG,eAAe,SAAS,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-irIzm1zJ.mjs","names":[],"sources":["../src/daemon/session-registry.ts","../src/daemon/context.ts","../src/daemon/index.ts"],"sourcesContent":["/**\n * Daemon-level session registry. Maps sessionId -> live AgentSession plus\n * ownership refcount so that closing a session from one client does NOT\n * dispose the underlying pi runtime if another client also holds it.\n */\n\nimport type { AgentSession } from \"@earendil-works/pi-coding-agent\";\n\nexport interface SessionEntry {\n\tsessionId: string;\n\tpiSession: AgentSession;\n\townerConnectionId: string;\n\talsoHeldBy: Set<string>;\n\tcwd: string;\n\tsessionFile: string | undefined;\n\tupdatedAt: Date;\n}\n\nexport interface SessionRegistry {\n\tregister(entry: NewSessionEntry): void;\n\tattach(sessionId: string, connectionId: string): SessionEntry | undefined;\n\trelease(sessionId: string, connectionId: string): ReleaseResult;\n\tget(sessionId: string): SessionEntry | undefined;\n\tlistAll(): SessionEntry[];\n\tlistOwnedBy(connectionId: string): SessionEntry[];\n}\n\nexport interface NewSessionEntry {\n\tsessionId: string;\n\tpiSession: AgentSession;\n\townerConnectionId: string;\n\tcwd: string;\n\tsessionFile: string | undefined;\n}\n\nexport type ReleaseResult =\n\t| { kind: \"disposed\"; entry: SessionEntry }\n\t| { kind: \"still-held\"; entry: SessionEntry }\n\t| { kind: \"unknown\" };\n\nexport function createSessionRegistry(): SessionRegistry {\n\tconst map = new Map<string, SessionEntry>();\n\n\treturn {\n\t\tregister(input) {\n\t\t\tconst entry: SessionEntry = {\n\t\t\t\tsessionId: input.sessionId,\n\t\t\t\tpiSession: input.piSession,\n\t\t\t\townerConnectionId: input.ownerConnectionId,\n\t\t\t\talsoHeldBy: new Set<string>(),\n\t\t\t\tcwd: input.cwd,\n\t\t\t\tsessionFile: input.sessionFile,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t};\n\t\t\tmap.set(input.sessionId, entry);\n\t\t},\n\n\t\tattach(sessionId, connectionId) {\n\t\t\tconst entry = map.get(sessionId);\n\t\t\tif (entry === undefined) return undefined;\n\t\t\tif (entry.ownerConnectionId !== connectionId) {\n\t\t\t\tentry.alsoHeldBy.add(connectionId);\n\t\t\t\tentry.updatedAt = new Date();\n\t\t\t}\n\t\t\treturn entry;\n\t\t},\n\n\t\trelease(sessionId, connectionId) {\n\t\t\tconst entry = map.get(sessionId);\n\t\t\tif (entry === undefined) return { kind: \"unknown\" };\n\n\t\t\tif (entry.alsoHeldBy.delete(connectionId)) {\n\t\t\t\tentry.updatedAt = new Date();\n\t\t\t\tif (entry.ownerConnectionId === connectionId && entry.alsoHeldBy.size === 0) {\n\t\t\t\t\tmap.delete(sessionId);\n\t\t\t\t\treturn { kind: \"disposed\", entry };\n\t\t\t\t}\n\t\t\t\treturn { kind: \"still-held\", entry };\n\t\t\t}\n\n\t\t\tif (entry.ownerConnectionId === connectionId) {\n\t\t\t\tif (entry.alsoHeldBy.size > 0) {\n\t\t\t\t\t// Hand ownership to one of the still-holders so the entry\n\t\t\t\t\t// keeps a coherent owner record. Pick first by iteration.\n\t\t\t\t\tconst next = entry.alsoHeldBy.values().next().value;\n\t\t\t\t\tif (next !== undefined) {\n\t\t\t\t\t\tentry.alsoHeldBy.delete(next);\n\t\t\t\t\t\tentry.ownerConnectionId = next;\n\t\t\t\t\t\tentry.updatedAt = new Date();\n\t\t\t\t\t\treturn { kind: \"still-held\", entry };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmap.delete(sessionId);\n\t\t\t\treturn { kind: \"disposed\", entry };\n\t\t\t}\n\n\t\t\treturn { kind: \"still-held\", entry };\n\t\t},\n\n\t\tget(sessionId) {\n\t\t\treturn map.get(sessionId);\n\t\t},\n\n\t\tlistAll() {\n\t\t\treturn Array.from(map.values());\n\t\t},\n\n\t\tlistOwnedBy(connectionId) {\n\t\t\treturn Array.from(map.values()).filter(\n\t\t\t\t(e) => e.ownerConnectionId === connectionId || e.alsoHeldBy.has(connectionId),\n\t\t\t);\n\t\t},\n\t};\n}\n","/**\n * Daemon-level shared state injected into per-connection PiAcpAgent instances.\n *\n * Phase 1 landed the interface + stub IdleTracker.\n * Phase 2 wires the real SessionRegistry.\n * Phase 3 will replace IdleTracker.\n */\n\nimport { createSessionRegistry, type SessionRegistry } from \"@pi-acp/daemon/session-registry\";\n\nexport interface DaemonContext {\n\t/** Cross-window session registry. PRD-003 FR-5. */\n\tsessionRegistry: SessionRegistry;\n\t/** Idle-shutdown tracker. Stub in Phase 1-2; real in Phase 3. */\n\tidleTracker: IdleTracker;\n}\n\n/** Phase-3 stub. Replaced when idle shutdown lands. */\nexport interface IdleTracker {\n\tbump(delta: 1 | -1): void;\n\tdispose(): void;\n}\n\nexport type { SessionEntry, SessionRegistry } from \"@pi-acp/daemon/session-registry\";\nexport { createSessionRegistry } from \"@pi-acp/daemon/session-registry\";\n\nexport function createNoopIdleTracker(): IdleTracker {\n\treturn {\n\t\tbump() {\n\t\t\t/* phase 3 wires this */\n\t\t},\n\t\tdispose() {\n\t\t\t/* phase 3 wires this */\n\t\t},\n\t};\n}\n\nexport function createDaemonContext(): DaemonContext {\n\treturn {\n\t\tsessionRegistry: createSessionRegistry(),\n\t\tidleTracker: createNoopIdleTracker(),\n\t};\n}\n","/**\n * Daemon entry point. Invoked when pi-acp is launched with `--daemon`.\n *\n * Lifecycle:\n * 1. Acquire per-UID lockfile (refuses if another daemon alive).\n * 2. Remove stale socket file if any (left by a dead prior daemon).\n * 3. Construct DaemonContext shared singletons (Phase 1: stubs).\n * 4. Bind socket; accept loop spawns a per-connection serveAcp instance.\n * 5. SIGINT / SIGTERM → graceful shutdown.\n */\n\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport { createDaemonContext, type DaemonContext } from \"@pi-acp/daemon/context\";\nimport {\n\tacquireLock,\n\tensureSocketParentDir,\n\treleaseLock,\n\tremoveStaleSocketIfAny,\n\tsocketPath,\n} from \"@pi-acp/daemon/socket\";\nimport { type ServeHandle, serveAcp } from \"@pi-acp/runtime/serve\";\n\ninterface Connection {\n\tsocket: Socket;\n\thandle: ServeHandle;\n}\n\nexport async function runDaemon(): Promise<void> {\n\tconst lockResult = acquireLock();\n\tif (!lockResult.ok) {\n\t\tprocess.stderr.write(\n\t\t\t`pi-acp daemon: already running (pid ${lockResult.heldByPid ?? \"unknown\"})\\n`,\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tensureSocketParentDir();\n\tremoveStaleSocketIfAny();\n\n\tconst ctx: DaemonContext = createDaemonContext();\n\tconst connections = new Set<Connection>();\n\tlet shuttingDown = false;\n\n\tconst server: Server = createServer((socket) => {\n\t\tif (shuttingDown) {\n\t\t\tsocket.destroy();\n\t\t\treturn;\n\t\t}\n\t\tconst handle = serveAcp({ input: socket, output: socket, daemonContext: ctx });\n\t\tconst entry: Connection = { socket, handle };\n\t\tconnections.add(entry);\n\t\tctx.idleTracker.bump(1);\n\n\t\tconst cleanup = (): void => {\n\t\t\tif (!connections.delete(entry)) return;\n\t\t\ttry {\n\t\t\t\thandle.dispose();\n\t\t\t} catch {\n\t\t\t\t/* best-effort */\n\t\t\t}\n\t\t\tctx.idleTracker.bump(-1);\n\t\t};\n\n\t\tsocket.on(\"close\", cleanup);\n\t\tsocket.on(\"error\", cleanup);\n\t});\n\n\tserver.on(\"error\", (err) => {\n\t\tprocess.stderr.write(`pi-acp daemon: server error: ${err.message}\\n`);\n\t});\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tconst path = socketPath();\n\t\tserver.listen(path, () => resolve());\n\t\tserver.once(\"error\", reject);\n\t});\n\n\tif (process.env[\"PI_ACP_DAEMON_DEBUG\"] === \"1\") {\n\t\tprocess.stderr.write(`pi-acp daemon: listening on ${socketPath()} (pid ${process.pid})\\n`);\n\t}\n\n\tconst shutdown = async (): Promise<void> => {\n\t\tif (shuttingDown) return;\n\t\tshuttingDown = true;\n\t\tserver.close();\n\t\tfor (const entry of connections) {\n\t\t\ttry {\n\t\t\t\tentry.handle.dispose();\n\t\t\t} catch {\n\t\t\t\t/* best-effort */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tentry.socket.destroy();\n\t\t\t} catch {\n\t\t\t\t/* best-effort */\n\t\t\t}\n\t\t}\n\t\tconnections.clear();\n\t\tctx.idleTracker.dispose();\n\t\tremoveStaleSocketIfAny();\n\t\treleaseLock();\n\t\tprocess.exit(0);\n\t};\n\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid shutdown();\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid shutdown();\n\t});\n}\n"],"mappings":"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"}
|