@ponythewhite/base-context 1.0.9 → 1.0.10

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/base-context-runtime/pyproject.toml +1 -1
  3. package/dist/base-context-runtime/uv.lock +1 -1
  4. package/dist/build-info.json +1 -1
  5. package/dist/bundle/{bundled-modules-5JRM6P4T.js → bundled-modules-MX6YNAM2.js} +2 -2
  6. package/dist/bundle/{chunk-NEHLB7JV.js → chunk-CDXQNS5P.js} +100 -22
  7. package/dist/bundle/{chunk-VVI3UKGR.js → chunk-UHLGPYMI.js} +20 -40
  8. package/dist/bundle/{cli-main-YUDBEYE2.js → cli-main-D7W3WWCV.js} +2 -2
  9. package/dist/bundle/cli.js +1 -1
  10. package/dist/bundle/{main-ZLGGAAGU.js → main-5J3QBZ5Y.js} +2 -2
  11. package/dist/core/canonical-context.d.ts.map +1 -1
  12. package/dist/core/canonical-context.js +115 -30
  13. package/dist/core/canonical-context.js.map +1 -1
  14. package/dist/core/context-epoch.d.ts +14 -3
  15. package/dist/core/context-epoch.d.ts.map +1 -1
  16. package/dist/core/context-epoch.js +5 -1
  17. package/dist/core/context-epoch.js.map +1 -1
  18. package/dist/core/public-context.d.ts.map +1 -1
  19. package/dist/core/public-context.js +20 -8
  20. package/dist/core/public-context.js.map +1 -1
  21. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  22. package/dist/modes/daemon/daemon-mode.js +15 -30
  23. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  24. package/dist/modes/daemon/daemon-socket.d.ts +5 -0
  25. package/dist/modes/daemon/daemon-socket.d.ts.map +1 -1
  26. package/dist/modes/daemon/daemon-socket.js +19 -0
  27. package/dist/modes/daemon/daemon-socket.js.map +1 -1
  28. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  29. package/dist/modes/daemon/daemon-supervisor.js +6 -7
  30. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  31. package/dist/modes/daemon/daemon-worker-client.d.ts.map +1 -1
  32. package/dist/modes/daemon/daemon-worker-client.js +5 -5
  33. package/dist/modes/daemon/daemon-worker-client.js.map +1 -1
  34. package/package.json +4 -4
@@ -1,3 +1,4 @@
1
+ import { type Server, type Socket } from "node:net";
1
2
  export { normalizeSocketPath } from "../../utils/daemon-socket-path.js";
2
3
  type DaemonSocketCompromiseListener = (error: Error) => void;
