zidane 9.1.9 → 9.1.13
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-Bha_yCgU.js → acp-Cz5mFUjB.js} +3 -3
- package/dist/{acp-Bha_yCgU.js.map → acp-Cz5mFUjB.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.js +1 -1
- package/dist/{auth-store-port-BvxVXx9g.js → auth-store-port-CDJa8Q8U.js} +2 -2
- package/dist/{auth-store-port-BvxVXx9g.js.map → auth-store-port-CDJa8Q8U.js.map} +1 -1
- package/dist/{auto-update-restart-CF3uPnr4.js → auto-update-restart-B4pU7Qgf.js} +10 -10
- package/dist/{auto-update-restart-CF3uPnr4.js.map → auto-update-restart-B4pU7Qgf.js.map} +1 -1
- package/dist/chat.js +9 -9
- package/dist/{cli-shared-C17TUz6H.js → cli-shared-BVtiiICW.js} +3 -3
- package/dist/{cli-shared-C17TUz6H.js.map → cli-shared-BVtiiICW.js.map} +1 -1
- package/dist/{credentials-BZP-MgtZ.js → credentials-axv5VvMR.js} +2 -2
- package/dist/{credentials-BZP-MgtZ.js.map → credentials-axv5VvMR.js.map} +1 -1
- package/dist/daemon-cli.js +2 -2
- package/dist/{discovery-Czp7bZZP.js → discovery-D7qVZWzq.js} +4 -4
- package/dist/{discovery-Czp7bZZP.js.map → discovery-D7qVZWzq.js.map} +1 -1
- package/dist/{engine-CDuOhwbt.js → engine-DjM5NPIf.js} +3 -3
- package/dist/{engine-CDuOhwbt.js.map → engine-DjM5NPIf.js.map} +1 -1
- package/dist/{executor-CoHqHsCp.js → executor-DlW7qAeN.js} +2 -2
- package/dist/{executor-CoHqHsCp.js.map → executor-DlW7qAeN.js.map} +1 -1
- package/dist/extensions/testing.js +2 -2
- package/dist/{extensions-BEaWxX4e.js → extensions-D7DeZNpn.js} +6 -6
- package/dist/{extensions-BEaWxX4e.js.map → extensions-D7DeZNpn.js.map} +1 -1
- package/dist/extensions.js +5 -5
- package/dist/{google-Bzt7uvy9.js → google-D1zjAViO.js} +2 -2
- package/dist/{google-Bzt7uvy9.js.map → google-D1zjAViO.js.map} +1 -1
- package/dist/{host-DkqB2P7L.js → host-Ds3RDlG5.js} +9 -9
- package/dist/{host-DkqB2P7L.js.map → host-Ds3RDlG5.js.map} +1 -1
- package/dist/{host-signals-Ca_oUeYl.js → host-signals-C8x5Yso0.js} +5 -5
- package/dist/{host-signals-Ca_oUeYl.js.map → host-signals-C8x5Yso0.js.map} +1 -1
- package/dist/{host-version-BwON7XSr.js → host-version-BGZumnpZ.js} +2 -2
- package/dist/{host-version-BwON7XSr.js.map → host-version-BGZumnpZ.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/ipc/remote.js +3 -3
- package/dist/{ipc-Dlqz_xIe.js → ipc-DGfuNEfR.js} +4 -4
- package/dist/{ipc-Dlqz_xIe.js.map → ipc-DGfuNEfR.js.map} +1 -1
- package/dist/ipc.js +4 -4
- package/dist/{links-B5Dz7jku.js → links-D_1RhPh5.js} +8 -8
- package/dist/{links-B5Dz7jku.js.map → links-D_1RhPh5.js.map} +1 -1
- package/dist/{loaded-entry-BGN69Fns.js → loaded-entry-CLva6GQT.js} +2 -2
- package/dist/{loaded-entry-BGN69Fns.js.map → loaded-entry-CLva6GQT.js.map} +1 -1
- package/dist/{memory-ZWFqa7di.js → memory-CaFXUvQs.js} +15 -15
- package/dist/{memory-ZWFqa7di.js.map → memory-CaFXUvQs.js.map} +1 -1
- package/dist/{path-display-B3Jeqx9E.js → path-display-ZIWdFaTI.js} +3 -3
- package/dist/{path-display-B3Jeqx9E.js.map → path-display-ZIWdFaTI.js.map} +1 -1
- package/dist/providers/google.js +1 -1
- package/dist/{providers-ChifjOYS.js → providers-CsYrxuX1.js} +2 -2
- package/dist/{providers-ChifjOYS.js.map → providers-CsYrxuX1.js.map} +1 -1
- package/dist/providers.js +1 -1
- package/dist/{registry-mmAX5qBS.js → registry-BY45AOA-.js} +2 -2
- package/dist/{registry-mmAX5qBS.js.map → registry-BY45AOA-.js.map} +1 -1
- package/dist/{registry-TW3C94iD.js → registry-DTotchq6.js} +2 -2
- package/dist/{registry-TW3C94iD.js.map → registry-DTotchq6.js.map} +1 -1
- package/dist/{registry-CYEN4C4M.js → registry-Dl6261Xh.js} +2 -2
- package/dist/{registry-CYEN4C4M.js.map → registry-Dl6261Xh.js.map} +1 -1
- package/dist/{remote-hosts-Ctg9fwQY.js → remote-hosts-usqMiMbs.js} +4 -4
- package/dist/{remote-hosts-Ctg9fwQY.js.map → remote-hosts-usqMiMbs.js.map} +1 -1
- package/dist/{setting-DPim5iCP.js → setting-DzTMJIV3.js} +2 -2
- package/dist/{setting-DPim5iCP.js.map → setting-DzTMJIV3.js.map} +1 -1
- package/dist/{settings-keys-BIWy4T_0.js → settings-keys-DDrfnBHi.js} +2 -2
- package/dist/{settings-keys-BIWy4T_0.js.map → settings-keys-DDrfnBHi.js.map} +1 -1
- package/dist/{system-freeze-CEuTQzNu.js → system-freeze-CY4Hc9WX.js} +2 -2
- package/dist/{system-freeze-CEuTQzNu.js.map → system-freeze-CY4Hc9WX.js.map} +1 -1
- package/dist/tui.js +15 -15
- package/dist/{version-CU8ppdCQ.js → version-Bu3Nc8-U.js} +2 -2
- package/dist/{version-CU8ppdCQ.js.map → version-Bu3Nc8-U.js.map} +1 -1
- package/dist/{wire-GwOPpxJg.js → wire-BKcREhuL.js} +2 -2
- package/dist/{wire-GwOPpxJg.js.map → wire-BKcREhuL.js.map} +1 -1
- package/dist/{ws-CqlZkB4w.js → ws-DriNRyP-.js} +4 -4
- package/dist/{ws-CqlZkB4w.js.map → ws-DriNRyP-.js.map} +1 -1
- package/dist/{ws-client-Bn_asPwE.js → ws-client-PZdbO0fj.js} +2 -2
- package/dist/{ws-client-Bn_asPwE.js.map → ws-client-PZdbO0fj.js.map} +1 -1
- package/docs/RELEASE.md +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-keys-BIWy4T_0.js","names":[],"sources":["../src/ipc/daemon/lifecycle.ts","../src/ipc/transports/client-core.ts","../src/chat/sync/hash.ts","../src/chat/sync/settings-keys.ts"],"sourcesContent":["/**\n * Daemon lifecycle: socket-path resolution, liveness probing, stale-socket\n * cleanup, and spawn-on-demand.\n *\n * The socket lives in a dedicated daemon subdir of the resolved state dir\n * (`~/.zidane/daemon/daemon.sock` by default, honoring the same\n * `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX` env knobs every other surface\n * uses), overridable via `ZIDANE_DAEMON_SOCKET` for parallel installs and\n * tests.\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync, readdirSync, statSync } from 'node:fs'\nimport { connect } from 'node:net'\nimport { basename, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { resolveStorageDirs } from '../../chat/xdg'\n\n/**\n * Build fingerprint for the handshake. Running from source it is the newest\n * mtime under the `src/` tree at import time. Bundled, it is the mtime of this\n * module's own file. Client and daemon load the same files from disk, so a\n * daemon spawned before a rebuild (or a source edit in dev) reports a\n * different id than a freshly-started client, and the client drains +\n * respawns it. This closes the \"same protocol version, different code\"\n * gap that a hand-bumped version constant can't: during development the\n * daemon process is frozen at spawn while the tree keeps moving.\n *\n * Compiled binaries (`/$bunfs/…` virtual paths) can't be stat'ed and\n * report `'unknown'`; the check is skipped when EITHER side is unknown,\n * so released builds rely on `IPC_PROTOCOL_VERSION` alone — correct,\n * since their code only changes when the version does.\n */\nexport const IPC_BUILD_ID: string = (() => {\n try {\n const selfPath = fileURLToPath(import.meta.url)\n /*\n * Dev (running from source): fingerprint the whole source tree, not\n * just this file — a change to server.ts / engine.ts / runtime code\n * with no edit to lifecycle.ts must still retire an old daemon. Walk\n * `src/` and take the newest mtime. When the tree isn't present\n * (bundled dist, compiled binary) we fall back to this module's own\n * file mtime; in a bundle every ipc source collapses into one chunk, so\n * that single mtime already moves on any ipc change.\n */\n /*\n * Root validation is load-bearing: from a built bundle this module\n * lives flat in `dist/`, so the two-levels-up guess would be the\n * PACKAGE root — walking it would synchronously stat the whole repo\n * (node_modules, .git) at import time and fingerprint unrelated\n * churn. Only walk when the candidate directory demonstrably IS the\n * ipc source tree; a bundle falls back to this chunk's own mtime,\n * which already moves on every rebuild.\n */\n const ipcRoot = dirname(dirname(selfPath))\n if (!existsSync(join(ipcRoot, 'protocol.ts')))\n return String(Math.floor(statSync(selfPath).mtimeMs))\n\n /*\n * Dev scope: walk the whole `src/` tree, not just `src/ipc` — daemon\n * behavior also lives in agent.ts, loop.ts, and chat/** (store,\n * replay, discovery), and an edit there must retire a running dev\n * daemon just as surely as an ipc edit (the spawn-marker incident\n * lived in chat/store.ts). A few hundred stats — milliseconds.\n */\n const srcRoot = dirname(ipcRoot)\n let newest = 0\n const walk = (dir: string): void => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory())\n walk(full)\n else\n newest = Math.max(newest, statSync(full).mtimeMs)\n }\n }\n try {\n walk(srcRoot)\n }\n catch {\n newest = statSync(selfPath).mtimeMs\n }\n return String(Math.floor(newest))\n }\n catch {\n return 'unknown'\n }\n})()\n\n/**\n * Socket file name. Dev builds (`ZIDANE_DEV=1`, set by every `dev` script\n * in the repo) get their OWN socket so a checkout's TUI, GUI and `zidane\n * daemon` pair up among themselves and never attach to — or get refused by\n * — the installed daemon. Without this, a protocol bump in the working\n * tree turned the dev GUI's dot red against the stable daemon, and a dev\n * daemon could drain the one the installed TUI relies on.\n * @param env - Environment to read `ZIDANE_DEV` from.\n * @returns `dev.sock` for a dev build, else `daemon.sock`.\n */\nexport function daemonSocketName(env: NodeJS.ProcessEnv = process.env): 'daemon.sock' | 'dev.sock' {\n return env.ZIDANE_DEV && env.ZIDANE_DEV !== '0' ? 'dev.sock' : 'daemon.sock'\n}\n\n/**\n * Path of the daemon socket this process uses.\n * The `ZIDANE_DAEMON_SOCKET` environment variable wins. Otherwise it is `<stateDir>/daemon/<socket name>`, see {@link daemonSocketName}.\n * @returns The socket path.\n */\nexport function defaultSocketPath(): string {\n const explicit = process.env.ZIDANE_DAEMON_SOCKET\n if (explicit)\n return explicit\n /*\n * The socket is transient runtime state, so it lands under the resolved\n * state dir — XDG-aware on XDG installs, `<storageDir>/<prefix>` in\n * legacy mode. Uses the same resolver every other surface reads through,\n * honoring `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX`.\n *\n * DEDICATED `daemon/` subdir: the state dir is shared with\n * `state.json` / logs, so the daemon can't chmod it 0700 without\n * affecting unrelated files. The server hardens this subdir instead;\n * client and server must agree on this path.\n */\n const dirs = resolveStorageDirs({\n ...(process.env.ZIDANE_PREFIX !== undefined ? { prefix: process.env.ZIDANE_PREFIX } : {}),\n })\n return join(dirs.stateDir, 'daemon', daemonSocketName())\n}\n\n/**\n * Legacy socket location (`<stateDir>/daemon.sock`) for the given socket path,\n * or null when the path isn't the default `<stateDir>/daemon/daemon.sock`\n * layout (explicit `ZIDANE_DAEMON_SOCKET` users never had a migration to run).\n * A daemon still serving the old path would be ORPHANED by the move — new clients\n * never probe it, and a service-style `--idle-exit 0` daemon would hold\n * agents and run-guard markers against the same sessions.db forever —\n * so daemon startup probes this path and drains any survivor.\n */\nexport function legacySocketPath(socketPath: string): string | null {\n const dir = dirname(socketPath)\n if (basename(socketPath) !== 'daemon.sock' || basename(dir) !== 'daemon')\n return null\n return join(dirname(dir), 'daemon.sock')\n}\n\n/**\n * Spawn command that re-enters the CURRENT zidane program as `… daemon`.\n *\n * Three launch shapes:\n * - compiled Bun binary — `Bun.main` is the virtual `/$bunfs/…` entry the\n * binary self-inserts as argv[1], so the spawn must NOT repeat it\n * (verified: repeating it makes the child parse the junk path as its\n * subcommand and boot the full TUI instead of the daemon).\n * - dev (`bun run cli.ts`) — the script path must ride along.\n * - Node hosts (no `Bun` global) — fall back to the published bin.\n */\nexport function selfDaemonSpawnCommand(): string[] {\n const bunMain = (globalThis as { Bun?: { main?: string } }).Bun?.main\n // Compiled binary: the virtual entry parses its own `daemon` subcommand.\n if (bunMain?.startsWith('/$bunfs'))\n return [process.execPath, 'daemon']\n // Dev-from-source: only re-execute `Bun.main` when it's actually a\n // zidane CLI entry (`cli.ts`/`cli.js`). For any other embedder (library\n // consumer, script) re-running their entry with a `daemon` argv would\n // launch an arbitrary program — fall back to the published bin instead.\n if (bunMain && /(?:^|[/\\\\])cli\\.[cm]?[jt]s$/.test(bunMain))\n return [process.execPath, bunMain, 'daemon']\n return ['zidane-daemon']\n}\n\n/**\n * True when something accepts connections on `socketPath`. Distinguishes a\n * live daemon from a stale socket file left by a crashed one.\n * @param socketPath - Path of the socket to try.\n * @param timeoutMs - How long to wait for the connection, in milliseconds.\n */\nexport function probeSocket(socketPath: string, timeoutMs = 1000): Promise<boolean> {\n return new Promise((resolve) => {\n /*\n * Skip the connect entirely unless the path is an actual socket file.\n * A stale REGULAR file (crashed daemon) or a missing path both mean\n * \"nothing listening\" — and connecting to them can surface a noisy\n * async ENOENT/ENOTSOCK that some runtimes treat as uncaught.\n */\n try {\n if (!existsSync(socketPath) || !statSync(socketPath).isSocket()) {\n resolve(false)\n return\n }\n }\n catch {\n resolve(false)\n return\n }\n\n let socket: ReturnType<typeof connect>\n try {\n socket = connect(socketPath)\n }\n catch {\n resolve(false)\n return\n }\n const done = (alive: boolean): void => {\n socket.destroy()\n resolve(alive)\n }\n socket.once('connect', () => done(true))\n socket.once('error', () => done(false))\n socket.setTimeout(timeoutMs, () => done(false))\n })\n}\n\n/** Options for {@link ensureDaemonRunning}. */\nexport interface EnsureDaemonOptions {\n /**\n * Socket the daemon listens on.\n * @default defaultSocketPath()\n */\n socketPath?: string\n /**\n * Command used to spawn the daemon when nothing is listening. Defaults to\n * `['zidane-daemon']` (the published bin). The child is detached and\n * unref'd — it outlives the spawning client by design.\n */\n spawnCommand?: string[]\n /** Total time to wait for the daemon to accept connections. Default 8000 ms. */\n timeoutMs?: number\n}\n\n/** Daemon starts in flight, by socket path, so concurrent callers share one. */\nconst pendingStarts = new Map<string, Promise<string>>()\n\n/**\n * Makes sure a daemon is listening on the socket, spawning one when nothing is. Concurrent calls for the same socket share one start.\n * Rejects when the spawn fails or the daemon does not accept connections within `timeoutMs`.\n * @param options - Socket, spawn command and timeout.\n * @returns The socket path once a connection succeeds.\n */\nexport function ensureDaemonRunning(options: EnsureDaemonOptions = {}): Promise<string> {\n const socketPath = options.socketPath ?? defaultSocketPath()\n const pending = pendingStarts.get(socketPath)\n if (pending)\n return pending\n const start = startDaemon({ ...options, socketPath }).finally(() => pendingStarts.delete(socketPath))\n pendingStarts.set(socketPath, start)\n return start\n}\n\nasync function startDaemon(options: EnsureDaemonOptions & { socketPath: string }): Promise<string> {\n const socketPath = options.socketPath\n if (await probeSocket(socketPath))\n return socketPath\n\n const [cmd, ...args] = options.spawnCommand ?? ['zidane-daemon']\n const child = spawn(cmd!, args, {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, ZIDANE_DAEMON_SOCKET: socketPath },\n })\n /*\n * A missing binary (e.g. `zidane-daemon` not on PATH) emits an async\n * `error` event; without a listener Node rethrows it as an uncaught\n * exception and crashes the caller. Capture it and surface a clean\n * rejection through the poll loop below.\n */\n let spawnError: Error | undefined\n child.once('error', (err: Error) => {\n spawnError = err\n })\n child.unref()\n\n const deadline = Date.now() + (options.timeoutMs ?? 8000)\n for (;;) {\n if (spawnError !== undefined)\n throw new Error(`Failed to spawn zidane daemon (${cmd}): ${spawnError.message}`)\n if (await probeSocket(socketPath))\n return socketPath\n if (Date.now() > deadline)\n throw new Error(`zidane daemon did not come up on ${socketPath} within ${options.timeoutMs ?? 8000}ms.`)\n await new Promise(r => setTimeout(r, 150))\n }\n}\n","/**\n * The client side every wire transport shares (the unix socket in\n * `../client.ts`, the WebSocket in `./ws-client.ts`): the `ipc.hello`\n * handshake with its version fences, typed requests with deadlines,\n * notifications, and signal fan-out. A transport supplies a connected\n * JSON-RPC {@link ClientChannel}; this turns it into an {@link IpcClient}.\n */\nimport type { AcpPeer, JsonRpcMessage } from '../../acp/types'\nimport type { HelloResult, HostSignal, IpcMethodName, IpcParams, IpcResult, SignalMeta } from '../protocol'\nimport { JsonRpcConnectionClosedError, JsonRpcRemoteError } from '../../acp/json-rpc'\nimport { compareVersions, ZIDANE_VERSION } from '../../version'\nimport { IPC_BUILD_ID } from '../daemon/lifecycle'\nimport { IPC_MIN_PROTOCOL_VERSION, IPC_PROTOCOL_VERSION, IpcErrorCode, PING_METHOD, SIGNAL_SEQ_FIELD } from '../protocol'\n\n/** A connected host client, shared by every transport. Requests are checked against {@link IpcMethods}. */\nexport interface IpcClient {\n /** The host's answer to the `ipc.hello` handshake. */\n hello: HelloResult\n /**\n * Calls a host method and resolves with its result. Rejects with a `JsonRpcRemoteError` when the host answers with an error, or with `JsonRpcConnectionClosedError` when the connection closes first.\n * A request not answered within its deadline (30 s, longer for slow methods) rejects and drops the connection. `machine.call` has no deadline.\n */\n request: <M extends IpcMethodName>(method: M, params: IpcParams<M>) => Promise<IpcResult<M>>\n /**\n * Fire-and-forget notification — same method table as `request`, but no\n * id, no response, no pending-promise bookkeeping. The hot path for\n * high-frequency writes (per-keystroke draft sync). Daemons that don't\n * know the method ignore it silently.\n */\n notify: <M extends IpcMethodName>(method: M, params: IpcParams<M>) => void\n /** Host signals, with their number in their session when they have one (`SignalMeta`). */\n onSignal: (cb: SignalListener) => () => void\n /** Closes the connection. Pending requests reject. */\n close: () => void\n}\n\nexport type SignalListener = (signal: HostSignal, meta?: SignalMeta) => void\n\n/** Thrown when the host refuses the handshake because its protocol, build or code version does not match the client's. */\nexport class IpcVersionMismatchError extends Error {\n /**\n * @param message - Description of the mismatch.\n * @param daemon - The host's handshake data, when it sent any.\n */\n constructor(message: string, readonly daemon?: HelloResult) {\n super(message)\n this.name = 'IpcVersionMismatchError'\n }\n}\n\n/** A connected JSON-RPC link to a host, as a transport hands it over. */\nexport interface ClientChannel {\n rpc: Pick<AcpPeer, 'sendRequest' | 'sendNotification'>\n /** The caller's `close()`: quiet (no `onClose`), fails what is in flight. Idempotent. */\n close: () => void\n /** Drop a wedged connection: like the host going away, so the transport's `onClose` fires. */\n abort: () => void\n /** Where the host is, for error messages (a socket path, a URL). */\n where: string\n}\n\nexport interface ClientCoreOptions {\n appVersion?: string\n /**\n * When the host refuses the handshake on a version / build mismatch:\n * `always` asks it to drain so a matching one can be spawned (session\n * controllers on the local daemon); `stale-only` only when the host is\n * provably older (passive local clients); `never` leaves it running (a\n * remote server's lifetime is not the client's to end).\n */\n drain: 'always' | 'stale-only' | 'never'\n /**\n * Send this build's fingerprint, so a host from an older build of the same\n * protocol refuses us. Off sends the `'unknown'` sentinel: clients that\n * can't act on a build mismatch (no drain, no respawn) opt out of that fence.\n */\n buildFence: boolean\n /**\n * Ping the host (`$/ping`) this often once attached; two pings in a row\n * left unanswered at the next tick abort the connection (the transport's\n * `onClose` fires, its owner reconnects). Off when absent or `0`: a\n * local socket learns of a dead host from the kernel.\n */\n heartbeatMs?: number\n}\n\n/** Missed heartbeats after which the host is considered gone. */\nexport const HEARTBEAT_MISSES = 2\n\n/** Starts the client heartbeat on `channel`; returns its stop. Stops itself once the channel closes. */\nfunction startHeartbeat(channel: ClientChannel, intervalMs: number): () => void {\n let awaiting = false\n let missed = 0\n const timer = setInterval(() => {\n if (awaiting && ++missed >= HEARTBEAT_MISSES) {\n clearInterval(timer)\n channel.abort()\n return\n }\n awaiting = true\n channel.rpc.sendRequest(PING_METHOD, {}).then(\n () => {\n awaiting = false\n missed = 0\n },\n (err: unknown) => {\n if (err instanceof JsonRpcConnectionClosedError) {\n clearInterval(timer)\n return\n }\n // Any answer, an error included (a host predating `$/ping`), proves it alive.\n awaiting = false\n missed = 0\n },\n )\n }, intervalMs)\n timer.unref?.()\n return () => clearInterval(timer)\n}\n\n/** The signal listeners of one client, and the JSON-RPC notification handler that feeds them. */\nexport function createSignalListeners() {\n const listeners = new Set<SignalListener>()\n return {\n listeners,\n onNotification(message: JsonRpcMessage & { method: string }): void {\n if (message.method !== 'ipc.signal')\n return\n const params = (message as { params?: HostSignal & { [SIGNAL_SEQ_FIELD]?: unknown } }).params\n if (!params)\n return\n const { [SIGNAL_SEQ_FIELD]: seq, ...signal } = params\n const meta = typeof seq === 'number' ? { seq } : undefined\n for (const cb of listeners)\n cb(signal as HostSignal, meta)\n },\n }\n}\n\nfunction hostRelease(hello: HelloResult): string | undefined {\n for (const value of [hello.codeVersion, hello.appVersion]) {\n if (typeof value !== 'string')\n continue\n const match = /^(?:zidane-gui\\/)?(v?\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)$/.exec(value.trim())\n if (match)\n return match[1]\n }\n return undefined\n}\n\nconst HELLO_TIMEOUT_MS = 5000\nconst LONG_REQUEST_TIMEOUT_MS = 10 * 60 * 1000\n/**\n * RPCs that legitimately run long: full compaction, index-backed cross-session search, a clone, a folder search,\n * and extension code (an RPC method, a command, a UI callback, a sign-in that waits for the user).\n */\nconst LONG_REQUEST_METHODS = new Set<string>(['sessions.compact', 'history.searchAll', 'history.searchSession', 'git.clone', 'fs.searchDirs', 'rpc.call', 'extensions.command.run', 'extensions.ui.invoke', 'extensions.integrations.start', 'extensions.ui.list', 'extensions.entries'])\n\n/**\n * A refused handshake. Asks the host to drain when `drain` allows it, closes\n * the channel, and returns the error to throw.\n */\nasync function refusedHello(channel: ClientChannel, err: JsonRpcRemoteError, drain: ClientCoreOptions['drain']): Promise<IpcVersionMismatchError> {\n /*\n * Ask the stale daemon to drain so the caller can respawn a matching\n * one. AWAIT the reply (bounded) before tearing the connection down —\n * closing discards unflushed writes, so a fire-and-forget\n * `daemon.shutdown` could be dropped, leaving the stale daemon alive\n * and defeating `connectOrSpawnDaemon`'s retry.\n */\n const daemon = (err.data as { daemon?: HelloResult } | undefined)?.daemon\n // A network server's lifetime is never a client's to end, whatever `drain` says.\n if (daemon?.hostKind === 'server') {\n channel.close()\n return new IpcVersionMismatchError(serverMismatchMessage(daemon, channel.where) ?? err.message, daemon)\n }\n /*\n * Passive clients (`stale-only`) never retire a daemon on a protocol /\n * build mismatch — it may be NEWER than them. A daemon that is provably\n * OLDER — an older release, or the same release speaking an older\n * protocol (a dev build from before a protocol bump) — is the case where\n * draining is always safe: nothing newer can be running on it, and\n * whoever attaches next spawns a current one. Without this a passive\n * client (the GUI) would sit in-process forever behind a stale daemon\n * nobody restarts: every retry is refused.\n */\n const daemonIsOlderRelease\n = typeof daemon?.codeVersion === 'string' && compareVersions(daemon.codeVersion, ZIDANE_VERSION) < 0\n const release = daemon && hostRelease(daemon)\n const daemonIsNewerRelease = release !== undefined && compareVersions(release, ZIDANE_VERSION) > 0\n const daemonSpeaksOlderProtocol = typeof daemon?.protocolVersion === 'number' && daemon.protocolVersion < IPC_PROTOCOL_VERSION\n const daemonIsStale = daemonIsOlderRelease || daemonSpeaksOlderProtocol\n if (drain !== 'never' && !daemonIsNewerRelease && (drain === 'always' || daemonIsStale)) {\n // `whenIdle`: a daemon with live runs defers its exit instead of\n // aborting them (older daemons ignore the flag and drain now).\n await Promise.race([\n channel.rpc.sendRequest('daemon.shutdown', { whenIdle: true }).catch(() => undefined),\n new Promise(resolve => setTimeout(resolve, 1500)),\n ])\n }\n channel.close()\n return new IpcVersionMismatchError(err.message, daemon)\n}\n\n/**\n * A server's protocol refusal, from this client's side: both versions and which side to upgrade.\n * `null` when the server's range (`minProtocolVersion`…`protocolVersion`) overlaps this client's.\n */\nfunction serverMismatchMessage(server: HelloResult, where: string): string | null {\n if (typeof server.protocolVersion !== 'number')\n return null\n const serverMin = typeof server.minProtocolVersion === 'number' ? server.minProtocolVersion : server.protocolVersion\n if (serverMin <= IPC_PROTOCOL_VERSION && server.protocolVersion >= IPC_MIN_PROTOCOL_VERSION)\n return null\n const upgrade = server.protocolVersion < IPC_PROTOCOL_VERSION ? 'server' : 'client'\n return `Protocol version mismatch with ${where}: the server speaks v${server.protocolVersion}, this client v${IPC_PROTOCOL_VERSION}. Upgrade the ${upgrade}.`\n}\n\nasync function handshake(channel: ClientChannel, options: ClientCoreOptions): Promise<HelloResult> {\n /*\n * Bound the handshake: a wedged daemon (accepting connections but never\n * reading — e.g. SIGSTOP'd) would otherwise hang this promise forever.\n * On timeout the channel is closed and the connect rejects.\n * `Promise.race` keeps the losing request promise handled, so the\n * connection-closed rejection that follows `close()` can't surface as an\n * unhandled rejection.\n */\n let helloTimer: ReturnType<typeof setTimeout> | undefined\n try {\n return (await Promise.race([\n channel.rpc.sendRequest('ipc.hello', {\n protocolVersion: IPC_PROTOCOL_VERSION,\n minProtocolVersion: IPC_MIN_PROTOCOL_VERSION,\n appVersion: options.appVersion ?? 'dev',\n pid: process.pid,\n // Build fingerprint: a daemon started from an OLDER build of this\n // same protocol version rejects us, gets drained, and respawns —\n // dev daemons cannot run stale code silently. Clients that\n // cannot act on a build mismatch send the 'unknown' sentinel\n // instead (see `buildFence`): fencing them would just lock them\n // out until someone else retires the host. They stay fenced by\n // protocol version and degrade via `optional()` like any client of\n // an older daemon.\n buildId: options.buildFence ? IPC_BUILD_ID : 'unknown',\n // Always sent, passive clients included: a stale-RELEASE daemon is\n // safe for anyone to retire (see `refusedHello`).\n codeVersion: ZIDANE_VERSION,\n }),\n new Promise<never>((_, reject) => {\n helloTimer = setTimeout(\n () => reject(new Error(`Daemon handshake timed out after ${HELLO_TIMEOUT_MS}ms on ${channel.where}.`)),\n HELLO_TIMEOUT_MS,\n )\n }),\n ])) as HelloResult\n }\n catch (err) {\n if (err instanceof JsonRpcRemoteError && err.code === IpcErrorCode.VersionMismatch)\n throw await refusedHello(channel, err, options.drain)\n channel.close()\n throw err\n }\n finally {\n clearTimeout(helloTimer)\n }\n}\n\n/** Handshake over `channel`, then the typed client around it. On failure the channel is closed. */\nexport async function openIpcClient(\n channel: ClientChannel,\n listeners: Set<SignalListener>,\n options: ClientCoreOptions,\n): Promise<IpcClient> {\n const hello = await handshake(channel, options)\n\n const release = hostRelease(hello)\n // A `retiring` daemon is fence-aware and already exits once idle; staying\n // attached until then keeps its live runs reachable from this window. A\n // network server is never retired for a client: an older one is attached\n // to, and this client degrades (`optional()`) like an older client does.\n if (!hello.retiring && hello.hostKind !== 'server' && release !== undefined && compareVersions(release, ZIDANE_VERSION) < 0) {\n // Pre-fence daemons accept hello without checking codeVersion. Disconnect\n // only: shutting down a successfully attached daemon can abort active runs.\n channel.close()\n throw new IpcVersionMismatchError(\n `Daemon release ${release} is older than client ${ZIDANE_VERSION}. Restart the daemon when active runs have finished.`,\n hello,\n )\n }\n\n // Env override is a test seam (process-level e2e exercises the real\n // deadline without waiting 30 s); undocumented, not a user setting.\n const requestTimeoutMs = Number(process.env.ZIDANE_IPC_REQUEST_TIMEOUT_MS) || 30_000\n\n const stopHeartbeat = options.heartbeatMs ? startHeartbeat(channel, options.heartbeatMs) : () => undefined\n\n return {\n hello,\n notify: (method, params) => {\n try {\n channel.rpc.sendNotification(method, params)\n }\n catch {\n // fire-and-forget by contract — a dying connection surfaces via onClose\n }\n },\n request: async (method, params) => {\n /*\n * Per-request deadline: close rejection covers a DEAD host, but a\n * wedged one (alive, not reading — the same class the server-side\n * backpressure bound handles for peers) would hang callers forever.\n * On timeout the connection is aborted (NOT `close()` — the `onClose`\n * path must fire so reconnect logic takes over) and every pending\n * request rejects via the close sweep. Known long-running RPCs get a\n * generous budget; tearing the connection down mid-`sessions.compact`\n * would abort real work, so those bounds are deliberately loose.\n */\n // A machine call lasts as long as its command (`docs/MACHINES.md`); a dead host is the heartbeat's to catch.\n if (method === 'machine.call')\n return channel.rpc.sendRequest(method, params) as Promise<IpcResult<typeof method>>\n const timeoutMs = LONG_REQUEST_METHODS.has(method) ? LONG_REQUEST_TIMEOUT_MS : requestTimeoutMs\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return (await Promise.race([\n channel.rpc.sendRequest(method, params),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Daemon request ${method} timed out after ${timeoutMs}ms, closing the connection.`))\n channel.abort()\n }, timeoutMs)\n timer.unref?.()\n }),\n ])) as IpcResult<typeof method>\n }\n finally {\n clearTimeout(timer)\n }\n },\n onSignal: (cb) => {\n listeners.add(cb)\n return () => listeners.delete(cb)\n },\n close: () => {\n stopHeartbeat()\n channel.close()\n },\n }\n}\n","/** Stable hashes of sync values: equal values (whatever their key order) hash the same, on both sides. */\nimport { createHash } from 'node:crypto'\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** JSON with sorted keys (undefined members dropped). */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value))\n return `[${value.map(stableStringify).join(',')}]`\n if (isRecord(value))\n return `{${Object.keys(value).sort().filter(k => value[k] !== undefined).map(k => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(',')}}`\n return JSON.stringify(value) ?? 'undefined'\n}\n\nexport function hashValue(value: unknown): string {\n return createHash('sha256').update(stableStringify(value)).digest('hex')\n}\n","/** Which settings the `settings` sync category carries (`./types`). */\nimport type { Settings } from '../types'\nimport { HOST_ENABLED_LISTS, isWindowOnlySetting } from '../host-scoped-settings'\n\n/**\n * Host-scoped settings that still don't sync: they describe this computer\n * (its environments, its daemon, remote hosts on it) or would turn off what\n * the server has that this computer doesn't (the enabled allowlists, its OS\n * sandbox). Every `sandbox*` key, present and future, is the server's own:\n * a client turning it off there would be a sandbox escape.\n */\nconst NOT_SYNCED_SETTINGS: ReadonlySet<string> = new Set<string>(['environments', 'useDaemon', 'remoteHosts', ...HOST_ENABLED_LISTS])\n\n// The same legacy `safeMode` mapping as the live settings wire (`settings.get` / `settings.set`).\nexport { settingsFromWire, settingsToWire } from '../host-scoped-settings'\n\n/** A setting the `settings` category carries: host-scoped (never window-only) and not machine-specific. */\nexport function isSyncableSetting(key: keyof Settings | string): boolean {\n return !isWindowOnlySetting(key) && !NOT_SYNCED_SETTINGS.has(key) && !key.startsWith('sandbox')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"settings-keys-DDrfnBHi.js","names":[],"sources":["../src/ipc/daemon/lifecycle.ts","../src/ipc/transports/client-core.ts","../src/chat/sync/hash.ts","../src/chat/sync/settings-keys.ts"],"sourcesContent":["/**\n * Daemon lifecycle: socket-path resolution, liveness probing, stale-socket\n * cleanup, and spawn-on-demand.\n *\n * The socket lives in a dedicated daemon subdir of the resolved state dir\n * (`~/.zidane/daemon/daemon.sock` by default, honoring the same\n * `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX` env knobs every other surface\n * uses), overridable via `ZIDANE_DAEMON_SOCKET` for parallel installs and\n * tests.\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync, readdirSync, statSync } from 'node:fs'\nimport { connect } from 'node:net'\nimport { basename, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { resolveStorageDirs } from '../../chat/xdg'\n\n/**\n * Build fingerprint for the handshake. Running from source it is the newest\n * mtime under the `src/` tree at import time. Bundled, it is the mtime of this\n * module's own file. Client and daemon load the same files from disk, so a\n * daemon spawned before a rebuild (or a source edit in dev) reports a\n * different id than a freshly-started client, and the client drains +\n * respawns it. This closes the \"same protocol version, different code\"\n * gap that a hand-bumped version constant can't: during development the\n * daemon process is frozen at spawn while the tree keeps moving.\n *\n * Compiled binaries (`/$bunfs/…` virtual paths) can't be stat'ed and\n * report `'unknown'`; the check is skipped when EITHER side is unknown,\n * so released builds rely on `IPC_PROTOCOL_VERSION` alone — correct,\n * since their code only changes when the version does.\n */\nexport const IPC_BUILD_ID: string = (() => {\n try {\n const selfPath = fileURLToPath(import.meta.url)\n /*\n * Dev (running from source): fingerprint the whole source tree, not\n * just this file — a change to server.ts / engine.ts / runtime code\n * with no edit to lifecycle.ts must still retire an old daemon. Walk\n * `src/` and take the newest mtime. When the tree isn't present\n * (bundled dist, compiled binary) we fall back to this module's own\n * file mtime; in a bundle every ipc source collapses into one chunk, so\n * that single mtime already moves on any ipc change.\n */\n /*\n * Root validation is load-bearing: from a built bundle this module\n * lives flat in `dist/`, so the two-levels-up guess would be the\n * PACKAGE root — walking it would synchronously stat the whole repo\n * (node_modules, .git) at import time and fingerprint unrelated\n * churn. Only walk when the candidate directory demonstrably IS the\n * ipc source tree; a bundle falls back to this chunk's own mtime,\n * which already moves on every rebuild.\n */\n const ipcRoot = dirname(dirname(selfPath))\n if (!existsSync(join(ipcRoot, 'protocol.ts')))\n return String(Math.floor(statSync(selfPath).mtimeMs))\n\n /*\n * Dev scope: walk the whole `src/` tree, not just `src/ipc` — daemon\n * behavior also lives in agent.ts, loop.ts, and chat/** (store,\n * replay, discovery), and an edit there must retire a running dev\n * daemon just as surely as an ipc edit (the spawn-marker incident\n * lived in chat/store.ts). A few hundred stats — milliseconds.\n */\n const srcRoot = dirname(ipcRoot)\n let newest = 0\n const walk = (dir: string): void => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory())\n walk(full)\n else\n newest = Math.max(newest, statSync(full).mtimeMs)\n }\n }\n try {\n walk(srcRoot)\n }\n catch {\n newest = statSync(selfPath).mtimeMs\n }\n return String(Math.floor(newest))\n }\n catch {\n return 'unknown'\n }\n})()\n\n/**\n * Socket file name. Dev builds (`ZIDANE_DEV=1`, set by every `dev` script\n * in the repo) get their OWN socket so a checkout's TUI, GUI and `zidane\n * daemon` pair up among themselves and never attach to — or get refused by\n * — the installed daemon. Without this, a protocol bump in the working\n * tree turned the dev GUI's dot red against the stable daemon, and a dev\n * daemon could drain the one the installed TUI relies on.\n * @param env - Environment to read `ZIDANE_DEV` from.\n * @returns `dev.sock` for a dev build, else `daemon.sock`.\n */\nexport function daemonSocketName(env: NodeJS.ProcessEnv = process.env): 'daemon.sock' | 'dev.sock' {\n return env.ZIDANE_DEV && env.ZIDANE_DEV !== '0' ? 'dev.sock' : 'daemon.sock'\n}\n\n/**\n * Path of the daemon socket this process uses.\n * The `ZIDANE_DAEMON_SOCKET` environment variable wins. Otherwise it is `<stateDir>/daemon/<socket name>`, see {@link daemonSocketName}.\n * @returns The socket path.\n */\nexport function defaultSocketPath(): string {\n const explicit = process.env.ZIDANE_DAEMON_SOCKET\n if (explicit)\n return explicit\n /*\n * The socket is transient runtime state, so it lands under the resolved\n * state dir — XDG-aware on XDG installs, `<storageDir>/<prefix>` in\n * legacy mode. Uses the same resolver every other surface reads through,\n * honoring `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX`.\n *\n * DEDICATED `daemon/` subdir: the state dir is shared with\n * `state.json` / logs, so the daemon can't chmod it 0700 without\n * affecting unrelated files. The server hardens this subdir instead;\n * client and server must agree on this path.\n */\n const dirs = resolveStorageDirs({\n ...(process.env.ZIDANE_PREFIX !== undefined ? { prefix: process.env.ZIDANE_PREFIX } : {}),\n })\n return join(dirs.stateDir, 'daemon', daemonSocketName())\n}\n\n/**\n * Legacy socket location (`<stateDir>/daemon.sock`) for the given socket path,\n * or null when the path isn't the default `<stateDir>/daemon/daemon.sock`\n * layout (explicit `ZIDANE_DAEMON_SOCKET` users never had a migration to run).\n * A daemon still serving the old path would be ORPHANED by the move — new clients\n * never probe it, and a service-style `--idle-exit 0` daemon would hold\n * agents and run-guard markers against the same sessions.db forever —\n * so daemon startup probes this path and drains any survivor.\n */\nexport function legacySocketPath(socketPath: string): string | null {\n const dir = dirname(socketPath)\n if (basename(socketPath) !== 'daemon.sock' || basename(dir) !== 'daemon')\n return null\n return join(dirname(dir), 'daemon.sock')\n}\n\n/**\n * Spawn command that re-enters the CURRENT zidane program as `… daemon`.\n *\n * Three launch shapes:\n * - compiled Bun binary — `Bun.main` is the virtual `/$bunfs/…` entry the\n * binary self-inserts as argv[1], so the spawn must NOT repeat it\n * (verified: repeating it makes the child parse the junk path as its\n * subcommand and boot the full TUI instead of the daemon).\n * - dev (`bun run cli.ts`) — the script path must ride along.\n * - Node hosts (no `Bun` global) — fall back to the published bin.\n */\nexport function selfDaemonSpawnCommand(): string[] {\n const bunMain = (globalThis as { Bun?: { main?: string } }).Bun?.main\n // Compiled binary: the virtual entry parses its own `daemon` subcommand.\n if (bunMain?.startsWith('/$bunfs'))\n return [process.execPath, 'daemon']\n // Dev-from-source: only re-execute `Bun.main` when it's actually a\n // zidane CLI entry (`cli.ts`/`cli.js`). For any other embedder (library\n // consumer, script) re-running their entry with a `daemon` argv would\n // launch an arbitrary program — fall back to the published bin instead.\n if (bunMain && /(?:^|[/\\\\])cli\\.[cm]?[jt]s$/.test(bunMain))\n return [process.execPath, bunMain, 'daemon']\n return ['zidane-daemon']\n}\n\n/**\n * True when something accepts connections on `socketPath`. Distinguishes a\n * live daemon from a stale socket file left by a crashed one.\n * @param socketPath - Path of the socket to try.\n * @param timeoutMs - How long to wait for the connection, in milliseconds.\n */\nexport function probeSocket(socketPath: string, timeoutMs = 1000): Promise<boolean> {\n return new Promise((resolve) => {\n /*\n * Skip the connect entirely unless the path is an actual socket file.\n * A stale REGULAR file (crashed daemon) or a missing path both mean\n * \"nothing listening\" — and connecting to them can surface a noisy\n * async ENOENT/ENOTSOCK that some runtimes treat as uncaught.\n */\n try {\n if (!existsSync(socketPath) || !statSync(socketPath).isSocket()) {\n resolve(false)\n return\n }\n }\n catch {\n resolve(false)\n return\n }\n\n let socket: ReturnType<typeof connect>\n try {\n socket = connect(socketPath)\n }\n catch {\n resolve(false)\n return\n }\n const done = (alive: boolean): void => {\n socket.destroy()\n resolve(alive)\n }\n socket.once('connect', () => done(true))\n socket.once('error', () => done(false))\n socket.setTimeout(timeoutMs, () => done(false))\n })\n}\n\n/** Options for {@link ensureDaemonRunning}. */\nexport interface EnsureDaemonOptions {\n /**\n * Socket the daemon listens on.\n * @default defaultSocketPath()\n */\n socketPath?: string\n /**\n * Command used to spawn the daemon when nothing is listening. Defaults to\n * `['zidane-daemon']` (the published bin). The child is detached and\n * unref'd — it outlives the spawning client by design.\n */\n spawnCommand?: string[]\n /** Total time to wait for the daemon to accept connections. Default 8000 ms. */\n timeoutMs?: number\n}\n\n/** Daemon starts in flight, by socket path, so concurrent callers share one. */\nconst pendingStarts = new Map<string, Promise<string>>()\n\n/**\n * Makes sure a daemon is listening on the socket, spawning one when nothing is. Concurrent calls for the same socket share one start.\n * Rejects when the spawn fails or the daemon does not accept connections within `timeoutMs`.\n * @param options - Socket, spawn command and timeout.\n * @returns The socket path once a connection succeeds.\n */\nexport function ensureDaemonRunning(options: EnsureDaemonOptions = {}): Promise<string> {\n const socketPath = options.socketPath ?? defaultSocketPath()\n const pending = pendingStarts.get(socketPath)\n if (pending)\n return pending\n const start = startDaemon({ ...options, socketPath }).finally(() => pendingStarts.delete(socketPath))\n pendingStarts.set(socketPath, start)\n return start\n}\n\nasync function startDaemon(options: EnsureDaemonOptions & { socketPath: string }): Promise<string> {\n const socketPath = options.socketPath\n if (await probeSocket(socketPath))\n return socketPath\n\n const [cmd, ...args] = options.spawnCommand ?? ['zidane-daemon']\n const child = spawn(cmd!, args, {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, ZIDANE_DAEMON_SOCKET: socketPath },\n })\n /*\n * A missing binary (e.g. `zidane-daemon` not on PATH) emits an async\n * `error` event; without a listener Node rethrows it as an uncaught\n * exception and crashes the caller. Capture it and surface a clean\n * rejection through the poll loop below.\n */\n let spawnError: Error | undefined\n child.once('error', (err: Error) => {\n spawnError = err\n })\n child.unref()\n\n const deadline = Date.now() + (options.timeoutMs ?? 8000)\n for (;;) {\n if (spawnError !== undefined)\n throw new Error(`Failed to spawn zidane daemon (${cmd}): ${spawnError.message}`)\n if (await probeSocket(socketPath))\n return socketPath\n if (Date.now() > deadline)\n throw new Error(`zidane daemon did not come up on ${socketPath} within ${options.timeoutMs ?? 8000}ms.`)\n await new Promise(r => setTimeout(r, 150))\n }\n}\n","/**\n * The client side every wire transport shares (the unix socket in\n * `../client.ts`, the WebSocket in `./ws-client.ts`): the `ipc.hello`\n * handshake with its version fences, typed requests with deadlines,\n * notifications, and signal fan-out. A transport supplies a connected\n * JSON-RPC {@link ClientChannel}; this turns it into an {@link IpcClient}.\n */\nimport type { AcpPeer, JsonRpcMessage } from '../../acp/types'\nimport type { HelloResult, HostSignal, IpcMethodName, IpcParams, IpcResult, SignalMeta } from '../protocol'\nimport { JsonRpcConnectionClosedError, JsonRpcRemoteError } from '../../acp/json-rpc'\nimport { compareVersions, ZIDANE_VERSION } from '../../version'\nimport { IPC_BUILD_ID } from '../daemon/lifecycle'\nimport { IPC_MIN_PROTOCOL_VERSION, IPC_PROTOCOL_VERSION, IpcErrorCode, PING_METHOD, SIGNAL_SEQ_FIELD } from '../protocol'\n\n/** A connected host client, shared by every transport. Requests are checked against {@link IpcMethods}. */\nexport interface IpcClient {\n /** The host's answer to the `ipc.hello` handshake. */\n hello: HelloResult\n /**\n * Calls a host method and resolves with its result. Rejects with a `JsonRpcRemoteError` when the host answers with an error, or with `JsonRpcConnectionClosedError` when the connection closes first.\n * A request not answered within its deadline (30 s, longer for slow methods) rejects and drops the connection. `machine.call` has no deadline.\n */\n request: <M extends IpcMethodName>(method: M, params: IpcParams<M>) => Promise<IpcResult<M>>\n /**\n * Fire-and-forget notification — same method table as `request`, but no\n * id, no response, no pending-promise bookkeeping. The hot path for\n * high-frequency writes (per-keystroke draft sync). Daemons that don't\n * know the method ignore it silently.\n */\n notify: <M extends IpcMethodName>(method: M, params: IpcParams<M>) => void\n /** Host signals, with their number in their session when they have one (`SignalMeta`). */\n onSignal: (cb: SignalListener) => () => void\n /** Closes the connection. Pending requests reject. */\n close: () => void\n}\n\nexport type SignalListener = (signal: HostSignal, meta?: SignalMeta) => void\n\n/** Thrown when the host refuses the handshake because its protocol, build or code version does not match the client's. */\nexport class IpcVersionMismatchError extends Error {\n /**\n * @param message - Description of the mismatch.\n * @param daemon - The host's handshake data, when it sent any.\n */\n constructor(message: string, readonly daemon?: HelloResult) {\n super(message)\n this.name = 'IpcVersionMismatchError'\n }\n}\n\n/** A connected JSON-RPC link to a host, as a transport hands it over. */\nexport interface ClientChannel {\n rpc: Pick<AcpPeer, 'sendRequest' | 'sendNotification'>\n /** The caller's `close()`: quiet (no `onClose`), fails what is in flight. Idempotent. */\n close: () => void\n /** Drop a wedged connection: like the host going away, so the transport's `onClose` fires. */\n abort: () => void\n /** Where the host is, for error messages (a socket path, a URL). */\n where: string\n}\n\nexport interface ClientCoreOptions {\n appVersion?: string\n /**\n * When the host refuses the handshake on a version / build mismatch:\n * `always` asks it to drain so a matching one can be spawned (session\n * controllers on the local daemon); `stale-only` only when the host is\n * provably older (passive local clients); `never` leaves it running (a\n * remote server's lifetime is not the client's to end).\n */\n drain: 'always' | 'stale-only' | 'never'\n /**\n * Send this build's fingerprint, so a host from an older build of the same\n * protocol refuses us. Off sends the `'unknown'` sentinel: clients that\n * can't act on a build mismatch (no drain, no respawn) opt out of that fence.\n */\n buildFence: boolean\n /**\n * Ping the host (`$/ping`) this often once attached; two pings in a row\n * left unanswered at the next tick abort the connection (the transport's\n * `onClose` fires, its owner reconnects). Off when absent or `0`: a\n * local socket learns of a dead host from the kernel.\n */\n heartbeatMs?: number\n}\n\n/** Missed heartbeats after which the host is considered gone. */\nexport const HEARTBEAT_MISSES = 2\n\n/** Starts the client heartbeat on `channel`; returns its stop. Stops itself once the channel closes. */\nfunction startHeartbeat(channel: ClientChannel, intervalMs: number): () => void {\n let awaiting = false\n let missed = 0\n const timer = setInterval(() => {\n if (awaiting && ++missed >= HEARTBEAT_MISSES) {\n clearInterval(timer)\n channel.abort()\n return\n }\n awaiting = true\n channel.rpc.sendRequest(PING_METHOD, {}).then(\n () => {\n awaiting = false\n missed = 0\n },\n (err: unknown) => {\n if (err instanceof JsonRpcConnectionClosedError) {\n clearInterval(timer)\n return\n }\n // Any answer, an error included (a host predating `$/ping`), proves it alive.\n awaiting = false\n missed = 0\n },\n )\n }, intervalMs)\n timer.unref?.()\n return () => clearInterval(timer)\n}\n\n/** The signal listeners of one client, and the JSON-RPC notification handler that feeds them. */\nexport function createSignalListeners() {\n const listeners = new Set<SignalListener>()\n return {\n listeners,\n onNotification(message: JsonRpcMessage & { method: string }): void {\n if (message.method !== 'ipc.signal')\n return\n const params = (message as { params?: HostSignal & { [SIGNAL_SEQ_FIELD]?: unknown } }).params\n if (!params)\n return\n const { [SIGNAL_SEQ_FIELD]: seq, ...signal } = params\n const meta = typeof seq === 'number' ? { seq } : undefined\n for (const cb of listeners)\n cb(signal as HostSignal, meta)\n },\n }\n}\n\nfunction hostRelease(hello: HelloResult): string | undefined {\n for (const value of [hello.codeVersion, hello.appVersion]) {\n if (typeof value !== 'string')\n continue\n const match = /^(?:zidane-gui\\/)?(v?\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)$/.exec(value.trim())\n if (match)\n return match[1]\n }\n return undefined\n}\n\nconst HELLO_TIMEOUT_MS = 5000\nconst LONG_REQUEST_TIMEOUT_MS = 10 * 60 * 1000\n/**\n * RPCs that legitimately run long: full compaction, index-backed cross-session search, a clone, a folder search,\n * and extension code (an RPC method, a command, a UI callback, a sign-in that waits for the user).\n */\nconst LONG_REQUEST_METHODS = new Set<string>(['sessions.compact', 'history.searchAll', 'history.searchSession', 'git.clone', 'fs.searchDirs', 'rpc.call', 'extensions.command.run', 'extensions.ui.invoke', 'extensions.integrations.start', 'extensions.ui.list', 'extensions.entries'])\n\n/**\n * A refused handshake. Asks the host to drain when `drain` allows it, closes\n * the channel, and returns the error to throw.\n */\nasync function refusedHello(channel: ClientChannel, err: JsonRpcRemoteError, drain: ClientCoreOptions['drain']): Promise<IpcVersionMismatchError> {\n /*\n * Ask the stale daemon to drain so the caller can respawn a matching\n * one. AWAIT the reply (bounded) before tearing the connection down —\n * closing discards unflushed writes, so a fire-and-forget\n * `daemon.shutdown` could be dropped, leaving the stale daemon alive\n * and defeating `connectOrSpawnDaemon`'s retry.\n */\n const daemon = (err.data as { daemon?: HelloResult } | undefined)?.daemon\n // A network server's lifetime is never a client's to end, whatever `drain` says.\n if (daemon?.hostKind === 'server') {\n channel.close()\n return new IpcVersionMismatchError(serverMismatchMessage(daemon, channel.where) ?? err.message, daemon)\n }\n /*\n * Passive clients (`stale-only`) never retire a daemon on a protocol /\n * build mismatch — it may be NEWER than them. A daemon that is provably\n * OLDER — an older release, or the same release speaking an older\n * protocol (a dev build from before a protocol bump) — is the case where\n * draining is always safe: nothing newer can be running on it, and\n * whoever attaches next spawns a current one. Without this a passive\n * client (the GUI) would sit in-process forever behind a stale daemon\n * nobody restarts: every retry is refused.\n */\n const daemonIsOlderRelease\n = typeof daemon?.codeVersion === 'string' && compareVersions(daemon.codeVersion, ZIDANE_VERSION) < 0\n const release = daemon && hostRelease(daemon)\n const daemonIsNewerRelease = release !== undefined && compareVersions(release, ZIDANE_VERSION) > 0\n const daemonSpeaksOlderProtocol = typeof daemon?.protocolVersion === 'number' && daemon.protocolVersion < IPC_PROTOCOL_VERSION\n const daemonIsStale = daemonIsOlderRelease || daemonSpeaksOlderProtocol\n if (drain !== 'never' && !daemonIsNewerRelease && (drain === 'always' || daemonIsStale)) {\n // `whenIdle`: a daemon with live runs defers its exit instead of\n // aborting them (older daemons ignore the flag and drain now).\n await Promise.race([\n channel.rpc.sendRequest('daemon.shutdown', { whenIdle: true }).catch(() => undefined),\n new Promise(resolve => setTimeout(resolve, 1500)),\n ])\n }\n channel.close()\n return new IpcVersionMismatchError(err.message, daemon)\n}\n\n/**\n * A server's protocol refusal, from this client's side: both versions and which side to upgrade.\n * `null` when the server's range (`minProtocolVersion`…`protocolVersion`) overlaps this client's.\n */\nfunction serverMismatchMessage(server: HelloResult, where: string): string | null {\n if (typeof server.protocolVersion !== 'number')\n return null\n const serverMin = typeof server.minProtocolVersion === 'number' ? server.minProtocolVersion : server.protocolVersion\n if (serverMin <= IPC_PROTOCOL_VERSION && server.protocolVersion >= IPC_MIN_PROTOCOL_VERSION)\n return null\n const upgrade = server.protocolVersion < IPC_PROTOCOL_VERSION ? 'server' : 'client'\n return `Protocol version mismatch with ${where}: the server speaks v${server.protocolVersion}, this client v${IPC_PROTOCOL_VERSION}. Upgrade the ${upgrade}.`\n}\n\nasync function handshake(channel: ClientChannel, options: ClientCoreOptions): Promise<HelloResult> {\n /*\n * Bound the handshake: a wedged daemon (accepting connections but never\n * reading — e.g. SIGSTOP'd) would otherwise hang this promise forever.\n * On timeout the channel is closed and the connect rejects.\n * `Promise.race` keeps the losing request promise handled, so the\n * connection-closed rejection that follows `close()` can't surface as an\n * unhandled rejection.\n */\n let helloTimer: ReturnType<typeof setTimeout> | undefined\n try {\n return (await Promise.race([\n channel.rpc.sendRequest('ipc.hello', {\n protocolVersion: IPC_PROTOCOL_VERSION,\n minProtocolVersion: IPC_MIN_PROTOCOL_VERSION,\n appVersion: options.appVersion ?? 'dev',\n pid: process.pid,\n // Build fingerprint: a daemon started from an OLDER build of this\n // same protocol version rejects us, gets drained, and respawns —\n // dev daemons cannot run stale code silently. Clients that\n // cannot act on a build mismatch send the 'unknown' sentinel\n // instead (see `buildFence`): fencing them would just lock them\n // out until someone else retires the host. They stay fenced by\n // protocol version and degrade via `optional()` like any client of\n // an older daemon.\n buildId: options.buildFence ? IPC_BUILD_ID : 'unknown',\n // Always sent, passive clients included: a stale-RELEASE daemon is\n // safe for anyone to retire (see `refusedHello`).\n codeVersion: ZIDANE_VERSION,\n }),\n new Promise<never>((_, reject) => {\n helloTimer = setTimeout(\n () => reject(new Error(`Daemon handshake timed out after ${HELLO_TIMEOUT_MS}ms on ${channel.where}.`)),\n HELLO_TIMEOUT_MS,\n )\n }),\n ])) as HelloResult\n }\n catch (err) {\n if (err instanceof JsonRpcRemoteError && err.code === IpcErrorCode.VersionMismatch)\n throw await refusedHello(channel, err, options.drain)\n channel.close()\n throw err\n }\n finally {\n clearTimeout(helloTimer)\n }\n}\n\n/** Handshake over `channel`, then the typed client around it. On failure the channel is closed. */\nexport async function openIpcClient(\n channel: ClientChannel,\n listeners: Set<SignalListener>,\n options: ClientCoreOptions,\n): Promise<IpcClient> {\n const hello = await handshake(channel, options)\n\n const release = hostRelease(hello)\n // A `retiring` daemon is fence-aware and already exits once idle; staying\n // attached until then keeps its live runs reachable from this window. A\n // network server is never retired for a client: an older one is attached\n // to, and this client degrades (`optional()`) like an older client does.\n if (!hello.retiring && hello.hostKind !== 'server' && release !== undefined && compareVersions(release, ZIDANE_VERSION) < 0) {\n // Pre-fence daemons accept hello without checking codeVersion. Disconnect\n // only: shutting down a successfully attached daemon can abort active runs.\n channel.close()\n throw new IpcVersionMismatchError(\n `Daemon release ${release} is older than client ${ZIDANE_VERSION}. Restart the daemon when active runs have finished.`,\n hello,\n )\n }\n\n // Env override is a test seam (process-level e2e exercises the real\n // deadline without waiting 30 s); undocumented, not a user setting.\n const requestTimeoutMs = Number(process.env.ZIDANE_IPC_REQUEST_TIMEOUT_MS) || 30_000\n\n const stopHeartbeat = options.heartbeatMs ? startHeartbeat(channel, options.heartbeatMs) : () => undefined\n\n return {\n hello,\n notify: (method, params) => {\n try {\n channel.rpc.sendNotification(method, params)\n }\n catch {\n // fire-and-forget by contract — a dying connection surfaces via onClose\n }\n },\n request: async (method, params) => {\n /*\n * Per-request deadline: close rejection covers a DEAD host, but a\n * wedged one (alive, not reading — the same class the server-side\n * backpressure bound handles for peers) would hang callers forever.\n * On timeout the connection is aborted (NOT `close()` — the `onClose`\n * path must fire so reconnect logic takes over) and every pending\n * request rejects via the close sweep. Known long-running RPCs get a\n * generous budget; tearing the connection down mid-`sessions.compact`\n * would abort real work, so those bounds are deliberately loose.\n */\n // A machine call lasts as long as its command (`docs/MACHINES.md`); a dead host is the heartbeat's to catch.\n if (method === 'machine.call')\n return channel.rpc.sendRequest(method, params) as Promise<IpcResult<typeof method>>\n const timeoutMs = LONG_REQUEST_METHODS.has(method) ? LONG_REQUEST_TIMEOUT_MS : requestTimeoutMs\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return (await Promise.race([\n channel.rpc.sendRequest(method, params),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Daemon request ${method} timed out after ${timeoutMs}ms, closing the connection.`))\n channel.abort()\n }, timeoutMs)\n timer.unref?.()\n }),\n ])) as IpcResult<typeof method>\n }\n finally {\n clearTimeout(timer)\n }\n },\n onSignal: (cb) => {\n listeners.add(cb)\n return () => listeners.delete(cb)\n },\n close: () => {\n stopHeartbeat()\n channel.close()\n },\n }\n}\n","/** Stable hashes of sync values: equal values (whatever their key order) hash the same, on both sides. */\nimport { createHash } from 'node:crypto'\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** JSON with sorted keys (undefined members dropped). */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value))\n return `[${value.map(stableStringify).join(',')}]`\n if (isRecord(value))\n return `{${Object.keys(value).sort().filter(k => value[k] !== undefined).map(k => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(',')}}`\n return JSON.stringify(value) ?? 'undefined'\n}\n\nexport function hashValue(value: unknown): string {\n return createHash('sha256').update(stableStringify(value)).digest('hex')\n}\n","/** Which settings the `settings` sync category carries (`./types`). */\nimport type { Settings } from '../types'\nimport { HOST_ENABLED_LISTS, isWindowOnlySetting } from '../host-scoped-settings'\n\n/**\n * Host-scoped settings that still don't sync: they describe this computer\n * (its environments, its daemon, remote hosts on it) or would turn off what\n * the server has that this computer doesn't (the enabled allowlists, its OS\n * sandbox). Every `sandbox*` key, present and future, is the server's own:\n * a client turning it off there would be a sandbox escape.\n */\nconst NOT_SYNCED_SETTINGS: ReadonlySet<string> = new Set<string>(['environments', 'useDaemon', 'remoteHosts', ...HOST_ENABLED_LISTS])\n\n// The same legacy `safeMode` mapping as the live settings wire (`settings.get` / `settings.set`).\nexport { settingsFromWire, settingsToWire } from '../host-scoped-settings'\n\n/** A setting the `settings` category carries: host-scoped (never window-only) and not machine-specific. */\nexport function isSyncableSetting(key: keyof Settings | string): boolean {\n return !isWindowOnlySetting(key) && !NOT_SYNCED_SETTINGS.has(key) && !key.startsWith('sandbox')\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as zidaneRangeFor } from "./host-version-
|
|
1
|
+
import { r as zidaneRangeFor } from "./host-version-BGZumnpZ.js";
|
|
2
2
|
import { t as defineSkill } from "./skills-BugCYhlH.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -955,4 +955,4 @@ function toolsNote(text, frozen, history) {
|
|
|
955
955
|
//#endregion
|
|
956
956
|
export { withZidaneSkillEditSkill as S, extensionScaffold as _, sectionDigest as a, validateExtensionName as b, EXTENSION_EDIT_METADATA_KEY as c, ZIDANE_SKILL_EDIT_SKILL_NAME as d, buildZidaneExtensionSkill as f, extensionDirForCwd as g, editSessionMetadata as h, latestSystemSnapshot as i, SKILL_EDIT_METADATA_KEY as l, createExtension as m, announcedDigest as n, sectionDigests as o, buildZidaneSkillEditSkill as p, catalogChangeNotes as r, systemSnapshotTurn as s, FROZEN_PROMPT_SECTION_KINDS as t, ZIDANE_EXTENSION_SKILL_NAME as u, readSkillEditMarker as v, withZidaneExtensionSkill as x, resolveExtensionDocsDir as y };
|
|
957
957
|
|
|
958
|
-
//# sourceMappingURL=system-freeze-
|
|
958
|
+
//# sourceMappingURL=system-freeze-CY4Hc9WX.js.map
|