3
4
  export declare class DaemonSocketPathLease {
@@ -13,6 +14,10 @@ export declare class DaemonSocketPathLease {
13
14
  release(): Promise<void>;
14
15
  private notifyCompromiseListener;
15
16
  }
17
+ export declare function closeDaemonServer(server: Server | undefined, clients: Iterable<{
18
+ socket: Socket;
19
+ detachInput(): void;
20
+ }>): Promise<void>;
16
21
  export interface DaemonSocketIdentity {
17
22
  dev: number;
18
23
  ino: number;
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-socket.d.ts","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-socket.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AASxE,KAAK,8BAA8B,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAE7D,qBAAa,qBAAqB;IAMhC,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAN7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAC,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6C;IAEjF,YACU,UAAU,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC9C;IAEJ,IAAI,UAAU,IAAI,KAAK,GAAG,SAAS,CAElC;IAED,aAAa,CAAC,QAAQ,EAAE,8BAA8B,GAAG,MAAM,IAAI,CAOlE;IAED,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAMnC;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7B;IAED,OAAO,CAAC,wBAAwB;CAOhC;AAED,MAAM,WAAW,oBAAoB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,uBAAuB,CAAC,UAAU,SAAkB,GAAG,MAAM,CAK5E;AAED,wBAAsB,4BAA4B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAyBjH;AAED,wBAAsB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB9G;AAmDD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAKjE;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAM5F;AAED,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,MAAM,EAClB,gBAAgB,CAAC,EAAE,oBAAoB,EACvC,KAAK,CAAC,EAAE,qBAAqB,GAC3B,IAAI,CAkDN;AAmCD,wBAAgB,sBAAsB,CAAC,UAAU,SAAkB,GAAG,MAAM,CAG3E","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, lstatSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { createConnection } from \"node:net\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir, getPackageDir } from \"../../config.js\";\nimport { PRODUCT } from \"../../product-identity.js\";\n\nexport { normalizeSocketPath } from \"../../utils/daemon-socket-path.js\";\n\nconst DAEMON_SOCKET_MODE = 0o600;\nconst DAEMON_SOCKET_DIR_MODE = 0o700;\nconst DAEMON_SOCKET_RELEASE_GRACE_MS = 1000;\nconst DAEMON_SOCKET_RELEASE_POLL_MS = 25;\nconst DAEMON_SOCKET_LOCK_STALE_MS = 5000;\nconst DAEMON_SOCKET_LOCK_UPDATE_MS = 1000;\n\ntype DaemonSocketCompromiseListener = (error: Error) => void;\n\nexport class DaemonSocketPathLease {\n\tprivate released = false;\n\tprivate compromisedError?: Error;\n\tprivate readonly compromiseListeners = new Set<DaemonSocketCompromiseListener>();\n\n\tconstructor(\n\t\treadonly socketPath: string,\n\t\tprivate readonly releaseLock: () => Promise<void>,\n\t) {}\n\n\tget compromise(): Error | undefined {\n\t\treturn this.compromisedError;\n\t}\n\n\tonCompromised(listener: DaemonSocketCompromiseListener): () => void {\n\t\tif (this.compromisedError) {\n\t\t\tthis.notifyCompromiseListener(listener, this.compromisedError);\n\t\t\treturn () => {};\n\t\t}\n\t\tthis.compromiseListeners.add(listener);\n\t\treturn () => this.compromiseListeners.delete(listener);\n\t}\n\n\trecordCompromise(error: Error): void {\n\t\tif (this.compromisedError) return;\n\t\tthis.compromisedError = error;\n\t\tconst listeners = [...this.compromiseListeners];\n\t\tthis.compromiseListeners.clear();\n\t\tfor (const listener of listeners) this.notifyCompromiseListener(listener, error);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (this.released) return;\n\t\tthis.released = true;\n\t\tthis.compromiseListeners.clear();\n\t\tawait this.releaseLock();\n\t}\n\n\tprivate notifyCompromiseListener(listener: DaemonSocketCompromiseListener, error: Error): void {\n\t\ttry {\n\t\t\tlistener(error);\n\t\t} catch {\n\t\t\t// A lease callback must not rethrow from proper-lockfile's refresh callback.\n\t\t}\n\t}\n}\n\nexport interface DaemonSocketIdentity {\n\tdev: number;\n\tino: number;\n}\n\nexport function defaultDaemonSocketPath(packageDir = getPackageDir()): string {\n\tif (process.platform === \"win32\") {\n\t\treturn `\\\\\\\\.\\\\pipe\\\\${PRODUCT.command}-${daemonScopeId(packageDir)}`;\n\t}\n\treturn join(defaultDaemonSocketDir(packageDir), \"daemon.sock\");\n}\n\nexport async function acquireDaemonSocketPathLease(socketPath: string): Promise<DaemonSocketPathLease | undefined> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tlet lease: DaemonSocketPathLease | undefined;\n\tlet pendingCompromise: Error | undefined;\n\tconst releaseLock = await lockfile.lock(socketPath, {\n\t\trealpath: false,\n\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\tonCompromised: (error) => {\n\t\t\tif (lease) lease.recordCompromise(error);\n\t\t\telse pendingCompromise = error;\n\t\t},\n\t\tretries: {\n\t\t\tretries: 600,\n\t\t\tfactor: 1,\n\t\t\tminTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t\tmaxTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t},\n\t});\n\tlease = new DaemonSocketPathLease(socketPath, releaseLock);\n\tif (pendingCompromise) lease.recordCompromise(pendingCompromise);\n\treturn lease;\n}\n\nexport async function prepareDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tassertSocketLeaseHeld(socketPath, lease);\n\t\tawait prepareUnixDaemonSocketPath(socketPath, lease);\n\t\treturn;\n\t}\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst ownedLease = await acquireDaemonSocketPathLease(socketPath);\n\ttry {\n\t\tawait prepareUnixDaemonSocketPath(socketPath, ownedLease);\n\t} finally {\n\t\tawait ownedLease?.release();\n\t}\n}\n\nasync function prepareUnixDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\n\tlet stat: ReturnType<typeof lstatSync>;\n\ttry {\n\t\tstat = lstatSync(socketPath);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tif (!stat.isSocket()) {\n\t\tthrow new Error(`Daemon socket path exists and is not a socket: ${socketPath}`);\n\t}\n\n\tconst staleIdentity: DaemonSocketIdentity = { dev: stat.dev, ino: stat.ino };\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst deadline = Date.now() + DAEMON_SOCKET_RELEASE_GRACE_MS;\n\twhile (Date.now() < deadline) {\n\t\tawait delay(DAEMON_SOCKET_RELEASE_POLL_MS);\n\t\tif (!existsSync(socketPath)) {\n\t\t\treturn;\n\t\t}\n\t\tlet currentIdentity: DaemonSocketIdentity | undefined;\n\t\ttry {\n\t\t\tcurrentIdentity = getDaemonSocketIdentity(socketPath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tif (!currentIdentity || currentIdentity.dev !== staleIdentity.dev || currentIdentity.ino !== staleIdentity.ino) {\n\t\t\tthrow new Error(`Daemon socket changed ownership while waiting for cleanup: ${socketPath}`);\n\t\t}\n\t\tif (await canConnectToUnixSocket(socketPath)) {\n\t\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t\t}\n\t}\n\n\tif (lease) assertSocketLeaseHeld(socketPath, lease);\n\tunlinkSync(socketPath);\n}\n\nexport function restrictDaemonSocketPath(socketPath: string): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tchmodSync(socketPath, DAEMON_SOCKET_MODE);\n}\n\nexport function getDaemonSocketIdentity(socketPath: string): DaemonSocketIdentity | undefined {\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tconst stat = lstatSync(socketPath);\n\treturn { dev: stat.dev, ino: stat.ino };\n}\n\nexport function cleanupDaemonSocketPath(\n\tsocketPath: string,\n\texpectedIdentity?: DaemonSocketIdentity,\n\tlease?: DaemonSocketPathLease,\n): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tif (lease.compromise) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t\t} catch {\n\t\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t\t}\n\t\treturn;\n\t}\n\tlet releaseLock: (() => void) | undefined;\n\tlet lockCompromised = false;\n\ttry {\n\t\treleaseLock = lockfile.lockSync(socketPath, {\n\t\t\trealpath: false,\n\t\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\t\tonCompromised: () => {\n\t\t\t\tlockCompromised = true;\n\t\t\t},\n\t\t\tretries: 0,\n\t\t});\n\t} catch {\n\t\treturn;\n\t}\n\tif (lockCompromised) {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort release.\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t} catch {\n\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t} finally {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort cleanup; a failed release is recoverable as a stale lock.\n\t\t}\n\t}\n}\n\nfunction cleanupUnixDaemonSocketPath(socketPath: string, expectedIdentity?: DaemonSocketIdentity): void {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (expectedIdentity) {\n\t\tconst currentIdentity = getDaemonSocketIdentity(socketPath);\n\t\tif (\n\t\t\t!currentIdentity ||\n\t\t\tcurrentIdentity.dev !== expectedIdentity.dev ||\n\t\t\tcurrentIdentity.ino !== expectedIdentity.ino\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t}\n\tunlinkSync(socketPath);\n}\n\nfunction assertSocketLease(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.socketPath !== socketPath) {\n\t\tthrow new Error(`Daemon socket lease does not match ${socketPath}`);\n\t}\n}\n\nfunction assertSocketLeaseHeld(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.compromise) {\n\t\tthrow new Error(`Daemon socket lease for ${socketPath} was compromised: ${lease.compromise.message}`);\n\t}\n}\n\nfunction daemonScopeId(packageDir: string): string {\n\treturn createHash(\"sha256\").update(`${getAgentDir()}\\0${packageDir}`).digest(\"hex\").slice(0, 12);\n}\n\nexport function defaultDaemonSocketDir(packageDir = getPackageDir()): string {\n\tconst suffix = typeof process.getuid === \"function\" ? String(process.getuid()) : \"user\";\n\treturn join(tmpdir(), `bc-${suffix}-${daemonScopeId(packageDir).slice(0, 8)}`);\n}\n\nfunction ensureDefaultDaemonSocketDir(socketPath: string): void {\n\tif (process.platform === \"win32\" || dirname(socketPath) !== defaultDaemonSocketDir()) {\n\t\treturn;\n\t}\n\n\tif (!existsSync(defaultDaemonSocketDir())) {\n\t\tmkdirSync(defaultDaemonSocketDir(), { recursive: true, mode: DAEMON_SOCKET_DIR_MODE });\n\t}\n\n\tconst stat = lstatSync(defaultDaemonSocketDir());\n\tif (!stat.isDirectory()) {\n\t\tthrow new Error(`Daemon socket directory exists and is not a directory: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tif (typeof process.getuid === \"function\" && stat.uid !== process.getuid()) {\n\t\tthrow new Error(`Daemon socket directory is not owned by the current user: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tchmodSync(defaultDaemonSocketDir(), DAEMON_SOCKET_DIR_MODE);\n}\n\nfunction canConnectToUnixSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolveConnect) => {\n\t\tconst socket = createConnection(socketPath);\n\t\tlet settled = false;\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n\t\tconst finish = (canConnect: boolean) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tif (timeoutId) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\tsocket.removeAllListeners();\n\t\t\tsocket.destroy();\n\t\t\tresolveConnect(canConnect);\n\t\t};\n\n\t\ttimeoutId = setTimeout(() => finish(false), 250);\n\t\tsocket.once(\"connect\", () => finish(true));\n\t\tsocket.once(\"error\", () => finish(false));\n\t});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}
1
+ {"version":3,"file":"daemon-socket.d.ts","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-socket.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAOtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AASxE,KAAK,8BAA8B,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAE7D,qBAAa,qBAAqB;IAMhC,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAN7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAC,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6C;IAEjF,YACU,UAAU,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC9C;IAEJ,IAAI,UAAU,IAAI,KAAK,GAAG,SAAS,CAElC;IAED,aAAa,CAAC,QAAQ,EAAE,8BAA8B,GAAG,MAAM,IAAI,CAOlE;IAED,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAMnC;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7B;IAED,OAAO,CAAC,wBAAwB;CAOhC;AAED,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,IAAI,IAAI,CAAA;CAAE,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,MAAM,WAAW,oBAAoB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,uBAAuB,CAAC,UAAU,SAAkB,GAAG,MAAM,CAK5E;AAED,wBAAsB,4BAA4B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAyBjH;AAED,wBAAsB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB9G;AAmDD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAKjE;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAM5F;AAED,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,MAAM,EAClB,gBAAgB,CAAC,EAAE,oBAAoB,EACvC,KAAK,CAAC,EAAE,qBAAqB,GAC3B,IAAI,CAkDN;AAmCD,wBAAgB,sBAAsB,CAAC,UAAU,SAAkB,GAAG,MAAM,CAG3E","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, lstatSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { createConnection, type Server, type Socket } from \"node:net\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir, getPackageDir } from \"../../config.js\";\nimport { PRODUCT } from \"../../product-identity.js\";\n\nexport { normalizeSocketPath } from \"../../utils/daemon-socket-path.js\";\n\nconst DAEMON_SOCKET_MODE = 0o600;\nconst DAEMON_SOCKET_DIR_MODE = 0o700;\nconst DAEMON_SOCKET_RELEASE_GRACE_MS = 1000;\nconst DAEMON_SOCKET_RELEASE_POLL_MS = 25;\nconst DAEMON_SOCKET_LOCK_STALE_MS = 5000;\nconst DAEMON_SOCKET_LOCK_UPDATE_MS = 1000;\n\ntype DaemonSocketCompromiseListener = (error: Error) => void;\n\nexport class DaemonSocketPathLease {\n\tprivate released = false;\n\tprivate compromisedError?: Error;\n\tprivate readonly compromiseListeners = new Set<DaemonSocketCompromiseListener>();\n\n\tconstructor(\n\t\treadonly socketPath: string,\n\t\tprivate readonly releaseLock: () => Promise<void>,\n\t) {}\n\n\tget compromise(): Error | undefined {\n\t\treturn this.compromisedError;\n\t}\n\n\tonCompromised(listener: DaemonSocketCompromiseListener): () => void {\n\t\tif (this.compromisedError) {\n\t\t\tthis.notifyCompromiseListener(listener, this.compromisedError);\n\t\t\treturn () => {};\n\t\t}\n\t\tthis.compromiseListeners.add(listener);\n\t\treturn () => this.compromiseListeners.delete(listener);\n\t}\n\n\trecordCompromise(error: Error): void {\n\t\tif (this.compromisedError) return;\n\t\tthis.compromisedError = error;\n\t\tconst listeners = [...this.compromiseListeners];\n\t\tthis.compromiseListeners.clear();\n\t\tfor (const listener of listeners) this.notifyCompromiseListener(listener, error);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (this.released) return;\n\t\tthis.released = true;\n\t\tthis.compromiseListeners.clear();\n\t\tawait this.releaseLock();\n\t}\n\n\tprivate notifyCompromiseListener(listener: DaemonSocketCompromiseListener, error: Error): void {\n\t\ttry {\n\t\t\tlistener(error);\n\t\t} catch {\n\t\t\t// A lease callback must not rethrow from proper-lockfile's refresh callback.\n\t\t}\n\t}\n}\n\nexport async function closeDaemonServer(\n\tserver: Server | undefined,\n\tclients: Iterable<{ socket: Socket; detachInput(): void }>,\n): Promise<void> {\n\tconst closingClients = [...clients];\n\tfor (const client of closingClients) {\n\t\tclient.detachInput();\n\t\tclient.socket.end();\n\t}\n\t// Allow final replies to flush, but do not wait indefinitely for a peer's FIN.\n\tconst timeout = setTimeout(() => {\n\t\tfor (const client of closingClients) client.socket.destroy();\n\t}, 1000);\n\ttimeout.unref();\n\ttry {\n\t\tawait new Promise<void>((resolveClose) => server?.close(() => resolveClose()) ?? resolveClose());\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\n\nexport interface DaemonSocketIdentity {\n\tdev: number;\n\tino: number;\n}\n\nexport function defaultDaemonSocketPath(packageDir = getPackageDir()): string {\n\tif (process.platform === \"win32\") {\n\t\treturn `\\\\\\\\.\\\\pipe\\\\${PRODUCT.command}-${daemonScopeId(packageDir)}`;\n\t}\n\treturn join(defaultDaemonSocketDir(packageDir), \"daemon.sock\");\n}\n\nexport async function acquireDaemonSocketPathLease(socketPath: string): Promise<DaemonSocketPathLease | undefined> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tlet lease: DaemonSocketPathLease | undefined;\n\tlet pendingCompromise: Error | undefined;\n\tconst releaseLock = await lockfile.lock(socketPath, {\n\t\trealpath: false,\n\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\tonCompromised: (error) => {\n\t\t\tif (lease) lease.recordCompromise(error);\n\t\t\telse pendingCompromise = error;\n\t\t},\n\t\tretries: {\n\t\t\tretries: 600,\n\t\t\tfactor: 1,\n\t\t\tminTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t\tmaxTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t},\n\t});\n\tlease = new DaemonSocketPathLease(socketPath, releaseLock);\n\tif (pendingCompromise) lease.recordCompromise(pendingCompromise);\n\treturn lease;\n}\n\nexport async function prepareDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tassertSocketLeaseHeld(socketPath, lease);\n\t\tawait prepareUnixDaemonSocketPath(socketPath, lease);\n\t\treturn;\n\t}\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst ownedLease = await acquireDaemonSocketPathLease(socketPath);\n\ttry {\n\t\tawait prepareUnixDaemonSocketPath(socketPath, ownedLease);\n\t} finally {\n\t\tawait ownedLease?.release();\n\t}\n}\n\nasync function prepareUnixDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\n\tlet stat: ReturnType<typeof lstatSync>;\n\ttry {\n\t\tstat = lstatSync(socketPath);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tif (!stat.isSocket()) {\n\t\tthrow new Error(`Daemon socket path exists and is not a socket: ${socketPath}`);\n\t}\n\n\tconst staleIdentity: DaemonSocketIdentity = { dev: stat.dev, ino: stat.ino };\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst deadline = Date.now() + DAEMON_SOCKET_RELEASE_GRACE_MS;\n\twhile (Date.now() < deadline) {\n\t\tawait delay(DAEMON_SOCKET_RELEASE_POLL_MS);\n\t\tif (!existsSync(socketPath)) {\n\t\t\treturn;\n\t\t}\n\t\tlet currentIdentity: DaemonSocketIdentity | undefined;\n\t\ttry {\n\t\t\tcurrentIdentity = getDaemonSocketIdentity(socketPath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tif (!currentIdentity || currentIdentity.dev !== staleIdentity.dev || currentIdentity.ino !== staleIdentity.ino) {\n\t\t\tthrow new Error(`Daemon socket changed ownership while waiting for cleanup: ${socketPath}`);\n\t\t}\n\t\tif (await canConnectToUnixSocket(socketPath)) {\n\t\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t\t}\n\t}\n\n\tif (lease) assertSocketLeaseHeld(socketPath, lease);\n\tunlinkSync(socketPath);\n}\n\nexport function restrictDaemonSocketPath(socketPath: string): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tchmodSync(socketPath, DAEMON_SOCKET_MODE);\n}\n\nexport function getDaemonSocketIdentity(socketPath: string): DaemonSocketIdentity | undefined {\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tconst stat = lstatSync(socketPath);\n\treturn { dev: stat.dev, ino: stat.ino };\n}\n\nexport function cleanupDaemonSocketPath(\n\tsocketPath: string,\n\texpectedIdentity?: DaemonSocketIdentity,\n\tlease?: DaemonSocketPathLease,\n): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tif (lease.compromise) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t\t} catch {\n\t\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t\t}\n\t\treturn;\n\t}\n\tlet releaseLock: (() => void) | undefined;\n\tlet lockCompromised = false;\n\ttry {\n\t\treleaseLock = lockfile.lockSync(socketPath, {\n\t\t\trealpath: false,\n\t\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\t\tonCompromised: () => {\n\t\t\t\tlockCompromised = true;\n\t\t\t},\n\t\t\tretries: 0,\n\t\t});\n\t} catch {\n\t\treturn;\n\t}\n\tif (lockCompromised) {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort release.\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t} catch {\n\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t} finally {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort cleanup; a failed release is recoverable as a stale lock.\n\t\t}\n\t}\n}\n\nfunction cleanupUnixDaemonSocketPath(socketPath: string, expectedIdentity?: DaemonSocketIdentity): void {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (expectedIdentity) {\n\t\tconst currentIdentity = getDaemonSocketIdentity(socketPath);\n\t\tif (\n\t\t\t!currentIdentity ||\n\t\t\tcurrentIdentity.dev !== expectedIdentity.dev ||\n\t\t\tcurrentIdentity.ino !== expectedIdentity.ino\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t}\n\tunlinkSync(socketPath);\n}\n\nfunction assertSocketLease(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.socketPath !== socketPath) {\n\t\tthrow new Error(`Daemon socket lease does not match ${socketPath}`);\n\t}\n}\n\nfunction assertSocketLeaseHeld(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.compromise) {\n\t\tthrow new Error(`Daemon socket lease for ${socketPath} was compromised: ${lease.compromise.message}`);\n\t}\n}\n\nfunction daemonScopeId(packageDir: string): string {\n\treturn createHash(\"sha256\").update(`${getAgentDir()}\\0${packageDir}`).digest(\"hex\").slice(0, 12);\n}\n\nexport function defaultDaemonSocketDir(packageDir = getPackageDir()): string {\n\tconst suffix = typeof process.getuid === \"function\" ? String(process.getuid()) : \"user\";\n\treturn join(tmpdir(), `bc-${suffix}-${daemonScopeId(packageDir).slice(0, 8)}`);\n}\n\nfunction ensureDefaultDaemonSocketDir(socketPath: string): void {\n\tif (process.platform === \"win32\" || dirname(socketPath) !== defaultDaemonSocketDir()) {\n\t\treturn;\n\t}\n\n\tif (!existsSync(defaultDaemonSocketDir())) {\n\t\tmkdirSync(defaultDaemonSocketDir(), { recursive: true, mode: DAEMON_SOCKET_DIR_MODE });\n\t}\n\n\tconst stat = lstatSync(defaultDaemonSocketDir());\n\tif (!stat.isDirectory()) {\n\t\tthrow new Error(`Daemon socket directory exists and is not a directory: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tif (typeof process.getuid === \"function\" && stat.uid !== process.getuid()) {\n\t\tthrow new Error(`Daemon socket directory is not owned by the current user: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tchmodSync(defaultDaemonSocketDir(), DAEMON_SOCKET_DIR_MODE);\n}\n\nfunction canConnectToUnixSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolveConnect) => {\n\t\tconst socket = createConnection(socketPath);\n\t\tlet settled = false;\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n\t\tconst finish = (canConnect: boolean) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tif (timeoutId) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\tsocket.removeAllListeners();\n\t\t\tsocket.destroy();\n\t\t\tresolveConnect(canConnect);\n\t\t};\n\n\t\ttimeoutId = setTimeout(() => finish(false), 250);\n\t\tsocket.once(\"connect\", () => finish(true));\n\t\tsocket.once(\"error\", () => finish(false));\n\t});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}
@@ -59,6 +59,25 @@ export class DaemonSocketPathLease {
59
59
  }
60
60
  }
61
61
  }
62
+ export async function closeDaemonServer(server, clients) {
63
+ const closingClients = [...clients];
64
+ for (const client of closingClients) {
65
+ client.detachInput();
66
+ client.socket.end();
67
+ }
68
+ // Allow final replies to flush, but do not wait indefinitely for a peer's FIN.
69
+ const timeout = setTimeout(() => {
70
+ for (const client of closingClients)
71
+ client.socket.destroy();
72
+ }, 1000);
73
+ timeout.unref();
74
+ try {
75
+ await new Promise((resolveClose) => server?.close(() => resolveClose()) ?? resolveClose());
76
+ }
77
+ finally {
78
+ clearTimeout(timeout);
79
+ }
80
+ }
62
81
  export function defaultDaemonSocketPath(packageDir = getPackageDir()) {
63
82
  if (process.platform === "win32") {
64
83
  return `\\\\.\\pipe\\${PRODUCT.command}-${daemonScopeId(packageDir)}`;
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-socket.js","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-socket.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, lstatSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { createConnection } from \"node:net\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir, getPackageDir } from \"../../config.js\";\nimport { PRODUCT } from \"../../product-identity.js\";\n\nexport { normalizeSocketPath } from \"../../utils/daemon-socket-path.js\";\n\nconst DAEMON_SOCKET_MODE = 0o600;\nconst DAEMON_SOCKET_DIR_MODE = 0o700;\nconst DAEMON_SOCKET_RELEASE_GRACE_MS = 1000;\nconst DAEMON_SOCKET_RELEASE_POLL_MS = 25;\nconst DAEMON_SOCKET_LOCK_STALE_MS = 5000;\nconst DAEMON_SOCKET_LOCK_UPDATE_MS = 1000;\n\ntype DaemonSocketCompromiseListener = (error: Error) => void;\n\nexport class DaemonSocketPathLease {\n\tprivate released = false;\n\tprivate compromisedError?: Error;\n\tprivate readonly compromiseListeners = new Set<DaemonSocketCompromiseListener>();\n\n\tconstructor(\n\t\treadonly socketPath: string,\n\t\tprivate readonly releaseLock: () => Promise<void>,\n\t) {}\n\n\tget compromise(): Error | undefined {\n\t\treturn this.compromisedError;\n\t}\n\n\tonCompromised(listener: DaemonSocketCompromiseListener): () => void {\n\t\tif (this.compromisedError) {\n\t\t\tthis.notifyCompromiseListener(listener, this.compromisedError);\n\t\t\treturn () => {};\n\t\t}\n\t\tthis.compromiseListeners.add(listener);\n\t\treturn () => this.compromiseListeners.delete(listener);\n\t}\n\n\trecordCompromise(error: Error): void {\n\t\tif (this.compromisedError) return;\n\t\tthis.compromisedError = error;\n\t\tconst listeners = [...this.compromiseListeners];\n\t\tthis.compromiseListeners.clear();\n\t\tfor (const listener of listeners) this.notifyCompromiseListener(listener, error);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (this.released) return;\n\t\tthis.released = true;\n\t\tthis.compromiseListeners.clear();\n\t\tawait this.releaseLock();\n\t}\n\n\tprivate notifyCompromiseListener(listener: DaemonSocketCompromiseListener, error: Error): void {\n\t\ttry {\n\t\t\tlistener(error);\n\t\t} catch {\n\t\t\t// A lease callback must not rethrow from proper-lockfile's refresh callback.\n\t\t}\n\t}\n}\n\nexport interface DaemonSocketIdentity {\n\tdev: number;\n\tino: number;\n}\n\nexport function defaultDaemonSocketPath(packageDir = getPackageDir()): string {\n\tif (process.platform === \"win32\") {\n\t\treturn `\\\\\\\\.\\\\pipe\\\\${PRODUCT.command}-${daemonScopeId(packageDir)}`;\n\t}\n\treturn join(defaultDaemonSocketDir(packageDir), \"daemon.sock\");\n}\n\nexport async function acquireDaemonSocketPathLease(socketPath: string): Promise<DaemonSocketPathLease | undefined> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tlet lease: DaemonSocketPathLease | undefined;\n\tlet pendingCompromise: Error | undefined;\n\tconst releaseLock = await lockfile.lock(socketPath, {\n\t\trealpath: false,\n\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\tonCompromised: (error) => {\n\t\t\tif (lease) lease.recordCompromise(error);\n\t\t\telse pendingCompromise = error;\n\t\t},\n\t\tretries: {\n\t\t\tretries: 600,\n\t\t\tfactor: 1,\n\t\t\tminTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t\tmaxTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t},\n\t});\n\tlease = new DaemonSocketPathLease(socketPath, releaseLock);\n\tif (pendingCompromise) lease.recordCompromise(pendingCompromise);\n\treturn lease;\n}\n\nexport async function prepareDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tassertSocketLeaseHeld(socketPath, lease);\n\t\tawait prepareUnixDaemonSocketPath(socketPath, lease);\n\t\treturn;\n\t}\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst ownedLease = await acquireDaemonSocketPathLease(socketPath);\n\ttry {\n\t\tawait prepareUnixDaemonSocketPath(socketPath, ownedLease);\n\t} finally {\n\t\tawait ownedLease?.release();\n\t}\n}\n\nasync function prepareUnixDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\n\tlet stat: ReturnType<typeof lstatSync>;\n\ttry {\n\t\tstat = lstatSync(socketPath);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tif (!stat.isSocket()) {\n\t\tthrow new Error(`Daemon socket path exists and is not a socket: ${socketPath}`);\n\t}\n\n\tconst staleIdentity: DaemonSocketIdentity = { dev: stat.dev, ino: stat.ino };\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst deadline = Date.now() + DAEMON_SOCKET_RELEASE_GRACE_MS;\n\twhile (Date.now() < deadline) {\n\t\tawait delay(DAEMON_SOCKET_RELEASE_POLL_MS);\n\t\tif (!existsSync(socketPath)) {\n\t\t\treturn;\n\t\t}\n\t\tlet currentIdentity: DaemonSocketIdentity | undefined;\n\t\ttry {\n\t\t\tcurrentIdentity = getDaemonSocketIdentity(socketPath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tif (!currentIdentity || currentIdentity.dev !== staleIdentity.dev || currentIdentity.ino !== staleIdentity.ino) {\n\t\t\tthrow new Error(`Daemon socket changed ownership while waiting for cleanup: ${socketPath}`);\n\t\t}\n\t\tif (await canConnectToUnixSocket(socketPath)) {\n\t\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t\t}\n\t}\n\n\tif (lease) assertSocketLeaseHeld(socketPath, lease);\n\tunlinkSync(socketPath);\n}\n\nexport function restrictDaemonSocketPath(socketPath: string): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tchmodSync(socketPath, DAEMON_SOCKET_MODE);\n}\n\nexport function getDaemonSocketIdentity(socketPath: string): DaemonSocketIdentity | undefined {\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tconst stat = lstatSync(socketPath);\n\treturn { dev: stat.dev, ino: stat.ino };\n}\n\nexport function cleanupDaemonSocketPath(\n\tsocketPath: string,\n\texpectedIdentity?: DaemonSocketIdentity,\n\tlease?: DaemonSocketPathLease,\n): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tif (lease.compromise) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t\t} catch {\n\t\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t\t}\n\t\treturn;\n\t}\n\tlet releaseLock: (() => void) | undefined;\n\tlet lockCompromised = false;\n\ttry {\n\t\treleaseLock = lockfile.lockSync(socketPath, {\n\t\t\trealpath: false,\n\t\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\t\tonCompromised: () => {\n\t\t\t\tlockCompromised = true;\n\t\t\t},\n\t\t\tretries: 0,\n\t\t});\n\t} catch {\n\t\treturn;\n\t}\n\tif (lockCompromised) {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort release.\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t} catch {\n\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t} finally {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort cleanup; a failed release is recoverable as a stale lock.\n\t\t}\n\t}\n}\n\nfunction cleanupUnixDaemonSocketPath(socketPath: string, expectedIdentity?: DaemonSocketIdentity): void {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (expectedIdentity) {\n\t\tconst currentIdentity = getDaemonSocketIdentity(socketPath);\n\t\tif (\n\t\t\t!currentIdentity ||\n\t\t\tcurrentIdentity.dev !== expectedIdentity.dev ||\n\t\t\tcurrentIdentity.ino !== expectedIdentity.ino\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t}\n\tunlinkSync(socketPath);\n}\n\nfunction assertSocketLease(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.socketPath !== socketPath) {\n\t\tthrow new Error(`Daemon socket lease does not match ${socketPath}`);\n\t}\n}\n\nfunction assertSocketLeaseHeld(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.compromise) {\n\t\tthrow new Error(`Daemon socket lease for ${socketPath} was compromised: ${lease.compromise.message}`);\n\t}\n}\n\nfunction daemonScopeId(packageDir: string): string {\n\treturn createHash(\"sha256\").update(`${getAgentDir()}\\0${packageDir}`).digest(\"hex\").slice(0, 12);\n}\n\nexport function defaultDaemonSocketDir(packageDir = getPackageDir()): string {\n\tconst suffix = typeof process.getuid === \"function\" ? String(process.getuid()) : \"user\";\n\treturn join(tmpdir(), `bc-${suffix}-${daemonScopeId(packageDir).slice(0, 8)}`);\n}\n\nfunction ensureDefaultDaemonSocketDir(socketPath: string): void {\n\tif (process.platform === \"win32\" || dirname(socketPath) !== defaultDaemonSocketDir()) {\n\t\treturn;\n\t}\n\n\tif (!existsSync(defaultDaemonSocketDir())) {\n\t\tmkdirSync(defaultDaemonSocketDir(), { recursive: true, mode: DAEMON_SOCKET_DIR_MODE });\n\t}\n\n\tconst stat = lstatSync(defaultDaemonSocketDir());\n\tif (!stat.isDirectory()) {\n\t\tthrow new Error(`Daemon socket directory exists and is not a directory: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tif (typeof process.getuid === \"function\" && stat.uid !== process.getuid()) {\n\t\tthrow new Error(`Daemon socket directory is not owned by the current user: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tchmodSync(defaultDaemonSocketDir(), DAEMON_SOCKET_DIR_MODE);\n}\n\nfunction canConnectToUnixSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolveConnect) => {\n\t\tconst socket = createConnection(socketPath);\n\t\tlet settled = false;\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n\t\tconst finish = (canConnect: boolean) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tif (timeoutId) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\tsocket.removeAllListeners();\n\t\t\tsocket.destroy();\n\t\t\tresolveConnect(canConnect);\n\t\t};\n\n\t\ttimeoutId = setTimeout(() => finish(false), 250);\n\t\tsocket.once(\"connect\", () => finish(true));\n\t\tsocket.once(\"error\", () => finish(false));\n\t});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}
1
+ {"version":3,"file":"daemon-socket.js","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-socket.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, lstatSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { createConnection, type Server, type Socket } from \"node:net\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir, getPackageDir } from \"../../config.js\";\nimport { PRODUCT } from \"../../product-identity.js\";\n\nexport { normalizeSocketPath } from \"../../utils/daemon-socket-path.js\";\n\nconst DAEMON_SOCKET_MODE = 0o600;\nconst DAEMON_SOCKET_DIR_MODE = 0o700;\nconst DAEMON_SOCKET_RELEASE_GRACE_MS = 1000;\nconst DAEMON_SOCKET_RELEASE_POLL_MS = 25;\nconst DAEMON_SOCKET_LOCK_STALE_MS = 5000;\nconst DAEMON_SOCKET_LOCK_UPDATE_MS = 1000;\n\ntype DaemonSocketCompromiseListener = (error: Error) => void;\n\nexport class DaemonSocketPathLease {\n\tprivate released = false;\n\tprivate compromisedError?: Error;\n\tprivate readonly compromiseListeners = new Set<DaemonSocketCompromiseListener>();\n\n\tconstructor(\n\t\treadonly socketPath: string,\n\t\tprivate readonly releaseLock: () => Promise<void>,\n\t) {}\n\n\tget compromise(): Error | undefined {\n\t\treturn this.compromisedError;\n\t}\n\n\tonCompromised(listener: DaemonSocketCompromiseListener): () => void {\n\t\tif (this.compromisedError) {\n\t\t\tthis.notifyCompromiseListener(listener, this.compromisedError);\n\t\t\treturn () => {};\n\t\t}\n\t\tthis.compromiseListeners.add(listener);\n\t\treturn () => this.compromiseListeners.delete(listener);\n\t}\n\n\trecordCompromise(error: Error): void {\n\t\tif (this.compromisedError) return;\n\t\tthis.compromisedError = error;\n\t\tconst listeners = [...this.compromiseListeners];\n\t\tthis.compromiseListeners.clear();\n\t\tfor (const listener of listeners) this.notifyCompromiseListener(listener, error);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (this.released) return;\n\t\tthis.released = true;\n\t\tthis.compromiseListeners.clear();\n\t\tawait this.releaseLock();\n\t}\n\n\tprivate notifyCompromiseListener(listener: DaemonSocketCompromiseListener, error: Error): void {\n\t\ttry {\n\t\t\tlistener(error);\n\t\t} catch {\n\t\t\t// A lease callback must not rethrow from proper-lockfile's refresh callback.\n\t\t}\n\t}\n}\n\nexport async function closeDaemonServer(\n\tserver: Server | undefined,\n\tclients: Iterable<{ socket: Socket; detachInput(): void }>,\n): Promise<void> {\n\tconst closingClients = [...clients];\n\tfor (const client of closingClients) {\n\t\tclient.detachInput();\n\t\tclient.socket.end();\n\t}\n\t// Allow final replies to flush, but do not wait indefinitely for a peer's FIN.\n\tconst timeout = setTimeout(() => {\n\t\tfor (const client of closingClients) client.socket.destroy();\n\t}, 1000);\n\ttimeout.unref();\n\ttry {\n\t\tawait new Promise<void>((resolveClose) => server?.close(() => resolveClose()) ?? resolveClose());\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\n\nexport interface DaemonSocketIdentity {\n\tdev: number;\n\tino: number;\n}\n\nexport function defaultDaemonSocketPath(packageDir = getPackageDir()): string {\n\tif (process.platform === \"win32\") {\n\t\treturn `\\\\\\\\.\\\\pipe\\\\${PRODUCT.command}-${daemonScopeId(packageDir)}`;\n\t}\n\treturn join(defaultDaemonSocketDir(packageDir), \"daemon.sock\");\n}\n\nexport async function acquireDaemonSocketPathLease(socketPath: string): Promise<DaemonSocketPathLease | undefined> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tlet lease: DaemonSocketPathLease | undefined;\n\tlet pendingCompromise: Error | undefined;\n\tconst releaseLock = await lockfile.lock(socketPath, {\n\t\trealpath: false,\n\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\tonCompromised: (error) => {\n\t\t\tif (lease) lease.recordCompromise(error);\n\t\t\telse pendingCompromise = error;\n\t\t},\n\t\tretries: {\n\t\t\tretries: 600,\n\t\t\tfactor: 1,\n\t\t\tminTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t\tmaxTimeout: DAEMON_SOCKET_RELEASE_POLL_MS,\n\t\t},\n\t});\n\tlease = new DaemonSocketPathLease(socketPath, releaseLock);\n\tif (pendingCompromise) lease.recordCompromise(pendingCompromise);\n\treturn lease;\n}\n\nexport async function prepareDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tensureDefaultDaemonSocketDir(socketPath);\n\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tassertSocketLeaseHeld(socketPath, lease);\n\t\tawait prepareUnixDaemonSocketPath(socketPath, lease);\n\t\treturn;\n\t}\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst ownedLease = await acquireDaemonSocketPathLease(socketPath);\n\ttry {\n\t\tawait prepareUnixDaemonSocketPath(socketPath, ownedLease);\n\t} finally {\n\t\tawait ownedLease?.release();\n\t}\n}\n\nasync function prepareUnixDaemonSocketPath(socketPath: string, lease?: DaemonSocketPathLease): Promise<void> {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\n\tlet stat: ReturnType<typeof lstatSync>;\n\ttry {\n\t\tstat = lstatSync(socketPath);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\tif (!stat.isSocket()) {\n\t\tthrow new Error(`Daemon socket path exists and is not a socket: ${socketPath}`);\n\t}\n\n\tconst staleIdentity: DaemonSocketIdentity = { dev: stat.dev, ino: stat.ino };\n\tif (await canConnectToUnixSocket(socketPath)) {\n\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t}\n\tconst deadline = Date.now() + DAEMON_SOCKET_RELEASE_GRACE_MS;\n\twhile (Date.now() < deadline) {\n\t\tawait delay(DAEMON_SOCKET_RELEASE_POLL_MS);\n\t\tif (!existsSync(socketPath)) {\n\t\t\treturn;\n\t\t}\n\t\tlet currentIdentity: DaemonSocketIdentity | undefined;\n\t\ttry {\n\t\t\tcurrentIdentity = getDaemonSocketIdentity(socketPath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tif (!currentIdentity || currentIdentity.dev !== staleIdentity.dev || currentIdentity.ino !== staleIdentity.ino) {\n\t\t\tthrow new Error(`Daemon socket changed ownership while waiting for cleanup: ${socketPath}`);\n\t\t}\n\t\tif (await canConnectToUnixSocket(socketPath)) {\n\t\t\tthrow new Error(`Daemon socket already in use: ${socketPath}`);\n\t\t}\n\t}\n\n\tif (lease) assertSocketLeaseHeld(socketPath, lease);\n\tunlinkSync(socketPath);\n}\n\nexport function restrictDaemonSocketPath(socketPath: string): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tchmodSync(socketPath, DAEMON_SOCKET_MODE);\n}\n\nexport function getDaemonSocketIdentity(socketPath: string): DaemonSocketIdentity | undefined {\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\tconst stat = lstatSync(socketPath);\n\treturn { dev: stat.dev, ino: stat.ino };\n}\n\nexport function cleanupDaemonSocketPath(\n\tsocketPath: string,\n\texpectedIdentity?: DaemonSocketIdentity,\n\tlease?: DaemonSocketPathLease,\n): void {\n\tif (process.platform === \"win32\") {\n\t\treturn;\n\t}\n\tif (lease) {\n\t\tassertSocketLease(socketPath, lease);\n\t\tif (lease.compromise) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t\t} catch {\n\t\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t\t}\n\t\treturn;\n\t}\n\tlet releaseLock: (() => void) | undefined;\n\tlet lockCompromised = false;\n\ttry {\n\t\treleaseLock = lockfile.lockSync(socketPath, {\n\t\t\trealpath: false,\n\t\t\tstale: DAEMON_SOCKET_LOCK_STALE_MS,\n\t\t\tupdate: DAEMON_SOCKET_LOCK_UPDATE_MS,\n\t\t\tonCompromised: () => {\n\t\t\t\tlockCompromised = true;\n\t\t\t},\n\t\t\tretries: 0,\n\t\t});\n\t} catch {\n\t\treturn;\n\t}\n\tif (lockCompromised) {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort release.\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tcleanupUnixDaemonSocketPath(socketPath, expectedIdentity);\n\t} catch {\n\t\t// Best effort cleanup; shutdown should not be blocked by socket unlink failures.\n\t} finally {\n\t\ttry {\n\t\t\treleaseLock();\n\t\t} catch {\n\t\t\t// Best effort cleanup; a failed release is recoverable as a stale lock.\n\t\t}\n\t}\n}\n\nfunction cleanupUnixDaemonSocketPath(socketPath: string, expectedIdentity?: DaemonSocketIdentity): void {\n\tif (!existsSync(socketPath)) {\n\t\treturn;\n\t}\n\tif (expectedIdentity) {\n\t\tconst currentIdentity = getDaemonSocketIdentity(socketPath);\n\t\tif (\n\t\t\t!currentIdentity ||\n\t\t\tcurrentIdentity.dev !== expectedIdentity.dev ||\n\t\t\tcurrentIdentity.ino !== expectedIdentity.ino\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t}\n\tunlinkSync(socketPath);\n}\n\nfunction assertSocketLease(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.socketPath !== socketPath) {\n\t\tthrow new Error(`Daemon socket lease does not match ${socketPath}`);\n\t}\n}\n\nfunction assertSocketLeaseHeld(socketPath: string, lease: DaemonSocketPathLease): void {\n\tif (lease.compromise) {\n\t\tthrow new Error(`Daemon socket lease for ${socketPath} was compromised: ${lease.compromise.message}`);\n\t}\n}\n\nfunction daemonScopeId(packageDir: string): string {\n\treturn createHash(\"sha256\").update(`${getAgentDir()}\\0${packageDir}`).digest(\"hex\").slice(0, 12);\n}\n\nexport function defaultDaemonSocketDir(packageDir = getPackageDir()): string {\n\tconst suffix = typeof process.getuid === \"function\" ? String(process.getuid()) : \"user\";\n\treturn join(tmpdir(), `bc-${suffix}-${daemonScopeId(packageDir).slice(0, 8)}`);\n}\n\nfunction ensureDefaultDaemonSocketDir(socketPath: string): void {\n\tif (process.platform === \"win32\" || dirname(socketPath) !== defaultDaemonSocketDir()) {\n\t\treturn;\n\t}\n\n\tif (!existsSync(defaultDaemonSocketDir())) {\n\t\tmkdirSync(defaultDaemonSocketDir(), { recursive: true, mode: DAEMON_SOCKET_DIR_MODE });\n\t}\n\n\tconst stat = lstatSync(defaultDaemonSocketDir());\n\tif (!stat.isDirectory()) {\n\t\tthrow new Error(`Daemon socket directory exists and is not a directory: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tif (typeof process.getuid === \"function\" && stat.uid !== process.getuid()) {\n\t\tthrow new Error(`Daemon socket directory is not owned by the current user: ${defaultDaemonSocketDir()}`);\n\t}\n\n\tchmodSync(defaultDaemonSocketDir(), DAEMON_SOCKET_DIR_MODE);\n}\n\nfunction canConnectToUnixSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolveConnect) => {\n\t\tconst socket = createConnection(socketPath);\n\t\tlet settled = false;\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n\t\tconst finish = (canConnect: boolean) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tif (timeoutId) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\tsocket.removeAllListeners();\n\t\t\tsocket.destroy();\n\t\t\tresolveConnect(canConnect);\n\t\t};\n\n\t\ttimeoutId = setTimeout(() => finish(false), 250);\n\t\tsocket.once(\"connect\", () => finish(true));\n\t\tsocket.once(\"error\", () => finish(false));\n\t});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}