zidane 8.2.3 → 8.2.5

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 (166) hide show
  1. package/dist/{acp-oYRjlh71.js → acp-BIInKhVI.js} +6 -6
  2. package/dist/{acp-oYRjlh71.js.map → acp-BIInKhVI.js.map} +1 -1
  3. package/dist/acp-cli.js +4 -4
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-CBQglXtH.js → agent-CmhCOmyO.js} +10 -5
  7. package/dist/{agent-CBQglXtH.js.map → agent-CmhCOmyO.js.map} +1 -1
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/chat/pure.d.ts +4 -4
  11. package/dist/chat/pure.js +7 -7
  12. package/dist/chat.d.ts +3 -3
  13. package/dist/chat.js +16 -16
  14. package/dist/{cli-shared-E5m_pI8A.js → cli-shared-GOehTeqk.js} +2 -2
  15. package/dist/{cli-shared-E5m_pI8A.js.map → cli-shared-GOehTeqk.js.map} +1 -1
  16. package/dist/{client-core-EEEX_u6D.d.ts → client-core-Dz8hELs4.d.ts} +15 -5
  17. package/dist/client-core-Dz8hELs4.d.ts.map +1 -0
  18. package/dist/codemode-worker.js +1 -1
  19. package/dist/codemode.js +1 -1
  20. package/dist/{connection-loop-CpgfUmuw.d.ts → connection-loop-dm_TN-QD.d.ts} +3 -3
  21. package/dist/{connection-loop-CpgfUmuw.d.ts.map → connection-loop-dm_TN-QD.d.ts.map} +1 -1
  22. package/dist/contexts/daytona.d.ts +1 -1
  23. package/dist/contexts/e2b.d.ts +1 -1
  24. package/dist/{credentials-B9DfBJP9.js → credentials-DxOQmset.js} +2 -2
  25. package/dist/{credentials-B9DfBJP9.js.map → credentials-DxOQmset.js.map} +1 -1
  26. package/dist/daemon-cli.js +2 -2
  27. package/dist/{dispatcher-Bv9-c6I2.js → dispatcher-D_9_QFO3.js} +22 -18
  28. package/dist/dispatcher-D_9_QFO3.js.map +1 -0
  29. package/dist/{engine-XmoPPB6U.js → engine-BsWBWgTB.js} +6 -6
  30. package/dist/{engine-XmoPPB6U.js.map → engine-BsWBWgTB.js.map} +1 -1
  31. package/dist/{eval-CfQlBR81.js → eval-CIHIF_Ns.js} +3 -3
  32. package/dist/{eval-CfQlBR81.js.map → eval-CIHIF_Ns.js.map} +1 -1
  33. package/dist/eval.d.ts +1 -1
  34. package/dist/eval.js +1 -1
  35. package/dist/{executor-BhthPE1q.js → executor-BSesPPcU.js} +3 -3
  36. package/dist/{executor-BhthPE1q.js.map → executor-BSesPPcU.js.map} +1 -1
  37. package/dist/{extensions-BgBwogRW.js → extensions-BJof-2mi.js} +3 -3
  38. package/dist/{extensions-BgBwogRW.js.map → extensions-BJof-2mi.js.map} +1 -1
  39. package/dist/extensions.d.ts +1 -1
  40. package/dist/extensions.js +1 -1
  41. package/dist/{fold-rules-DSypi36k.d.ts → fold-rules-CMpWNPqk.d.ts} +2 -2
  42. package/dist/{fold-rules-DSypi36k.d.ts.map → fold-rules-CMpWNPqk.d.ts.map} +1 -1
  43. package/dist/{headless-R4pbpTkm.js → headless-Bs9Lb4vs.js} +4 -4
  44. package/dist/{headless-R4pbpTkm.js.map → headless-Bs9Lb4vs.js.map} +1 -1
  45. package/dist/headless.d.ts +1 -1
  46. package/dist/headless.js +1 -1
  47. package/dist/history-worker.js +1 -1
  48. package/dist/{host-scoped-settings-C-Nmb6vr.js → host-scoped-settings-DxxI_yRo.js} +2 -2
  49. package/dist/{host-scoped-settings-C-Nmb6vr.js.map → host-scoped-settings-DxxI_yRo.js.map} +1 -1
  50. package/dist/{host-signals-BFudPyCT.js → host-signals-CUKPZZxC.js} +5 -5
  51. package/dist/{host-signals-BFudPyCT.js.map → host-signals-CUKPZZxC.js.map} +1 -1
  52. package/dist/{index-DyQyDLX1.d.ts → index-4mDRyTGJ.d.ts} +1722 -1691
  53. package/dist/index-4mDRyTGJ.d.ts.map +1 -0
  54. package/dist/{index-CDS7YdUC.d.ts → index-Dlmu8HnG.d.ts} +5 -5
  55. package/dist/{index-CDS7YdUC.d.ts.map → index-Dlmu8HnG.d.ts.map} +1 -1
  56. package/dist/{index-AqRZ3gEz.d.ts → index-QsAN9yjZ.d.ts} +3 -3
  57. package/dist/{index-AqRZ3gEz.d.ts.map → index-QsAN9yjZ.d.ts.map} +1 -1
  58. package/dist/index.d.ts +3 -3
  59. package/dist/index.js +8 -8
  60. package/dist/ipc/remote.d.ts +5 -5
  61. package/dist/ipc/remote.js +3 -3
  62. package/dist/ipc.d.ts +5 -5
  63. package/dist/ipc.js +9 -9
  64. package/dist/{links-rFaWqS2Y.js → links-Bto-5dVu.js} +10 -10
  65. package/dist/{links-rFaWqS2Y.js.map → links-Bto-5dVu.js.map} +1 -1
  66. package/dist/{mcp-allowlist-CLXweyxk.js → mcp-allowlist-BbnTBKNN.js} +2 -2
  67. package/dist/{mcp-allowlist-CLXweyxk.js.map → mcp-allowlist-BbnTBKNN.js.map} +1 -1
  68. package/dist/mcp.d.ts +1 -1
  69. package/dist/{oauth-7_48zR7n.js → oauth-BwPiX0hL.js} +66 -32
  70. package/dist/oauth-BwPiX0hL.js.map +1 -0
  71. package/dist/output/stream-json.d.ts +1 -1
  72. package/dist/output/terminal.d.ts +1 -1
  73. package/dist/plugins.d.ts +1 -1
  74. package/dist/{prelude-HzY_JEGq.js → prelude-qjPUkw4f.js} +26 -2
  75. package/dist/prelude-qjPUkw4f.js.map +1 -0
  76. package/dist/{presets-DXeHsd24.js → presets-C4hkykN-.js} +2 -2
  77. package/dist/{presets-DXeHsd24.js.map → presets-C4hkykN-.js.map} +1 -1
  78. package/dist/presets.d.ts +1 -1
  79. package/dist/presets.js +1 -1
  80. package/dist/{protocol-BIv1Yuvl.js → protocol-Do_i6Ras.js} +3 -1
  81. package/dist/protocol-Do_i6Ras.js.map +1 -0
  82. package/dist/providers/anthropic.d.ts +1 -1
  83. package/dist/providers/arcee.d.ts +1 -1
  84. package/dist/providers/baseten.d.ts +1 -1
  85. package/dist/providers/cerebras.d.ts +1 -1
  86. package/dist/providers/google.d.ts +1 -1
  87. package/dist/providers/local.d.ts +1 -1
  88. package/dist/providers/neuralwatt.d.ts +1 -1
  89. package/dist/providers/openai-compat.d.ts +1 -1
  90. package/dist/providers/openai.d.ts +1 -1
  91. package/dist/providers/openrouter.d.ts +1 -1
  92. package/dist/providers/xai.d.ts +1 -1
  93. package/dist/providers.d.ts +1 -1
  94. package/dist/{registry-eourRLY2.js → registry-DxPP_tAt.js} +3 -3
  95. package/dist/{registry-eourRLY2.js.map → registry-DxPP_tAt.js.map} +1 -1
  96. package/dist/{remote-hosts-DY2J07ut.js → remote-hosts-DcCh7fqp.js} +4 -4
  97. package/dist/{remote-hosts-DY2J07ut.js.map → remote-hosts-DcCh7fqp.js.map} +1 -1
  98. package/dist/restate.d.ts +1 -1
  99. package/dist/{secret-shapes-BjW4l6U0.js → secret-shapes-HvaYVzwc.js} +10 -2
  100. package/dist/secret-shapes-HvaYVzwc.js.map +1 -0
  101. package/dist/session/sqlite.d.ts +1 -1
  102. package/dist/{session-7TUHs42_.js → session-CCsKXID6.js} +6 -2
  103. package/dist/session-CCsKXID6.js.map +1 -0
  104. package/dist/{session-factory-CMwIXE-7.js → session-factory-Ct7J9CK1.js} +6 -6
  105. package/dist/{session-factory-CMwIXE-7.js.map → session-factory-Ct7J9CK1.js.map} +1 -1
  106. package/dist/session.d.ts +2 -2
  107. package/dist/session.js +1 -1
  108. package/dist/{settings-keys-CPdmkTz7.js → settings-keys-CnrJgT1T.js} +4 -4
  109. package/dist/{settings-keys-CPdmkTz7.js.map → settings-keys-CnrJgT1T.js.map} +1 -1
  110. package/dist/{shell-path-B8b3EOtK.js → shell-path-rJDa0OMv.js} +4 -4
  111. package/dist/{shell-path-B8b3EOtK.js.map → shell-path-rJDa0OMv.js.map} +1 -1
  112. package/dist/skills.d.ts +1 -1
  113. package/dist/{spaces-CyjMh5Zf.js → spaces-C_x2bvEl.js} +14 -4
  114. package/dist/spaces-C_x2bvEl.js.map +1 -0
  115. package/dist/{store-L01W0gGI.js → store-BpkWIaSH.js} +10 -30
  116. package/dist/store-BpkWIaSH.js.map +1 -0
  117. package/dist/{todo-edits-BERpCRPj.js → todo-edits-DKoxu7Ss.js} +3 -3
  118. package/dist/{todo-edits-BERpCRPj.js.map → todo-edits-DKoxu7Ss.js.map} +1 -1
  119. package/dist/tools/fetch-url.d.ts +1 -1
  120. package/dist/tools/web-search.d.ts +1 -1
  121. package/dist/{tools-pZVFIJ_K.js → tools-FHEIKGkh.js} +2 -2
  122. package/dist/{tools-pZVFIJ_K.js.map → tools-FHEIKGkh.js.map} +1 -1
  123. package/dist/tools.d.ts +1 -1
  124. package/dist/tools.js +2 -2
  125. package/dist/{transcript-anchors-jHrg-r-H.js → transcript-anchors-BcFLh3ZA.js} +10 -10
  126. package/dist/{transcript-anchors-jHrg-r-H.js.map → transcript-anchors-BcFLh3ZA.js.map} +1 -1
  127. package/dist/{transcript-cache-DXdgVuoy.js → transcript-cache-CdHKtx9y.js} +4 -4
  128. package/dist/{transcript-cache-DXdgVuoy.js.map → transcript-cache-CdHKtx9y.js.map} +1 -1
  129. package/dist/{transcript-reconcile-CPdrNbbR.js → transcript-reconcile-D_SWw6dX.js} +11 -3
  130. package/dist/transcript-reconcile-D_SWw6dX.js.map +1 -0
  131. package/dist/tui.d.ts +7 -7
  132. package/dist/tui.js +24 -23
  133. package/dist/tui.js.map +1 -1
  134. package/dist/{turn-operations-DuGtKuc_.d.ts → turn-operations-Dbk3xPKu.d.ts} +11 -3
  135. package/dist/{turn-operations-DuGtKuc_.d.ts.map → turn-operations-Dbk3xPKu.d.ts.map} +1 -1
  136. package/dist/{types-C0PJmBaO.d.ts → types-CMXR7A0b.d.ts} +2 -2
  137. package/dist/{types-C0PJmBaO.d.ts.map → types-CMXR7A0b.d.ts.map} +1 -1
  138. package/dist/types-CzmghX7C.js.map +1 -1
  139. package/dist/types.d.ts +2 -2
  140. package/dist/{url-D8KZ7Edw.js → url-D4vG7lzO.js} +2 -2
  141. package/dist/{url-D8KZ7Edw.js.map → url-D4vG7lzO.js.map} +1 -1
  142. package/dist/{version-bd3DQJkc.js → version-DOvIuU8M.js} +2 -2
  143. package/dist/{version-bd3DQJkc.js.map → version-DOvIuU8M.js.map} +1 -1
  144. package/dist/{workspace-exec-stream-CyfYgqAh.js → workspace-exec-stream-Bzxczzu2.js} +5 -5
  145. package/dist/{workspace-exec-stream-CyfYgqAh.js.map → workspace-exec-stream-Bzxczzu2.js.map} +1 -1
  146. package/dist/{write-file-QYrcDdl3.js → write-file-ChtRPdEL.js} +3 -3
  147. package/dist/{write-file-QYrcDdl3.js.map → write-file-ChtRPdEL.js.map} +1 -1
  148. package/dist/{ws-CURQ2igk.js → ws-CrD-4Mx6.js} +6 -6
  149. package/dist/{ws-CURQ2igk.js.map → ws-CrD-4Mx6.js.map} +1 -1
  150. package/dist/{ws-client-BVljYbdb.js → ws-client-Dond8vT3.js} +4 -4
  151. package/dist/{ws-client-BVljYbdb.js.map → ws-client-Dond8vT3.js.map} +1 -1
  152. package/docs/CODEMODE.md +6 -0
  153. package/docs/IPC.md +2 -0
  154. package/docs/ONBOARDING.md +1 -1
  155. package/package.json +1 -1
  156. package/dist/client-core-EEEX_u6D.d.ts.map +0 -1
  157. package/dist/dispatcher-Bv9-c6I2.js.map +0 -1
  158. package/dist/index-DyQyDLX1.d.ts.map +0 -1
  159. package/dist/oauth-7_48zR7n.js.map +0 -1
  160. package/dist/prelude-HzY_JEGq.js.map +0 -1
  161. package/dist/protocol-BIv1Yuvl.js.map +0 -1
  162. package/dist/secret-shapes-BjW4l6U0.js.map +0 -1
  163. package/dist/session-7TUHs42_.js.map +0 -1
  164. package/dist/spaces-CyjMh5Zf.js.map +0 -1
  165. package/dist/store-L01W0gGI.js.map +0 -1
  166. package/dist/transcript-reconcile-CPdrNbbR.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"links-rFaWqS2Y.js","names":[],"sources":["../src/ipc/machine/agent.ts","../src/ipc/machine/reverse.ts","../src/ipc/machine/share-lock.ts","../src/ipc/machine/links.ts"],"sourcesContent":["/**\n * The machine agent (`docs/MACHINES.md` § Machine side): dials its host with\n * a machine-scoped token, registers, and serves the host's execution\n * requests through the confined executor (`./executor.ts`) until stopped.\n * Reconnects with the hosts' connection loop (backoff; a refused token, a\n * different host at the URL, or a refused registration stop it for good).\n *\n * The CLI (`./cli.ts`, `zidane machine share`) runs it in the foreground;\n * `./links.ts` starts it from the local daemon / in-process host.\n */\nimport type { HostConnectionStatus } from '../../chat/hosts/connection'\nimport type { MachineRegisterParams, MachineRegisterResult, MachineRoot } from '../../chat/machine-protocol'\nimport type { IpcClient } from '../transports/client-core'\nimport type { MachineExecutorOptions } from './executor'\nimport { mkdirSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { JsonRpcRemoteError } from '../../acp/json-rpc'\nimport { writeJsonFileAtomic } from '../../atomic-write'\nimport { HostIdentityError } from '../../chat/hosts/connection'\nimport { displayHostUrl } from '../../chat/hosts/url'\nimport { isValidMachineName, MACHINE_PROTOCOL_VERSION, MachineErrorCode } from '../../chat/machine-protocol'\nimport { ZIDANE_VERSION } from '../../version'\nimport { keepHostConnected } from '../hosts/connection-loop'\nimport { IpcVersionMismatchError } from '../transports/client-core'\nimport { connectWsClient, HostAuthError } from '../transports/ws-client'\nimport { createMachineExecutor, shareableRoot } from './executor'\nimport { machineIdentity } from './identity'\n\nexport { machineIdentity } from './identity'\n\nexport interface MachineAgentOptions {\n /** `ws://` / `wss://` URL of the host. */\n hostUrl: string\n /** A `machine`-scoped token of that host. */\n token: string\n /** Display name, and the suffix of its environment kinds (`local@<name>`). */\n name: string\n /** Folders the host may use; absolute, existing. */\n roots: ReadonlyArray<string | MachineRoot>\n /** This machine's data dir: its id, the host it pinned, task logs. Never reachable by the host's file ops. */\n dataDir: string\n appVersion?: string\n /** Every status change (`connected`, `reconnecting`, `auth-refused`, …) and why it failed. */\n onStatus?: (status: HostConnectionStatus, detail?: string) => void\n /** Backoff bounds and heartbeat (tests shrink them). */\n minDelayMs?: number\n maxDelayMs?: number\n heartbeatMs?: number\n terminalGraceMs?: number\n /** This machine user's `sandbox*` settings: every command the host sends runs under them (`docs/MACHINES.md` § Sandbox). */\n sandbox?: MachineExecutorOptions['sandbox']\n /** This machine user's zidane data dir: never shared (a root can't be in it, file ops never reach it), and its credential files are hidden from sandboxed commands. */\n credentialsDataDir?: string\n}\n\nexport interface MachineAgent {\n machineId: string\n status: () => HostConnectionStatus\n /** Reconnect now (after a fix on the host side). */\n retry: () => void\n stop: () => Promise<void>\n}\n\n/** A registration the host refused for good (its name taken, another protocol). */\nclass MachineRegisterError extends Error {\n constructor(message: string, readonly forGood: boolean) {\n super(message)\n this.name = 'MachineRegisterError'\n }\n}\n\nfunction normalizeRoots(roots: MachineAgentOptions['roots'], zidaneDataDir: string | undefined): MachineRoot[] {\n if (roots.length === 0)\n throw new Error('Share at least one folder (--root).')\n return roots.map((root) => {\n const entry = typeof root === 'string' ? { path: root } : root\n const real = shareableRoot(entry.path, zidaneDataDir)\n if (!statSync(real).isDirectory())\n throw new Error(`Root ${entry.path} is not a folder.`)\n return { path: real, ...(entry.label ? { label: entry.label } : {}) }\n })\n}\n\n/** The host id this machine pinned for `url` (the first host that answered there). */\nfunction pinnedHosts(dataDir: string): { get: (url: string) => string | undefined, set: (url: string, hostId: string) => void } {\n const file = join(dataDir, 'machine-hosts.json')\n const read = (): Record<string, string> => {\n try {\n return JSON.parse(readFileSync(file, 'utf8')) as Record<string, string>\n }\n catch {\n return {}\n }\n }\n return {\n get: url => read()[url],\n set: (url, hostId) => writeJsonFileAtomic(file, { ...read(), [url]: hostId }, { ensureDir: true, mode: 0o600 }),\n }\n}\n\nexport function startMachineAgent(options: MachineAgentOptions): MachineAgent {\n if (!isValidMachineName(options.name))\n throw new Error(`\"${options.name}\" is not a valid machine name: 1 to 64 letters, digits, \".\", \"-\" or \"_\".`)\n mkdirSync(options.dataDir, { recursive: true, mode: 0o700 })\n const dataDir = realpathSync.native(options.dataDir)\n const roots = normalizeRoots(options.roots, options.credentialsDataDir)\n const machineId = machineIdentity(dataDir)\n const pins = pinnedHosts(dataDir)\n const url = options.hostUrl\n let client: IpcClient | null = null\n let status: HostConnectionStatus = 'connecting'\n\n const executor = createMachineExecutor({\n roots,\n dataDir,\n ...(options.terminalGraceMs !== undefined ? { terminalGraceMs: options.terminalGraceMs } : {}),\n ...(options.sandbox ? { sandbox: options.sandbox } : {}),\n ...(options.credentialsDataDir ? { credentialsDataDir: options.credentialsDataDir } : {}),\n notify: (method, params) => {\n if (!client)\n return false\n try {\n (client.notify as unknown as (m: string, p: unknown) => void)(method, params)\n return true\n }\n catch {\n return false\n }\n },\n })\n\n const register: MachineRegisterParams = {\n machineId,\n name: options.name,\n roots,\n capabilities: { shell: true, terminals: typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined', background: true, worktrees: true, stream: true, sandboxed: executor.sandbox.on },\n version: options.appVersion ?? ZIDANE_VERSION,\n protocolVersion: MACHINE_PROTOCOL_VERSION,\n }\n\n const loop = keepHostConnected({\n minDelayMs: options.minDelayMs ?? 1000,\n maxDelayMs: options.maxDelayMs ?? 30_000,\n async connect({ signal, onClose }) {\n const expectHostId = pins.get(url)\n const connected = await connectWsClient({\n url,\n token: options.token,\n signal,\n onClose,\n appVersion: register.version,\n ...(options.heartbeatMs !== undefined ? { heartbeatMs: options.heartbeatMs } : {}),\n ...(expectHostId ? { expectHostId } : {}),\n serve: {\n request: (method, params) => executor.handle(method, params),\n notification: (method, params) => void executor.handle(method, params).catch(() => undefined),\n },\n })\n try {\n await (connected.request as unknown as (m: string, p: unknown) => Promise<MachineRegisterResult>)('machine.register', register)\n }\n catch (err) {\n connected.close()\n const code = err instanceof JsonRpcRemoteError ? err.code : undefined\n throw new MachineRegisterError(`${displayHostUrl(url)} refused this machine: ${(err as Error).message}`, code === MachineErrorCode.NameTaken || code === MachineErrorCode.VersionSkew || code === MachineErrorCode.IdBound)\n }\n if (!expectHostId && connected.hello.hostId)\n pins.set(url, connected.hello.hostId)\n return connected\n },\n classify: (err) => {\n if (err instanceof HostIdentityError)\n return 'identity'\n if (err instanceof HostAuthError || (err instanceof MachineRegisterError && err.forGood))\n return 'auth'\n return err instanceof IpcVersionMismatchError ? 'protocol' : 'retry'\n },\n update: (patch) => {\n if (patch.client !== undefined)\n client = patch.client\n if (patch.status) {\n status = patch.status\n options.onStatus?.(patch.status, patch.error)\n }\n },\n onConnected: () => executor.linkUp(),\n onDisconnected: () => executor.linkLost(),\n })\n\n return {\n machineId,\n status: () => status,\n retry: () => loop.retry(),\n async stop() {\n loop.stop()\n await executor.close()\n },\n }\n}\n","/**\n * A saved server used as a machine of this local host (`local@<server>`,\n * `docs/MACHINES.md` § Reverse direction). A local host has no listener, so\n * it dials the server as a client (its saved client token), asks it to lend\n * itself (`machine.attach`), and registers it in its own machine registry\n * with a connection that sends each host→machine request as `machine.call`.\n * The server's notifications come back as `machine:notification` signals.\n * Reconnects with the hosts' connection loop; a refused token, another host\n * at the URL, or a server that does not lend itself stop it for good.\n */\nimport type { AttachedMachine, MachineConnection, MachineRegistry } from '../../chat/host/machines'\nimport type { MachineLinkState } from '../../chat/machine-protocol'\nimport type { JsonValue } from '../../types'\nimport type { IpcClient } from '../transports/client-core'\nimport { JsonRpcConnectionClosedError, JsonRpcRemoteError } from '../../acp/json-rpc'\nimport { MachineLinkLostError } from '../../chat/host/machines'\nimport { HostIdentityError } from '../../chat/hosts/connection'\nimport { displayHostUrl } from '../../chat/hosts/url'\nimport { MachineErrorCode } from '../../chat/machine-protocol'\nimport { keepHostConnected } from '../hosts/connection-loop'\nimport { connectWsClient, HostAuthError } from '../transports/ws-client'\n\nexport interface ServerMachineOptions {\n machines: MachineRegistry\n url: string\n /** The saved server's client token. */\n token: string\n /** The saved server's host id: another host at the URL is refused. */\n expectHostId: string\n /** This host's id (the server keys its executor by it, so tasks survive a reconnect). */\n localHostId: string\n appVersion?: string\n onState: (state: MachineLinkState, detail?: string, machineName?: string) => void\n minDelayMs?: number\n maxDelayMs?: number\n heartbeatMs?: number\n}\n\nexport interface ServerMachine {\n retry: () => void\n stop: () => void\n}\n\n/** The server refused to lend itself (`allowAsMachine` off, a name taken here): for good. A busy server is retried. */\nclass AttachRefusedError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'AttachRefusedError'\n }\n}\n\nfunction connectionOver(client: IpcClient): MachineConnection {\n return {\n async request(method, params) {\n try {\n return await client.request('machine.call', { method, params: (params ?? {}) as JsonValue })\n }\n catch (err) {\n if (err instanceof JsonRpcConnectionClosedError)\n throw new MachineLinkLostError()\n throw err instanceof JsonRpcRemoteError ? new Error(err.message) : err\n }\n },\n notify: (method, params) => client.notify('machine.call', { method, params: (params ?? {}) as JsonValue }),\n close: () => client.close(),\n }\n}\n\nexport function startServerMachine(options: ServerMachineOptions): ServerMachine {\n let attached: AttachedMachine | undefined\n const where = displayHostUrl(options.url)\n\n const detach = (): void => {\n attached?.closed()\n attached = undefined\n }\n\n const loop = keepHostConnected({\n ...(options.minDelayMs !== undefined ? { minDelayMs: options.minDelayMs } : {}),\n ...(options.maxDelayMs !== undefined ? { maxDelayMs: options.maxDelayMs } : {}),\n async connect({ signal, onClose }) {\n const client = await connectWsClient({\n url: options.url,\n token: options.token,\n expectHostId: options.expectHostId,\n signal,\n onClose,\n ...(options.appVersion ? { appVersion: options.appVersion } : {}),\n ...(options.heartbeatMs !== undefined ? { heartbeatMs: options.heartbeatMs } : {}),\n })\n // Subscribed before attaching: the server re-sends queued task exits while it answers `machine.attach`.\n const early: Array<{ method: string, params: unknown }> = []\n let current: AttachedMachine | undefined\n client.onSignal((s) => {\n if (s.kind !== 'machine:notification')\n return\n if (current)\n current.notification(s.method, s.params)\n else if (early.length < 1024)\n early.push({ method: s.method, params: s.params })\n })\n try {\n const params = await client.request('machine.attach', { hostId: options.localHostId })\n detach()\n current = options.machines.attach(params, connectionOver(client))\n attached = current\n for (const { method, params: p } of early.splice(0))\n current.notification(method, p)\n options.onState('connected', undefined, params.name)\n }\n catch (err) {\n client.close()\n // No room there now (a capacity limit) or the link dropped: the loop retries with backoff.\n if (err instanceof JsonRpcConnectionClosedError || (err instanceof JsonRpcRemoteError && err.code === MachineErrorCode.Busy))\n throw new Error(`${where} can't lend itself right now: ${(err as Error).message}`)\n throw new AttachRefusedError(`${where} refused to be a machine: ${(err as Error).message}`)\n }\n return client\n },\n classify: err => (err instanceof HostIdentityError ? 'identity' : err instanceof HostAuthError || err instanceof AttachRefusedError ? 'auth' : 'retry'),\n update: (patch) => {\n if (!patch.status || patch.status === 'connected')\n return\n options.onState(patch.status === 'auth-refused' && patch.error?.includes('refused to be a machine') ? 'refused' : patch.status, patch.error)\n },\n onDisconnected: detach,\n })\n\n return {\n retry: () => loop.retry(),\n stop() {\n loop.stop()\n detach()\n },\n }\n}\n","/**\n * `machine-links.lock`: which process runs this computer's shares (one\n * machine id, one connection; two processes sharing it would keep replacing\n * each other's). Exclusive by construction, not by a read-then-write:\n *\n * - taking a free lock is an exclusive create (`wx`): one contender wins;\n * - the record names its holder (`pid`) and a per-process `nonce`; the\n * holder touches the file on every check, so a holder is stale when its\n * pid is gone OR the file went untouched for {@link STALE_MS} (a reused\n * pid never touches it);\n * - a stale lock is moved aside (an atomic rename to a name of our own),\n * checked to be the very record judged stale, then the exclusive create\n * runs again; a live lock moved aside by mistake is put back without\n * clobbering (`link`, which fails if the name is taken);\n * - a holder re-reads the record on every check: another nonce there means\n * it lost the lock, and the caller stops its shares;\n * - release removes the file only when it still carries this nonce.\n */\nimport { randomBytes } from 'node:crypto'\nimport { closeSync, linkSync, openSync, readFileSync, renameSync, rmSync, statSync, utimesSync, writeSync } from 'node:fs'\nimport process from 'node:process'\n\n/** A holder that has not touched its lock for this long is gone (its pid may be reused). */\nexport const SHARE_LOCK_STALE_MS = 30_000\n\ninterface LockRecord {\n pid: number\n nonce: string\n}\n\nexport interface ShareLock {\n /** Hold the lock (taking it when free or stale); `false` when another live process holds it. */\n acquire: () => boolean\n /** Whether this process held it at the last {@link acquire}. */\n held: () => boolean\n /** Remove the lock when this process still holds it; whether it did. */\n release: () => boolean\n}\n\nexport interface ShareLockOptions {\n /** Test seams. */\n pid?: number\n alive?: (pid: number) => boolean\n now?: () => number\n staleMs?: number\n}\n\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n }\n catch (err) {\n return (err as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\nfunction readRecord(file: string): { record?: LockRecord, mtimeMs: number } | undefined {\n try {\n const mtimeMs = statSync(file).mtimeMs\n const raw = JSON.parse(readFileSync(file, 'utf8')) as Partial<LockRecord>\n return { mtimeMs, ...(typeof raw.pid === 'number' && typeof raw.nonce === 'string' ? { record: { pid: raw.pid, nonce: raw.nonce } } : {}) }\n }\n catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT')\n return undefined\n // Unreadable or half-written: judged by its age alone.\n try {\n return { mtimeMs: statSync(file).mtimeMs }\n }\n catch {\n return undefined\n }\n }\n}\n\nexport function createShareLock(file: string, options: ShareLockOptions = {}): ShareLock {\n const pid = options.pid ?? process.pid\n const alive = options.alive ?? processAlive\n const now = options.now ?? Date.now\n const staleMs = options.staleMs ?? SHARE_LOCK_STALE_MS\n const nonce = randomBytes(12).toString('hex')\n let holding = false\n\n const create = (): boolean => {\n let fd: number\n try {\n fd = openSync(file, 'wx', 0o600)\n }\n catch {\n return false\n }\n try {\n writeSync(fd, JSON.stringify({ pid, nonce }))\n }\n finally {\n closeSync(fd)\n }\n return true\n }\n\n const ours = (): boolean => readRecord(file)?.record?.nonce === nonce\n\n const stale = (found: { record?: LockRecord, mtimeMs: number }): boolean =>\n now() - found.mtimeMs > staleMs || (found.record !== undefined && !alive(found.record.pid))\n\n /** Move a stale lock aside; `true` when the record moved is the one judged stale. */\n const evict = (judged: { record?: LockRecord, mtimeMs: number }): boolean => {\n const aside = `${file}.${nonce}.stale`\n try {\n renameSync(file, aside)\n }\n catch {\n return false\n }\n const moved = readRecord(aside)\n const same = moved?.record?.nonce === judged.record?.nonce && moved?.mtimeMs === judged.mtimeMs\n if (!same) {\n // A live lock taken meanwhile: put it back, never over a newer one.\n try {\n linkSync(aside, file)\n }\n catch {\n // Someone holds the name again; that holder's own check settles it.\n }\n }\n rmSync(aside, { force: true })\n return same\n }\n\n return {\n acquire() {\n if (holding) {\n if (ours()) {\n try {\n const at = new Date(now())\n utimesSync(file, at, at)\n }\n catch {\n // The touch is best effort; the record still names this process.\n }\n return true\n }\n holding = false\n }\n if (create()) {\n holding = true\n return true\n }\n const found = readRecord(file)\n if (found && !stale(found))\n return false\n if (found && !evict(found))\n return false\n holding = create()\n return holding\n },\n held: () => holding,\n release() {\n const mine = holding && ours()\n if (mine)\n rmSync(file, { force: true })\n holding = false\n return mine\n },\n }\n}\n","/**\n * A local host's machine links (`docs/MACHINES.md` § Client UI), loaded only\n * behind remote hosts:\n *\n * - **Shares**: this computer lent to a saved server (a machine agent in\n * this process, with the machine token stored under `machine:<hostId>`).\n * One process runs them for the whole computer: the holder of\n * `machine-links.lock` (the daemon, or the first window's in-process\n * host; exclusive, `./share-lock.ts`). Two processes sharing one computer\n * under one machine id would keep replacing each other's connection, so a\n * process that finds it lost the lock stops its shares at once.\n * - **Uses**: a saved server lent to THIS host (`local@<server>`), over its\n * own client connection. Per host process: each host has its own machine\n * registry, so each attaches under its own instance id.\n *\n * The wanted links live in `<dataDir>/machine-links.json` (any process writes\n * it); every process re-reads it every {@link POLL_MS}, the holder applies the\n * shares and publishes their state in `machine-links-status.json`. Tokens\n * are never in either file.\n */\nimport type { MachineRegistry } from '../../chat/host/machines'\nimport type { HostEntry } from '../../chat/hosts/types'\nimport type { MachineLinksStatus, MachineLinkState, MachineShareStatus, MachineUseStatus } from '../../chat/machine-protocol'\nimport type { LooseSandboxSettings } from '../../chat/sandbox-settings'\nimport type { MachineAgent } from './agent'\nimport type { ServerMachine } from './reverse'\nimport { randomBytes } from 'node:crypto'\nimport { existsSync, readFileSync, rmSync } from 'node:fs'\nimport { isAbsolute, join } from 'node:path'\nimport { OWNER_ONLY, writeJsonFileAtomic } from '../../atomic-write'\nimport { readCredentialEntry, writeCredentialEntry } from '../../chat/credentials'\nimport { createHostRegistry } from '../../chat/hosts'\nimport { isValidMachineName, machineCredentialKey } from '../../chat/machine-protocol'\nimport { startMachineAgent } from './agent'\nimport { shareableRoot } from './executor'\nimport { startServerMachine } from './reverse'\nimport { createShareLock } from './share-lock'\n\nconst POLL_MS = 2000\nconst CONFIG_FILE = 'machine-links.json'\nconst STATUS_FILE = 'machine-links-status.json'\nconst LOCK_FILE = 'machine-links.lock'\nconst MAX_ROOTS = 32\n\ninterface ShareConfig {\n hostId: string\n name: string\n roots: string[]\n}\n\ninterface LinksConfig {\n version: 1\n shares: ShareConfig[]\n uses: string[]\n}\n\nexport interface MachineLinksOptions {\n /** The host's data dir: saved hosts, credentials, these files. */\n dataDir: string\n machines: MachineRegistry\n /** This host's id (`ipc.hello`). */\n hostId: string | undefined\n /** This user's settings: a share applies their `sandbox*` to what it runs (`docs/MACHINES.md` § Sandbox). */\n settings?: () => LooseSandboxSettings\n /** Every status change, for `machine:links`. */\n onChange: (status: MachineLinksStatus) => void\n appVersion?: string\n /** Connection pacing (tests shrink them). */\n minDelayMs?: number\n maxDelayMs?: number\n heartbeatMs?: number\n pollMs?: number\n}\n\nexport interface MachineLinks {\n status: () => MachineLinksStatus\n startShare: (input: { hostId: string, name: string, roots: string[], token?: string }) => MachineLinksStatus\n stopShare: (hostId: string) => MachineLinksStatus\n setUse: (hostId: string, enabled: boolean) => MachineLinksStatus\n /** Stop everything (remote hosts turned off, or the host closes); the wanted links stay saved. */\n close: () => Promise<void>\n}\n\nfunction readJson<T>(file: string, fallback: T): T {\n try {\n return existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) as T : fallback\n }\n catch {\n return fallback\n }\n}\n\nfunction readConfig(file: string): LinksConfig {\n const raw = readJson<Partial<LinksConfig>>(file, {})\n const shares = Array.isArray(raw.shares)\n ? raw.shares.filter((s): s is ShareConfig => !!s && typeof s.hostId === 'string' && typeof s.name === 'string' && Array.isArray(s.roots) && s.roots.every(r => typeof r === 'string'))\n : []\n const uses = Array.isArray(raw.uses) ? raw.uses.filter((u): u is string => typeof u === 'string') : []\n return { version: 1, shares, uses }\n}\n\n/**\n * A share's name and roots, as a client sent them: every root absolute,\n * existing, and not (by its real path) zidane's data dir, inside it, or a\n * credentials folder (`./executor`'s `shareableRoot`).\n */\nexport function checkShareInput(input: { name: string, roots: string[] }, zidaneDataDir: string): void {\n if (!isValidMachineName(input.name))\n throw new Error(`\"${input.name}\" is not a valid machine name: 1 to 64 letters, digits, \".\", \"-\" or \"_\".`)\n if (!Array.isArray(input.roots) || input.roots.length === 0)\n throw new Error('Share at least one folder.')\n if (input.roots.length > MAX_ROOTS)\n throw new Error(`Share at most ${MAX_ROOTS} folders.`)\n for (const root of input.roots) {\n if (typeof root !== 'string' || !isAbsolute(root))\n throw new Error(`${String(root)} is not an absolute folder path.`)\n shareableRoot(root, zidaneDataDir)\n }\n}\n\nexport function startMachineLinks(options: MachineLinksOptions): MachineLinks {\n const { dataDir, machines } = options\n const configFile = join(dataDir, CONFIG_FILE)\n const statusFile = join(dataDir, STATUS_FILE)\n const lock = createShareLock(join(dataDir, LOCK_FILE))\n // Reading entries and tokens needs no probe: nothing is dialed here but the links themselves.\n const hosts = createHostRegistry({ dataDir, probe: async () => ({ ok: false, error: 'unreachable', message: 'not probed here' }) })\n const instance = `${(options.hostId ?? 'local').slice(0, 40)}-${randomBytes(4).toString('hex')}`\n const pacing = {\n ...(options.minDelayMs !== undefined ? { minDelayMs: options.minDelayMs } : {}),\n ...(options.maxDelayMs !== undefined ? { maxDelayMs: options.maxDelayMs } : {}),\n ...(options.heartbeatMs !== undefined ? { heartbeatMs: options.heartbeatMs } : {}),\n }\n\n const shares = new Map<string, { config: ShareConfig, agent?: MachineAgent, status: MachineShareStatus }>()\n const uses = new Map<string, { link?: ServerMachine, status: MachineUseStatus }>()\n let holder = false\n let closed = false\n let lastPublished = ''\n\n const ownStatus = (): MachineLinksStatus => ({ shares: [...shares.values()].map(s => s.status), uses: [...uses.values()].map(u => u.status) })\n\n /** Shares as the holder reports them (another process may run them). */\n const status = (): MachineLinksStatus => {\n const own = ownStatus()\n if (holder)\n return own\n const config = readConfig(configFile)\n const published = readJson<Partial<MachineLinksStatus>>(statusFile, {})\n const known = new Map((published.shares ?? []).map(s => [s.hostId, s]))\n return {\n shares: config.shares.map(s => known.get(s.hostId) ?? { hostId: s.hostId, name: s.name, roots: s.roots, state: 'connecting' as const, detail: 'waiting for the zidane process that shares this computer' }),\n uses: own.uses,\n }\n }\n\n const publish = (): void => {\n if (closed)\n return\n const snapshot = status()\n const text = JSON.stringify(snapshot)\n if (holder) {\n try {\n writeJsonFileAtomic(statusFile, { shares: snapshot.shares }, OWNER_ONLY)\n }\n catch {\n // Best effort: other processes show \"waiting\" until it lands.\n }\n }\n if (text === lastPublished)\n return\n lastPublished = text\n options.onChange(snapshot)\n }\n\n const entry = (hostId: string): HostEntry => {\n const found = hosts.get(hostId)\n if (!found)\n throw new Error(`No saved server \"${hostId}\": add it on the Hosts page first.`)\n return found\n }\n\n function runShare(config: ShareConfig): void {\n const status: MachineShareStatus = { hostId: config.hostId, name: config.name, roots: config.roots, state: 'connecting' }\n const share: { config: ShareConfig, agent?: MachineAgent, status: MachineShareStatus } = { config, status }\n shares.set(config.hostId, share)\n const set = (state: MachineLinkState, detail?: string): void => {\n share.status = { ...status, state, ...(detail && state !== 'connected' ? { detail } : {}) }\n publish()\n }\n try {\n const server = entry(config.hostId)\n const cred = readCredentialEntry(dataDir, machineCredentialKey(config.hostId))\n const token = cred?.kind === 'apikey' ? cred.value : undefined\n if (!token)\n throw new Error(`No machine token saved for ${server.name}.`)\n share.agent = startMachineAgent({ hostUrl: server.url, token, name: config.name, roots: config.roots, dataDir: join(dataDir, 'machine'), credentialsDataDir: dataDir, ...(options.settings ? { sandbox: options.settings() } : {}), ...pacing, ...(options.appVersion ? { appVersion: options.appVersion } : {}), onStatus: (state, detail) => set(state, detail) })\n }\n catch (err) {\n set('refused', (err as Error).message)\n }\n }\n\n function runUse(hostId: string): void {\n const use: { link?: ServerMachine, status: MachineUseStatus } = { status: { hostId, state: 'connecting' } }\n uses.set(hostId, use)\n const set = (state: MachineLinkState, detail?: string, machineName?: string): void => {\n use.status = { hostId, state, ...(detail && state !== 'connected' ? { detail } : {}), ...(machineName ?? use.status.machineName ? { machineName: machineName ?? use.status.machineName } : {}) }\n publish()\n }\n try {\n const server = entry(hostId)\n const token = hosts.token(hostId)\n if (!token)\n throw new Error(`No token saved for ${server.name}: add it again.`)\n use.link = startServerMachine({ machines, url: server.url, token, expectHostId: hostId, localHostId: instance, ...pacing, ...(options.appVersion ? { appVersion: options.appVersion } : {}), onState: set })\n }\n catch (err) {\n set('refused', (err as Error).message)\n }\n }\n\n const stopShare = async (hostId: string): Promise<void> => {\n const share = shares.get(hostId)\n shares.delete(hostId)\n await share?.agent?.stop()\n }\n\n /** Bring the running links in line with the saved config. */\n function reconcile(): void {\n if (closed)\n return\n const config = readConfig(configFile)\n holder = lock.acquire()\n if (!holder) {\n // Lost the lock (or never had it): another process runs this computer's shares, never two.\n for (const hostId of [...shares.keys()])\n void stopShare(hostId)\n }\n else {\n const wanted = new Map(config.shares.map(s => [s.hostId, s]))\n for (const [hostId, share] of shares) {\n const want = wanted.get(hostId)\n if (!want || JSON.stringify(want) !== JSON.stringify(share.config))\n void stopShare(hostId)\n }\n for (const share of config.shares) {\n if (!shares.has(share.hostId))\n runShare(share)\n }\n }\n const wantedUses = new Set(config.uses)\n for (const [hostId, use] of uses) {\n if (!wantedUses.has(hostId)) {\n use.link?.stop()\n uses.delete(hostId)\n }\n }\n for (const hostId of config.uses) {\n if (!uses.has(hostId))\n runUse(hostId)\n }\n publish()\n }\n\n const writeConfig = (mutate: (config: LinksConfig) => void): MachineLinksStatus => {\n const config = readConfig(configFile)\n mutate(config)\n writeJsonFileAtomic(configFile, config, OWNER_ONLY)\n reconcile()\n return status()\n }\n\n reconcile()\n const timer = setInterval(reconcile, options.pollMs ?? POLL_MS)\n timer.unref?.()\n\n return {\n status,\n startShare(input) {\n checkShareInput(input, dataDir)\n const server = entry(input.hostId)\n const token = input.token?.trim()\n if (token)\n writeCredentialEntry(dataDir, machineCredentialKey(input.hostId), { kind: 'apikey', value: token })\n else if (readCredentialEntry(dataDir, machineCredentialKey(input.hostId))?.kind !== 'apikey')\n throw new Error(`No machine token for ${server.name}: create one there (zidane server token create --scope machine) or let this window create it.`)\n return writeConfig((config) => {\n config.shares = [...config.shares.filter(s => s.hostId !== input.hostId), { hostId: input.hostId, name: input.name, roots: [...input.roots] }]\n })\n },\n stopShare(hostId) {\n return writeConfig((config) => {\n config.shares = config.shares.filter(s => s.hostId !== hostId)\n })\n },\n setUse(hostId, enabled) {\n if (enabled) {\n const server = entry(hostId)\n if (!hosts.token(hostId))\n throw new Error(`No token saved for ${server.name}: add it again.`)\n }\n return writeConfig((config) => {\n config.uses = enabled ? [...new Set([...config.uses, hostId])] : config.uses.filter(u => u !== hostId)\n })\n },\n async close() {\n if (closed)\n return\n closed = true\n clearInterval(timer)\n for (const use of uses.values())\n use.link?.stop()\n uses.clear()\n await Promise.all([...shares.keys()].map(stopShare))\n // Only the holder's own files: a process that lost the lock leaves the new holder's alone.\n if (lock.release())\n rmSync(statusFile, { force: true })\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgEA,IAAM,uBAAN,cAAmC,MAAM;CACD;CAAtC,YAAY,SAAiB,SAA2B;EACtD,MAAM,OAAO;EADuB,KAAA,UAAA;EAEpC,KAAK,OAAO;CACd;AACF;AAEA,SAAS,eAAe,OAAqC,eAAkD;CAC7G,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,qCAAqC;CACvD,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,QAAQ,OAAO,SAAS,WAAW,EAAE,MAAM,KAAK,IAAI;EAC1D,MAAM,OAAO,cAAc,MAAM,MAAM,aAAa;EACpD,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,YAAY,GAC9B,MAAM,IAAI,MAAM,QAAQ,MAAM,KAAK,kBAAkB;EACvD,OAAO;GAAE,MAAM;GAAM,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAAG;CACtE,CAAC;AACH;;AAGA,SAAS,YAAY,SAA2G;CAC9H,MAAM,OAAO,KAAK,SAAS,oBAAoB;CAC/C,MAAM,aAAqC;EACzC,IAAI;GACF,OAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;EAC9C,QACM;GACJ,OAAO,CAAC;EACV;CACF;CACA,OAAO;EACL,MAAK,QAAO,KAAK,CAAC,CAAC;EACnB,MAAM,KAAK,WAAW,oBAAoB,MAAM;GAAE,GAAG,KAAK;IAAI,MAAM;EAAO,GAAG;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;CAChH;AACF;AAEA,SAAgB,kBAAkB,SAA4C;CAC5E,IAAI,CAAC,mBAAmB,QAAQ,IAAI,GAClC,MAAM,IAAI,MAAM,IAAI,QAAQ,KAAK,yEAAyE;CAC5G,UAAU,QAAQ,SAAS;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CAC3D,MAAM,UAAU,aAAa,OAAO,QAAQ,OAAO;CACnD,MAAM,QAAQ,eAAe,QAAQ,OAAO,QAAQ,kBAAkB;CACtE,MAAM,YAAY,gBAAgB,OAAO;CACzC,MAAM,OAAO,YAAY,OAAO;CAChC,MAAM,MAAM,QAAQ;CACpB,IAAI,SAA2B;CAC/B,IAAI,SAA+B;CAEnC,MAAM,WAAW,sBAAsB;EACrC;EACA;EACA,GAAI,QAAQ,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI,CAAC;EAC5F,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;EACtD,GAAI,QAAQ,qBAAqB,EAAE,oBAAoB,QAAQ,mBAAmB,IAAI,CAAC;EACvF,SAAS,QAAQ,WAAW;GAC1B,IAAI,CAAC,QACH,OAAO;GACT,IAAI;IACF,OAAQ,OAAsD,QAAQ,MAAM;IAC5E,OAAO;GACT,QACM;IACJ,OAAO;GACT;EACF;CACF,CAAC;CAED,MAAM,WAAkC;EACtC;EACA,MAAM,QAAQ;EACd;EACA,cAAc;GAAE,OAAO;GAAM,WAAW,OAAQ,WAAiC,QAAQ;GAAa,YAAY;GAAM,WAAW;GAAM,QAAQ;GAAM,WAAW,SAAS,QAAQ;EAAG;EACtL,SAAS,QAAQ,cAAc;EAC/B,iBAAA;CACF;CAEA,MAAM,OAAO,kBAAkB;EAC7B,YAAY,QAAQ,cAAc;EAClC,YAAY,QAAQ,cAAc;EAClC,MAAM,QAAQ,EAAE,QAAQ,WAAW;GACjC,MAAM,eAAe,KAAK,IAAI,GAAG;GACjC,MAAM,YAAY,MAAM,gBAAgB;IACtC;IACA,OAAO,QAAQ;IACf;IACA;IACA,YAAY,SAAS;IACrB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;IAChF,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;IACvC,OAAO;KACL,UAAU,QAAQ,WAAW,SAAS,OAAO,QAAQ,MAAM;KAC3D,eAAe,QAAQ,WAAW,KAAK,SAAS,OAAO,QAAQ,MAAM,CAAC,CAAC,YAAY,KAAA,CAAS;IAC9F;GACF,CAAC;GACD,IAAI;IACF,MAAO,UAAU,QAAiF,oBAAoB,QAAQ;GAChI,SACO,KAAK;IACV,UAAU,MAAM;IAChB,MAAM,OAAO,eAAe,qBAAqB,IAAI,OAAO,KAAA;IAC5D,MAAM,IAAI,qBAAqB,GAAG,eAAe,GAAG,EAAE,yBAA0B,IAAc,WAAW,SAAS,iBAAiB,aAAa,SAAS,iBAAiB,eAAe,SAAS,iBAAiB,OAAO;GAC5N;GACA,IAAI,CAAC,gBAAgB,UAAU,MAAM,QACnC,KAAK,IAAI,KAAK,UAAU,MAAM,MAAM;GACtC,OAAO;EACT;EACA,WAAW,QAAQ;GACjB,IAAI,eAAe,mBACjB,OAAO;GACT,IAAI,eAAe,iBAAkB,eAAe,wBAAwB,IAAI,SAC9E,OAAO;GACT,OAAO,eAAe,0BAA0B,aAAa;EAC/D;EACA,SAAS,UAAU;GACjB,IAAI,MAAM,WAAW,KAAA,GACnB,SAAS,MAAM;GACjB,IAAI,MAAM,QAAQ;IAChB,SAAS,MAAM;IACf,QAAQ,WAAW,MAAM,QAAQ,MAAM,KAAK;GAC9C;EACF;EACA,mBAAmB,SAAS,OAAO;EACnC,sBAAsB,SAAS,SAAS;CAC1C,CAAC;CAED,OAAO;EACL;EACA,cAAc;EACd,aAAa,KAAK,MAAM;EACxB,MAAM,OAAO;GACX,KAAK,KAAK;GACV,MAAM,SAAS,MAAM;EACvB;CACF;AACF;;;;AC1JA,IAAM,qBAAN,cAAiC,MAAM;CACrC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AAEA,SAAS,eAAe,QAAsC;CAC5D,OAAO;EACL,MAAM,QAAQ,QAAQ,QAAQ;GAC5B,IAAI;IACF,OAAO,MAAM,OAAO,QAAQ,gBAAgB;KAAE;KAAQ,QAAS,UAAU,CAAC;IAAgB,CAAC;GAC7F,SACO,KAAK;IACV,IAAI,eAAe,8BACjB,MAAM,IAAI,qBAAqB;IACjC,MAAM,eAAe,qBAAqB,IAAI,MAAM,IAAI,OAAO,IAAI;GACrE;EACF;EACA,SAAS,QAAQ,WAAW,OAAO,OAAO,gBAAgB;GAAE;GAAQ,QAAS,UAAU,CAAC;EAAgB,CAAC;EACzG,aAAa,OAAO,MAAM;CAC5B;AACF;AAEA,SAAgB,mBAAmB,SAA8C;CAC/E,IAAI;CACJ,MAAM,QAAQ,eAAe,QAAQ,GAAG;CAExC,MAAM,eAAqB;EACzB,UAAU,OAAO;EACjB,WAAW,KAAA;CACb;CAEA,MAAM,OAAO,kBAAkB;EAC7B,GAAI,QAAQ,eAAe,KAAA,IAAY,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;EAC7E,GAAI,QAAQ,eAAe,KAAA,IAAY,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;EAC7E,MAAM,QAAQ,EAAE,QAAQ,WAAW;GACjC,MAAM,SAAS,MAAM,gBAAgB;IACnC,KAAK,QAAQ;IACb,OAAO,QAAQ;IACf,cAAc,QAAQ;IACtB;IACA;IACA,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;IAC/D,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;GAClF,CAAC;GAED,MAAM,QAAoD,CAAC;GAC3D,IAAI;GACJ,OAAO,UAAU,MAAM;IACrB,IAAI,EAAE,SAAS,wBACb;IACF,IAAI,SACF,QAAQ,aAAa,EAAE,QAAQ,EAAE,MAAM;SACpC,IAAI,MAAM,SAAS,MACtB,MAAM,KAAK;KAAE,QAAQ,EAAE;KAAQ,QAAQ,EAAE;IAAO,CAAC;GACrD,CAAC;GACD,IAAI;IACF,MAAM,SAAS,MAAM,OAAO,QAAQ,kBAAkB,EAAE,QAAQ,QAAQ,YAAY,CAAC;IACrF,OAAO;IACP,UAAU,QAAQ,SAAS,OAAO,QAAQ,eAAe,MAAM,CAAC;IAChE,WAAW;IACX,KAAK,MAAM,EAAE,QAAQ,QAAQ,OAAO,MAAM,OAAO,CAAC,GAChD,QAAQ,aAAa,QAAQ,CAAC;IAChC,QAAQ,QAAQ,aAAa,KAAA,GAAW,OAAO,IAAI;GACrD,SACO,KAAK;IACV,OAAO,MAAM;IAEb,IAAI,eAAe,gCAAiC,eAAe,sBAAsB,IAAI,SAAS,iBAAiB,MACrH,MAAM,IAAI,MAAM,GAAG,MAAM,gCAAiC,IAAc,SAAS;IACnF,MAAM,IAAI,mBAAmB,GAAG,MAAM,4BAA6B,IAAc,SAAS;GAC5F;GACA,OAAO;EACT;EACA,WAAU,QAAQ,eAAe,oBAAoB,aAAa,eAAe,iBAAiB,eAAe,qBAAqB,SAAS;EAC/I,SAAS,UAAU;GACjB,IAAI,CAAC,MAAM,UAAU,MAAM,WAAW,aACpC;GACF,QAAQ,QAAQ,MAAM,WAAW,kBAAkB,MAAM,OAAO,SAAS,yBAAyB,IAAI,YAAY,MAAM,QAAQ,MAAM,KAAK;EAC7I;EACA,gBAAgB;CAClB,CAAC;CAED,OAAO;EACL,aAAa,KAAK,MAAM;EACxB,OAAO;GACL,KAAK,KAAK;GACV,OAAO;EACT;CACF;AACF;ACxFA,SAAS,aAAa,KAAsB;CAC1C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SACO,KAAK;EACV,OAAQ,IAA8B,SAAS;CACjD;AACF;AAEA,SAAS,WAAW,MAAoE;CACtF,IAAI;EACF,MAAM,UAAU,SAAS,IAAI,CAAC,CAAC;EAC/B,MAAM,MAAM,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;EACjD,OAAO;GAAE;GAAS,GAAI,OAAO,IAAI,QAAQ,YAAY,OAAO,IAAI,UAAU,WAAW,EAAE,QAAQ;IAAE,KAAK,IAAI;IAAK,OAAO,IAAI;GAAM,EAAE,IAAI,CAAC;EAAG;CAC5I,SACO,KAAK;EACV,IAAK,IAA8B,SAAS,UAC1C,OAAO,KAAA;EAET,IAAI;GACF,OAAO,EAAE,SAAS,SAAS,IAAI,CAAC,CAAC,QAAQ;EAC3C,QACM;GACJ;EACF;CACF;AACF;AAEA,SAAgB,gBAAgB,MAAc,UAA4B,CAAC,GAAc;CACvF,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,UAAU,QAAQ,WAAA;CACxB,MAAM,QAAQ,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC5C,IAAI,UAAU;CAEd,MAAM,eAAwB;EAC5B,IAAI;EACJ,IAAI;GACF,KAAK,SAAS,MAAM,MAAM,GAAK;EACjC,QACM;GACJ,OAAO;EACT;EACA,IAAI;GACF,UAAU,IAAI,KAAK,UAAU;IAAE;IAAK;GAAM,CAAC,CAAC;EAC9C,UACQ;GACN,UAAU,EAAE;EACd;EACA,OAAO;CACT;CAEA,MAAM,aAAsB,WAAW,IAAI,CAAC,EAAE,QAAQ,UAAU;CAEhE,MAAM,SAAS,UACb,IAAI,IAAI,MAAM,UAAU,WAAY,MAAM,WAAW,KAAA,KAAa,CAAC,MAAM,MAAM,OAAO,GAAG;;CAG3F,MAAM,SAAS,WAA8D;EAC3E,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM;EAC/B,IAAI;GACF,WAAW,MAAM,KAAK;EACxB,QACM;GACJ,OAAO;EACT;EACA,MAAM,QAAQ,WAAW,KAAK;EAC9B,MAAM,OAAO,OAAO,QAAQ,UAAU,OAAO,QAAQ,SAAS,OAAO,YAAY,OAAO;EACxF,IAAI,CAAC,MAEH,IAAI;GACF,SAAS,OAAO,IAAI;EACtB,QACM,CAEN;EAEF,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;EAC7B,OAAO;CACT;CAEA,OAAO;EACL,UAAU;GACR,IAAI,SAAS;IACX,IAAI,KAAK,GAAG;KACV,IAAI;MACF,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;MACzB,WAAW,MAAM,IAAI,EAAE;KACzB,QACM,CAEN;KACA,OAAO;IACT;IACA,UAAU;GACZ;GACA,IAAI,OAAO,GAAG;IACZ,UAAU;IACV,OAAO;GACT;GACA,MAAM,QAAQ,WAAW,IAAI;GAC7B,IAAI,SAAS,CAAC,MAAM,KAAK,GACvB,OAAO;GACT,IAAI,SAAS,CAAC,MAAM,KAAK,GACvB,OAAO;GACT,UAAU,OAAO;GACjB,OAAO;EACT;EACA,YAAY;EACZ,UAAU;GACR,MAAM,OAAO,WAAW,KAAK;GAC7B,IAAI,MACF,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;GAC9B,UAAU;GACV,OAAO;EACT;CACF;AACF;;;AChIA,MAAM,UAAU;AAChB,MAAM,cAAc;AACpB,MAAM,cAAc;AACpB,MAAM,YAAY;AAClB,MAAM,YAAY;AAyClB,SAAS,SAAY,MAAc,UAAgB;CACjD,IAAI;EACF,OAAO,WAAW,IAAI,IAAI,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC,IAAS;CAC1E,QACM;EACJ,OAAO;CACT;AACF;AAEA,SAAS,WAAW,MAA2B;CAC7C,MAAM,MAAM,SAA+B,MAAM,CAAC,CAAC;CAKnD,OAAO;EAAE,SAAS;EAAG,QAJN,MAAM,QAAQ,IAAI,MAAM,IACnC,IAAI,OAAO,QAAQ,MAAwB,CAAC,CAAC,KAAK,OAAO,EAAE,WAAW,YAAY,OAAO,EAAE,SAAS,YAAY,MAAM,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,OAAM,MAAK,OAAO,MAAM,QAAQ,CAAC,IACnL,CAAC;EAEwB,MADhB,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,KAAK,QAAQ,MAAmB,OAAO,MAAM,QAAQ,IAAI,CAAC;CACnE;AACpC;;;;;;AAOA,SAAgB,gBAAgB,OAA0C,eAA6B;CACrG,IAAI,CAAC,mBAAmB,MAAM,IAAI,GAChC,MAAM,IAAI,MAAM,IAAI,MAAM,KAAK,yEAAyE;CAC1G,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,WAAW,GACxD,MAAM,IAAI,MAAM,4BAA4B;CAC9C,IAAI,MAAM,MAAM,SAAS,WACvB,MAAM,IAAI,MAAM,iBAAiB,UAAU,UAAU;CACvD,KAAK,MAAM,QAAQ,MAAM,OAAO;EAC9B,IAAI,OAAO,SAAS,YAAY,CAAC,WAAW,IAAI,GAC9C,MAAM,IAAI,MAAM,GAAG,OAAO,IAAI,EAAE,iCAAiC;EACnE,cAAc,MAAM,aAAa;CACnC;AACF;AAEA,SAAgB,kBAAkB,SAA4C;CAC5E,MAAM,EAAE,SAAS,aAAa;CAC9B,MAAM,aAAa,KAAK,SAAS,WAAW;CAC5C,MAAM,aAAa,KAAK,SAAS,WAAW;CAC5C,MAAM,OAAO,gBAAgB,KAAK,SAAS,SAAS,CAAC;CAErD,MAAM,QAAQ,mBAAmB;EAAE;EAAS,OAAO,aAAa;GAAE,IAAI;GAAO,OAAO;GAAe,SAAS;EAAkB;CAAG,CAAC;CAClI,MAAM,WAAW,IAAI,QAAQ,UAAU,QAAA,CAAS,MAAM,GAAG,EAAE,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK;CAC7F,MAAM,SAAS;EACb,GAAI,QAAQ,eAAe,KAAA,IAAY,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;EAC7E,GAAI,QAAQ,eAAe,KAAA,IAAY,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;EAC7E,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;CAClF;CAEA,MAAM,yBAAS,IAAI,IAAuF;CAC1G,MAAM,uBAAO,IAAI,IAAgE;CACjF,IAAI,SAAS;CACb,IAAI,SAAS;CACb,IAAI,gBAAgB;CAEpB,MAAM,mBAAuC;EAAE,QAAQ,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,KAAI,MAAK,EAAE,MAAM;EAAG,MAAM,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAI,MAAK,EAAE,MAAM;CAAE;;CAG5I,MAAM,eAAmC;EACvC,MAAM,MAAM,UAAU;EACtB,IAAI,QACF,OAAO;EACT,MAAM,SAAS,WAAW,UAAU;EACpC,MAAM,YAAY,SAAsC,YAAY,CAAC,CAAC;EACtE,MAAM,QAAQ,IAAI,KAAK,UAAU,UAAU,CAAC,EAAA,CAAG,KAAI,MAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;EACtE,OAAO;GACL,QAAQ,OAAO,OAAO,KAAI,MAAK,MAAM,IAAI,EAAE,MAAM,KAAK;IAAE,QAAQ,EAAE;IAAQ,MAAM,EAAE;IAAM,OAAO,EAAE;IAAO,OAAO;IAAuB,QAAQ;GAA2D,CAAC;GAC1M,MAAM,IAAI;EACZ;CACF;CAEA,MAAM,gBAAsB;EAC1B,IAAI,QACF;EACF,MAAM,WAAW,OAAO;EACxB,MAAM,OAAO,KAAK,UAAU,QAAQ;EACpC,IAAI,QACF,IAAI;GACF,oBAAoB,YAAY,EAAE,QAAQ,SAAS,OAAO,GAAG,UAAU;EACzE,QACM,CAEN;EAEF,IAAI,SAAS,eACX;EACF,gBAAgB;EAChB,QAAQ,SAAS,QAAQ;CAC3B;CAEA,MAAM,SAAS,WAA8B;EAC3C,MAAM,QAAQ,MAAM,IAAI,MAAM;EAC9B,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,oBAAoB,OAAO,mCAAmC;EAChF,OAAO;CACT;CAEA,SAAS,SAAS,QAA2B;EAC3C,MAAM,SAA6B;GAAE,QAAQ,OAAO;GAAQ,MAAM,OAAO;GAAM,OAAO,OAAO;GAAO,OAAO;EAAa;EACxH,MAAM,QAAmF;GAAE;GAAQ;EAAO;EAC1G,OAAO,IAAI,OAAO,QAAQ,KAAK;EAC/B,MAAM,OAAO,OAAyB,WAA0B;GAC9D,MAAM,SAAS;IAAE,GAAG;IAAQ;IAAO,GAAI,UAAU,UAAU,cAAc,EAAE,OAAO,IAAI,CAAC;GAAG;GAC1F,QAAQ;EACV;EACA,IAAI;GACF,MAAM,SAAS,MAAM,OAAO,MAAM;GAClC,MAAM,OAAO,oBAAoB,SAAS,qBAAqB,OAAO,MAAM,CAAC;GAC7E,MAAM,QAAQ,MAAM,SAAS,WAAW,KAAK,QAAQ,KAAA;GACrD,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,8BAA8B,OAAO,KAAK,EAAE;GAC9D,MAAM,QAAQ,kBAAkB;IAAE,SAAS,OAAO;IAAK;IAAO,MAAM,OAAO;IAAM,OAAO,OAAO;IAAO,SAAS,KAAK,SAAS,SAAS;IAAG,oBAAoB;IAAS,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS,EAAE,IAAI,CAAC;IAAI,GAAG;IAAQ,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;IAAI,WAAW,OAAO,WAAW,IAAI,OAAO,MAAM;GAAE,CAAC;EACrW,SACO,KAAK;GACV,IAAI,WAAY,IAAc,OAAO;EACvC;CACF;CAEA,SAAS,OAAO,QAAsB;EACpC,MAAM,MAA0D,EAAE,QAAQ;GAAE;GAAQ,OAAO;EAAa,EAAE;EAC1G,KAAK,IAAI,QAAQ,GAAG;EACpB,MAAM,OAAO,OAAyB,QAAiB,gBAA+B;GACpF,IAAI,SAAS;IAAE;IAAQ;IAAO,GAAI,UAAU,UAAU,cAAc,EAAE,OAAO,IAAI,CAAC;IAAI,GAAI,eAAe,IAAI,OAAO,cAAc,EAAE,aAAa,eAAe,IAAI,OAAO,YAAY,IAAI,CAAC;GAAG;GAC/L,QAAQ;EACV;EACA,IAAI;GACF,MAAM,SAAS,MAAM,MAAM;GAC3B,MAAM,QAAQ,MAAM,MAAM,MAAM;GAChC,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,sBAAsB,OAAO,KAAK,gBAAgB;GACpE,IAAI,OAAO,mBAAmB;IAAE;IAAU,KAAK,OAAO;IAAK;IAAO,cAAc;IAAQ,aAAa;IAAU,GAAG;IAAQ,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;IAAI,SAAS;GAAI,CAAC;EAC7M,SACO,KAAK;GACV,IAAI,WAAY,IAAc,OAAO;EACvC;CACF;CAEA,MAAM,YAAY,OAAO,WAAkC;EACzD,MAAM,QAAQ,OAAO,IAAI,MAAM;EAC/B,OAAO,OAAO,MAAM;EACpB,MAAM,OAAO,OAAO,KAAK;CAC3B;;CAGA,SAAS,YAAkB;EACzB,IAAI,QACF;EACF,MAAM,SAAS,WAAW,UAAU;EACpC,SAAS,KAAK,QAAQ;EACtB,IAAI,CAAC,QAEH,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,KAAK,CAAC,GACpC,UAAe,MAAM;OAEpB;GACH,MAAM,SAAS,IAAI,IAAI,OAAO,OAAO,KAAI,MAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;GAC5D,KAAK,MAAM,CAAC,QAAQ,UAAU,QAAQ;IACpC,MAAM,OAAO,OAAO,IAAI,MAAM;IAC9B,IAAI,CAAC,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,MAAM,MAAM,GAC/D,UAAe,MAAM;GACzB;GACA,KAAK,MAAM,SAAS,OAAO,QACzB,IAAI,CAAC,OAAO,IAAI,MAAM,MAAM,GAC1B,SAAS,KAAK;EAEpB;EACA,MAAM,aAAa,IAAI,IAAI,OAAO,IAAI;EACtC,KAAK,MAAM,CAAC,QAAQ,QAAQ,MAC1B,IAAI,CAAC,WAAW,IAAI,MAAM,GAAG;GAC3B,IAAI,MAAM,KAAK;GACf,KAAK,OAAO,MAAM;EACpB;EAEF,KAAK,MAAM,UAAU,OAAO,MAC1B,IAAI,CAAC,KAAK,IAAI,MAAM,GAClB,OAAO,MAAM;EAEjB,QAAQ;CACV;CAEA,MAAM,eAAe,WAA8D;EACjF,MAAM,SAAS,WAAW,UAAU;EACpC,OAAO,MAAM;EACb,oBAAoB,YAAY,QAAQ,UAAU;EAClD,UAAU;EACV,OAAO,OAAO;CAChB;CAEA,UAAU;CACV,MAAM,QAAQ,YAAY,WAAW,QAAQ,UAAU,OAAO;CAC9D,MAAM,QAAQ;CAEd,OAAO;EACL;EACA,WAAW,OAAO;GAChB,gBAAgB,OAAO,OAAO;GAC9B,MAAM,SAAS,MAAM,MAAM,MAAM;GACjC,MAAM,QAAQ,MAAM,OAAO,KAAK;GAChC,IAAI,OACF,qBAAqB,SAAS,qBAAqB,MAAM,MAAM,GAAG;IAAE,MAAM;IAAU,OAAO;GAAM,CAAC;QAC/F,IAAI,oBAAoB,SAAS,qBAAqB,MAAM,MAAM,CAAC,CAAC,EAAE,SAAS,UAClF,MAAM,IAAI,MAAM,wBAAwB,OAAO,KAAK,8FAA8F;GACpJ,OAAO,aAAa,WAAW;IAC7B,OAAO,SAAS,CAAC,GAAG,OAAO,OAAO,QAAO,MAAK,EAAE,WAAW,MAAM,MAAM,GAAG;KAAE,QAAQ,MAAM;KAAQ,MAAM,MAAM;KAAM,OAAO,CAAC,GAAG,MAAM,KAAK;IAAE,CAAC;GAC/I,CAAC;EACH;EACA,UAAU,QAAQ;GAChB,OAAO,aAAa,WAAW;IAC7B,OAAO,SAAS,OAAO,OAAO,QAAO,MAAK,EAAE,WAAW,MAAM;GAC/D,CAAC;EACH;EACA,OAAO,QAAQ,SAAS;GACtB,IAAI,SAAS;IACX,MAAM,SAAS,MAAM,MAAM;IAC3B,IAAI,CAAC,MAAM,MAAM,MAAM,GACrB,MAAM,IAAI,MAAM,sBAAsB,OAAO,KAAK,gBAAgB;GACtE;GACA,OAAO,aAAa,WAAW;IAC7B,OAAO,OAAO,UAAU,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,OAAO,MAAM,MAAM,CAAC,CAAC,IAAI,OAAO,KAAK,QAAO,MAAK,MAAM,MAAM;GACvG,CAAC;EACH;EACA,MAAM,QAAQ;GACZ,IAAI,QACF;GACF,SAAS;GACT,cAAc,KAAK;GACnB,KAAK,MAAM,OAAO,KAAK,OAAO,GAC5B,IAAI,MAAM,KAAK;GACjB,KAAK,MAAM;GACX,MAAM,QAAQ,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;GAEnD,IAAI,KAAK,QAAQ,GACf,OAAO,YAAY,EAAE,OAAO,KAAK,CAAC;EACtC;CACF;AACF"}
1
+ {"version":3,"file":"links-Bto-5dVu.js","names":[],"sources":["../src/ipc/machine/agent.ts","../src/ipc/machine/reverse.ts","../src/ipc/machine/share-lock.ts","../src/ipc/machine/links.ts"],"sourcesContent":["/**\n * The machine agent (`docs/MACHINES.md` § Machine side): dials its host with\n * a machine-scoped token, registers, and serves the host's execution\n * requests through the confined executor (`./executor.ts`) until stopped.\n * Reconnects with the hosts' connection loop (backoff; a refused token, a\n * different host at the URL, or a refused registration stop it for good).\n *\n * The CLI (`./cli.ts`, `zidane machine share`) runs it in the foreground;\n * `./links.ts` starts it from the local daemon / in-process host.\n */\nimport type { HostConnectionStatus } from '../../chat/hosts/connection'\nimport type { MachineRegisterParams, MachineRegisterResult, MachineRoot } from '../../chat/machine-protocol'\nimport type { IpcClient } from '../transports/client-core'\nimport type { MachineExecutorOptions } from './executor'\nimport { mkdirSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { JsonRpcRemoteError } from '../../acp/json-rpc'\nimport { writeJsonFileAtomic } from '../../atomic-write'\nimport { HostIdentityError } from '../../chat/hosts/connection'\nimport { displayHostUrl } from '../../chat/hosts/url'\nimport { isValidMachineName, MACHINE_PROTOCOL_VERSION, MachineErrorCode } from '../../chat/machine-protocol'\nimport { ZIDANE_VERSION } from '../../version'\nimport { keepHostConnected } from '../hosts/connection-loop'\nimport { IpcVersionMismatchError } from '../transports/client-core'\nimport { connectWsClient, HostAuthError } from '../transports/ws-client'\nimport { createMachineExecutor, shareableRoot } from './executor'\nimport { machineIdentity } from './identity'\n\nexport { machineIdentity } from './identity'\n\nexport interface MachineAgentOptions {\n /** `ws://` / `wss://` URL of the host. */\n hostUrl: string\n /** A `machine`-scoped token of that host. */\n token: string\n /** Display name, and the suffix of its environment kinds (`local@<name>`). */\n name: string\n /** Folders the host may use; absolute, existing. */\n roots: ReadonlyArray<string | MachineRoot>\n /** This machine's data dir: its id, the host it pinned, task logs. Never reachable by the host's file ops. */\n dataDir: string\n appVersion?: string\n /** Every status change (`connected`, `reconnecting`, `auth-refused`, …) and why it failed. */\n onStatus?: (status: HostConnectionStatus, detail?: string) => void\n /** Backoff bounds and heartbeat (tests shrink them). */\n minDelayMs?: number\n maxDelayMs?: number\n heartbeatMs?: number\n terminalGraceMs?: number\n /** This machine user's `sandbox*` settings: every command the host sends runs under them (`docs/MACHINES.md` § Sandbox). */\n sandbox?: MachineExecutorOptions['sandbox']\n /** This machine user's zidane data dir: never shared (a root can't be in it, file ops never reach it), and its credential files are hidden from sandboxed commands. */\n credentialsDataDir?: string\n}\n\nexport interface MachineAgent {\n machineId: string\n status: () => HostConnectionStatus\n /** Reconnect now (after a fix on the host side). */\n retry: () => void\n stop: () => Promise<void>\n}\n\n/** A registration the host refused for good (its name taken, another protocol). */\nclass MachineRegisterError extends Error {\n constructor(message: string, readonly forGood: boolean) {\n super(message)\n this.name = 'MachineRegisterError'\n }\n}\n\nfunction normalizeRoots(roots: MachineAgentOptions['roots'], zidaneDataDir: string | undefined): MachineRoot[] {\n if (roots.length === 0)\n throw new Error('Share at least one folder (--root).')\n return roots.map((root) => {\n const entry = typeof root === 'string' ? { path: root } : root\n const real = shareableRoot(entry.path, zidaneDataDir)\n if (!statSync(real).isDirectory())\n throw new Error(`Root ${entry.path} is not a folder.`)\n return { path: real, ...(entry.label ? { label: entry.label } : {}) }\n })\n}\n\n/** The host id this machine pinned for `url` (the first host that answered there). */\nfunction pinnedHosts(dataDir: string): { get: (url: string) => string | undefined, set: (url: string, hostId: string) => void } {\n const file = join(dataDir, 'machine-hosts.json')\n const read = (): Record<string, string> => {\n try {\n return JSON.parse(readFileSync(file, 'utf8')) as Record<string, string>\n }\n catch {\n return {}\n }\n }\n return {\n get: url => read()[url],\n set: (url, hostId) => writeJsonFileAtomic(file, { ...read(), [url]: hostId }, { ensureDir: true, mode: 0o600 }),\n }\n}\n\nexport function startMachineAgent(options: MachineAgentOptions): MachineAgent {\n if (!isValidMachineName(options.name))\n throw new Error(`\"${options.name}\" is not a valid machine name: 1 to 64 letters, digits, \".\", \"-\" or \"_\".`)\n mkdirSync(options.dataDir, { recursive: true, mode: 0o700 })\n const dataDir = realpathSync.native(options.dataDir)\n const roots = normalizeRoots(options.roots, options.credentialsDataDir)\n const machineId = machineIdentity(dataDir)\n const pins = pinnedHosts(dataDir)\n const url = options.hostUrl\n let client: IpcClient | null = null\n let status: HostConnectionStatus = 'connecting'\n\n const executor = createMachineExecutor({\n roots,\n dataDir,\n ...(options.terminalGraceMs !== undefined ? { terminalGraceMs: options.terminalGraceMs } : {}),\n ...(options.sandbox ? { sandbox: options.sandbox } : {}),\n ...(options.credentialsDataDir ? { credentialsDataDir: options.credentialsDataDir } : {}),\n notify: (method, params) => {\n if (!client)\n return false\n try {\n (client.notify as unknown as (m: string, p: unknown) => void)(method, params)\n return true\n }\n catch {\n return false\n }\n },\n })\n\n const register: MachineRegisterParams = {\n machineId,\n name: options.name,\n roots,\n capabilities: { shell: true, terminals: typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined', background: true, worktrees: true, stream: true, sandboxed: executor.sandbox.on },\n version: options.appVersion ?? ZIDANE_VERSION,\n protocolVersion: MACHINE_PROTOCOL_VERSION,\n }\n\n const loop = keepHostConnected({\n minDelayMs: options.minDelayMs ?? 1000,\n maxDelayMs: options.maxDelayMs ?? 30_000,\n async connect({ signal, onClose }) {\n const expectHostId = pins.get(url)\n const connected = await connectWsClient({\n url,\n token: options.token,\n signal,\n onClose,\n appVersion: register.version,\n ...(options.heartbeatMs !== undefined ? { heartbeatMs: options.heartbeatMs } : {}),\n ...(expectHostId ? { expectHostId } : {}),\n serve: {\n request: (method, params) => executor.handle(method, params),\n notification: (method, params) => void executor.handle(method, params).catch(() => undefined),\n },\n })\n try {\n await (connected.request as unknown as (m: string, p: unknown) => Promise<MachineRegisterResult>)('machine.register', register)\n }\n catch (err) {\n connected.close()\n const code = err instanceof JsonRpcRemoteError ? err.code : undefined\n throw new MachineRegisterError(`${displayHostUrl(url)} refused this machine: ${(err as Error).message}`, code === MachineErrorCode.NameTaken || code === MachineErrorCode.VersionSkew || code === MachineErrorCode.IdBound)\n }\n if (!expectHostId && connected.hello.hostId)\n pins.set(url, connected.hello.hostId)\n return connected\n },\n classify: (err) => {\n if (err instanceof HostIdentityError)\n return 'identity'\n if (err instanceof HostAuthError || (err instanceof MachineRegisterError && err.forGood))\n return 'auth'\n return err instanceof IpcVersionMismatchError ? 'protocol' : 'retry'\n },\n update: (patch) => {\n if (patch.client !== undefined)\n client = patch.client\n if (patch.status) {\n status = patch.status\n options.onStatus?.(patch.status, patch.error)\n }\n },\n onConnected: () => executor.linkUp(),\n onDisconnected: () => executor.linkLost(),\n })\n\n return {\n machineId,\n status: () => status,\n retry: () => loop.retry(),\n async stop() {\n loop.stop()\n await executor.close()\n },\n }\n}\n","/**\n * A saved server used as a machine of this local host (`local@<server>`,\n * `docs/MACHINES.md` § Reverse direction). A local host has no listener, so\n * it dials the server as a client (its saved client token), asks it to lend\n * itself (`machine.attach`), and registers it in its own machine registry\n * with a connection that sends each host→machine request as `machine.call`.\n * The server's notifications come back as `machine:notification` signals.\n * Reconnects with the hosts' connection loop; a refused token, another host\n * at the URL, or a server that does not lend itself stop it for good.\n */\nimport type { AttachedMachine, MachineConnection, MachineRegistry } from '../../chat/host/machines'\nimport type { MachineLinkState } from '../../chat/machine-protocol'\nimport type { JsonValue } from '../../types'\nimport type { IpcClient } from '../transports/client-core'\nimport { JsonRpcConnectionClosedError, JsonRpcRemoteError } from '../../acp/json-rpc'\nimport { MachineLinkLostError } from '../../chat/host/machines'\nimport { HostIdentityError } from '../../chat/hosts/connection'\nimport { displayHostUrl } from '../../chat/hosts/url'\nimport { MachineErrorCode } from '../../chat/machine-protocol'\nimport { keepHostConnected } from '../hosts/connection-loop'\nimport { connectWsClient, HostAuthError } from '../transports/ws-client'\n\nexport interface ServerMachineOptions {\n machines: MachineRegistry\n url: string\n /** The saved server's client token. */\n token: string\n /** The saved server's host id: another host at the URL is refused. */\n expectHostId: string\n /** This host's id (the server keys its executor by it, so tasks survive a reconnect). */\n localHostId: string\n appVersion?: string\n onState: (state: MachineLinkState, detail?: string, machineName?: string) => void\n minDelayMs?: number\n maxDelayMs?: number\n heartbeatMs?: number\n}\n\nexport interface ServerMachine {\n retry: () => void\n stop: () => void\n}\n\n/** The server refused to lend itself (`allowAsMachine` off, a name taken here): for good. A busy server is retried. */\nclass AttachRefusedError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'AttachRefusedError'\n }\n}\n\nfunction connectionOver(client: IpcClient): MachineConnection {\n return {\n async request(method, params) {\n try {\n return await client.request('machine.call', { method, params: (params ?? {}) as JsonValue })\n }\n catch (err) {\n if (err instanceof JsonRpcConnectionClosedError)\n throw new MachineLinkLostError()\n throw err instanceof JsonRpcRemoteError ? new Error(err.message) : err\n }\n },\n notify: (method, params) => client.notify('machine.call', { method, params: (params ?? {}) as JsonValue }),\n close: () => client.close(),\n }\n}\n\nexport function startServerMachine(options: ServerMachineOptions): ServerMachine {\n let attached: AttachedMachine | undefined\n const where = displayHostUrl(options.url)\n\n const detach = (): void => {\n attached?.closed()\n attached = undefined\n }\n\n const loop = keepHostConnected({\n ...(options.minDelayMs !== undefined ? { minDelayMs: options.minDelayMs } : {}),\n ...(options.maxDelayMs !== undefined ? { maxDelayMs: options.maxDelayMs } : {}),\n async connect({ signal, onClose }) {\n const client = await connectWsClient({\n url: options.url,\n token: options.token,\n expectHostId: options.expectHostId,\n signal,\n onClose,\n ...(options.appVersion ? { appVersion: options.appVersion } : {}),\n ...(options.heartbeatMs !== undefined ? { heartbeatMs: options.heartbeatMs } : {}),\n })\n // Subscribed before attaching: the server re-sends queued task exits while it answers `machine.attach`.\n const early: Array<{ method: string, params: unknown }> = []\n let current: AttachedMachine | undefined\n client.onSignal((s) => {\n if (s.kind !== 'machine:notification')\n return\n if (current)\n current.notification(s.method, s.params)\n else if (early.length < 1024)\n early.push({ method: s.method, params: s.params })\n })\n try {\n const params = await client.request('machine.attach', { hostId: options.localHostId })\n detach()\n current = options.machines.attach(params, connectionOver(client))\n attached = current\n for (const { method, params: p } of early.splice(0))\n current.notification(method, p)\n options.onState('connected', undefined, params.name)\n }\n catch (err) {\n client.close()\n // No room there now (a capacity limit) or the link dropped: the loop retries with backoff.\n if (err instanceof JsonRpcConnectionClosedError || (err instanceof JsonRpcRemoteError && err.code === MachineErrorCode.Busy))\n throw new Error(`${where} can't lend itself right now: ${(err as Error).message}`)\n throw new AttachRefusedError(`${where} refused to be a machine: ${(err as Error).message}`)\n }\n return client\n },\n classify: err => (err instanceof HostIdentityError ? 'identity' : err instanceof HostAuthError || err instanceof AttachRefusedError ? 'auth' : 'retry'),\n update: (patch) => {\n if (!patch.status || patch.status === 'connected')\n return\n options.onState(patch.status === 'auth-refused' && patch.error?.includes('refused to be a machine') ? 'refused' : patch.status, patch.error)\n },\n onDisconnected: detach,\n })\n\n return {\n retry: () => loop.retry(),\n stop() {\n loop.stop()\n detach()\n },\n }\n}\n","/**\n * `machine-links.lock`: which process runs this computer's shares (one\n * machine id, one connection; two processes sharing it would keep replacing\n * each other's). Exclusive by construction, not by a read-then-write:\n *\n * - taking a free lock is an exclusive create (`wx`): one contender wins;\n * - the record names its holder (`pid`) and a per-process `nonce`; the\n * holder touches the file on every check, so a holder is stale when its\n * pid is gone OR the file went untouched for {@link STALE_MS} (a reused\n * pid never touches it);\n * - a stale lock is moved aside (an atomic rename to a name of our own),\n * checked to be the very record judged stale, then the exclusive create\n * runs again; a live lock moved aside by mistake is put back without\n * clobbering (`link`, which fails if the name is taken);\n * - a holder re-reads the record on every check: another nonce there means\n * it lost the lock, and the caller stops its shares;\n * - release removes the file only when it still carries this nonce.\n */\nimport { randomBytes } from 'node:crypto'\nimport { closeSync, linkSync, openSync, readFileSync, renameSync, rmSync, statSync, utimesSync, writeSync } from 'node:fs'\nimport process from 'node:process'\n\n/** A holder that has not touched its lock for this long is gone (its pid may be reused). */\nexport const SHARE_LOCK_STALE_MS = 30_000\n\ninterface LockRecord {\n pid: number\n nonce: string\n}\n\nexport interface ShareLock {\n /** Hold the lock (taking it when free or stale); `false` when another live process holds it. */\n acquire: () => boolean\n /** Whether this process held it at the last {@link acquire}. */\n held: () => boolean\n /** Remove the lock when this process still holds it; whether it did. */\n release: () => boolean\n}\n\nexport interface ShareLockOptions {\n /** Test seams. */\n pid?: number\n alive?: (pid: number) => boolean\n now?: () => number\n staleMs?: number\n}\n\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n }\n catch (err) {\n return (err as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\nfunction readRecord(file: string): { record?: LockRecord, mtimeMs: number } | undefined {\n try {\n const mtimeMs = statSync(file).mtimeMs\n const raw = JSON.parse(readFileSync(file, 'utf8')) as Partial<LockRecord>\n return { mtimeMs, ...(typeof raw.pid === 'number' && typeof raw.nonce === 'string' ? { record: { pid: raw.pid, nonce: raw.nonce } } : {}) }\n }\n catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT')\n return undefined\n // Unreadable or half-written: judged by its age alone.\n try {\n return { mtimeMs: statSync(file).mtimeMs }\n }\n catch {\n return undefined\n }\n }\n}\n\nexport function createShareLock(file: string, options: ShareLockOptions = {}): ShareLock {\n const pid = options.pid ?? process.pid\n const alive = options.alive ?? processAlive\n const now = options.now ?? Date.now\n const staleMs = options.staleMs ?? SHARE_LOCK_STALE_MS\n const nonce = randomBytes(12).toString('hex')\n let holding = false\n\n const create = (): boolean => {\n let fd: number\n try {\n fd = openSync(file, 'wx', 0o600)\n }\n catch {\n return false\n }\n try {\n writeSync(fd, JSON.stringify({ pid, nonce }))\n }\n finally {\n closeSync(fd)\n }\n return true\n }\n\n const ours = (): boolean => readRecord(file)?.record?.nonce === nonce\n\n const stale = (found: { record?: LockRecord, mtimeMs: number }): boolean =>\n now() - found.mtimeMs > staleMs || (found.record !== undefined && !alive(found.record.pid))\n\n /** Move a stale lock aside; `true` when the record moved is the one judged stale. */\n const evict = (judged: { record?: LockRecord, mtimeMs: number }): boolean => {\n const aside = `${file}.${nonce}.stale`\n try {\n renameSync(file, aside)\n }\n catch {\n return false\n }\n const moved = readRecord(aside)\n const same = moved?.record?.nonce === judged.record?.nonce && moved?.mtimeMs === judged.mtimeMs\n if (!same) {\n // A live lock taken meanwhile: put it back, never over a newer one.\n try {\n linkSync(aside, file)\n }\n catch {\n // Someone holds the name again; that holder's own check settles it.\n }\n }\n rmSync(aside, { force: true })\n return same\n }\n\n return {\n acquire() {\n if (holding) {\n if (ours()) {\n try {\n const at = new Date(now())\n utimesSync(file, at, at)\n }\n catch {\n // The touch is best effort; the record still names this process.\n }\n return true\n }\n holding = false\n }\n if (create()) {\n holding = true\n return true\n }\n const found = readRecord(file)\n if (found && !stale(found))\n return false\n if (found && !evict(found))\n return false\n holding = create()\n return holding\n },\n held: () => holding,\n release() {\n const mine = holding && ours()\n if (mine)\n rmSync(file, { force: true })\n holding = false\n return mine\n },\n }\n}\n","/**\n * A local host's machine links (`docs/MACHINES.md` § Client UI), loaded only\n * behind remote hosts:\n *\n * - **Shares**: this computer lent to a saved server (a machine agent in\n * this process, with the machine token stored under `machine:<hostId>`).\n * One process runs them for the whole computer: the holder of\n * `machine-links.lock` (the daemon, or the first window's in-process\n * host; exclusive, `./share-lock.ts`). Two processes sharing one computer\n * under one machine id would keep replacing each other's connection, so a\n * process that finds it lost the lock stops its shares at once.\n * - **Uses**: a saved server lent to THIS host (`local@<server>`), over its\n * own client connection. Per host process: each host has its own machine\n * registry, so each attaches under its own instance id.\n *\n * The wanted links live in `<dataDir>/machine-links.json` (any process writes\n * it); every process re-reads it every {@link POLL_MS}, the holder applies the\n * shares and publishes their state in `machine-links-status.json`. Tokens\n * are never in either file.\n */\nimport type { MachineRegistry } from '../../chat/host/machines'\nimport type { HostEntry } from '../../chat/hosts/types'\nimport type { MachineLinksStatus, MachineLinkState, MachineShareStatus, MachineUseStatus } from '../../chat/machine-protocol'\nimport type { LooseSandboxSettings } from '../../chat/sandbox-settings'\nimport type { MachineAgent } from './agent'\nimport type { ServerMachine } from './reverse'\nimport { randomBytes } from 'node:crypto'\nimport { existsSync, readFileSync, rmSync } from 'node:fs'\nimport { isAbsolute, join } from 'node:path'\nimport { OWNER_ONLY, writeJsonFileAtomic } from '../../atomic-write'\nimport { readCredentialEntry, writeCredentialEntry } from '../../chat/credentials'\nimport { createHostRegistry } from '../../chat/hosts'\nimport { isValidMachineName, machineCredentialKey } from '../../chat/machine-protocol'\nimport { startMachineAgent } from './agent'\nimport { shareableRoot } from './executor'\nimport { startServerMachine } from './reverse'\nimport { createShareLock } from './share-lock'\n\nconst POLL_MS = 2000\nconst CONFIG_FILE = 'machine-links.json'\nconst STATUS_FILE = 'machine-links-status.json'\nconst LOCK_FILE = 'machine-links.lock'\nconst MAX_ROOTS = 32\n\ninterface ShareConfig {\n hostId: string\n name: string\n roots: string[]\n}\n\ninterface LinksConfig {\n version: 1\n shares: ShareConfig[]\n uses: string[]\n}\n\nexport interface MachineLinksOptions {\n /** The host's data dir: saved hosts, credentials, these files. */\n dataDir: string\n machines: MachineRegistry\n /** This host's id (`ipc.hello`). */\n hostId: string | undefined\n /** This user's settings: a share applies their `sandbox*` to what it runs (`docs/MACHINES.md` § Sandbox). */\n settings?: () => LooseSandboxSettings\n /** Every status change, for `machine:links`. */\n onChange: (status: MachineLinksStatus) => void\n appVersion?: string\n /** Connection pacing (tests shrink them). */\n minDelayMs?: number\n maxDelayMs?: number\n heartbeatMs?: number\n pollMs?: number\n}\n\nexport interface MachineLinks {\n status: () => MachineLinksStatus\n startShare: (input: { hostId: string, name: string, roots: string[], token?: string }) => MachineLinksStatus\n stopShare: (hostId: string) => MachineLinksStatus\n setUse: (hostId: string, enabled: boolean) => MachineLinksStatus\n /** Stop everything (remote hosts turned off, or the host closes); the wanted links stay saved. */\n close: () => Promise<void>\n}\n\nfunction readJson<T>(file: string, fallback: T): T {\n try {\n return existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) as T : fallback\n }\n catch {\n return fallback\n }\n}\n\nfunction readConfig(file: string): LinksConfig {\n const raw = readJson<Partial<LinksConfig>>(file, {})\n const shares = Array.isArray(raw.shares)\n ? raw.shares.filter((s): s is ShareConfig => !!s && typeof s.hostId === 'string' && typeof s.name === 'string' && Array.isArray(s.roots) && s.roots.every(r => typeof r === 'string'))\n : []\n const uses = Array.isArray(raw.uses) ? raw.uses.filter((u): u is string => typeof u === 'string') : []\n return { version: 1, shares, uses }\n}\n\n/**\n * A share's name and roots, as a client sent them: every root absolute,\n * existing, and not (by its real path) zidane's data dir, inside it, or a\n * credentials folder (`./executor`'s `shareableRoot`).\n */\nexport function checkShareInput(input: { name: string, roots: string[] }, zidaneDataDir: string): void {\n if (!isValidMachineName(input.name))\n throw new Error(`\"${input.name}\" is not a valid machine name: 1 to 64 letters, digits, \".\", \"-\" or \"_\".`)\n if (!Array.isArray(input.roots) || input.roots.length === 0)\n throw new Error('Share at least one folder.')\n if (input.roots.length > MAX_ROOTS)\n throw new Error(`Share at most ${MAX_ROOTS} folders.`)\n for (const root of input.roots) {\n if (typeof root !== 'string' || !isAbsolute(root))\n throw new Error(`${String(root)} is not an absolute folder path.`)\n shareableRoot(root, zidaneDataDir)\n }\n}\n\nexport function startMachineLinks(options: MachineLinksOptions): MachineLinks {\n const { dataDir, machines } = options\n const configFile = join(dataDir, CONFIG_FILE)\n const statusFile = join(dataDir, STATUS_FILE)\n const lock = createShareLock(join(dataDir, LOCK_FILE))\n // Reading entries and tokens needs no probe: nothing is dialed here but the links themselves.\n const hosts = createHostRegistry({ dataDir, probe: async () => ({ ok: false, error: 'unreachable', message: 'not probed here' }) })\n const instance = `${(options.hostId ?? 'local').slice(0, 40)}-${randomBytes(4).toString('hex')}`\n const pacing = {\n ...(options.minDelayMs !== undefined ? { minDelayMs: options.minDelayMs } : {}),\n ...(options.maxDelayMs !== undefined ? { maxDelayMs: options.maxDelayMs } : {}),\n ...(options.heartbeatMs !== undefined ? { heartbeatMs: options.heartbeatMs } : {}),\n }\n\n const shares = new Map<string, { config: ShareConfig, agent?: MachineAgent, status: MachineShareStatus }>()\n const uses = new Map<string, { link?: ServerMachine, status: MachineUseStatus }>()\n let holder = false\n let closed = false\n let lastPublished = ''\n\n const ownStatus = (): MachineLinksStatus => ({ shares: [...shares.values()].map(s => s.status), uses: [...uses.values()].map(u => u.status) })\n\n /** Shares as the holder reports them (another process may run them). */\n const status = (): MachineLinksStatus => {\n const own = ownStatus()\n if (holder)\n return own\n const config = readConfig(configFile)\n const published = readJson<Partial<MachineLinksStatus>>(statusFile, {})\n const known = new Map((published.shares ?? []).map(s => [s.hostId, s]))\n return {\n shares: config.shares.map(s => known.get(s.hostId) ?? { hostId: s.hostId, name: s.name, roots: s.roots, state: 'connecting' as const, detail: 'waiting for the zidane process that shares this computer' }),\n uses: own.uses,\n }\n }\n\n const publish = (): void => {\n if (closed)\n return\n const snapshot = status()\n const text = JSON.stringify(snapshot)\n if (holder) {\n try {\n writeJsonFileAtomic(statusFile, { shares: snapshot.shares }, OWNER_ONLY)\n }\n catch {\n // Best effort: other processes show \"waiting\" until it lands.\n }\n }\n if (text === lastPublished)\n return\n lastPublished = text\n options.onChange(snapshot)\n }\n\n const entry = (hostId: string): HostEntry => {\n const found = hosts.get(hostId)\n if (!found)\n throw new Error(`No saved server \"${hostId}\": add it on the Hosts page first.`)\n return found\n }\n\n function runShare(config: ShareConfig): void {\n const status: MachineShareStatus = { hostId: config.hostId, name: config.name, roots: config.roots, state: 'connecting' }\n const share: { config: ShareConfig, agent?: MachineAgent, status: MachineShareStatus } = { config, status }\n shares.set(config.hostId, share)\n const set = (state: MachineLinkState, detail?: string): void => {\n share.status = { ...status, state, ...(detail && state !== 'connected' ? { detail } : {}) }\n publish()\n }\n try {\n const server = entry(config.hostId)\n const cred = readCredentialEntry(dataDir, machineCredentialKey(config.hostId))\n const token = cred?.kind === 'apikey' ? cred.value : undefined\n if (!token)\n throw new Error(`No machine token saved for ${server.name}.`)\n share.agent = startMachineAgent({ hostUrl: server.url, token, name: config.name, roots: config.roots, dataDir: join(dataDir, 'machine'), credentialsDataDir: dataDir, ...(options.settings ? { sandbox: options.settings() } : {}), ...pacing, ...(options.appVersion ? { appVersion: options.appVersion } : {}), onStatus: (state, detail) => set(state, detail) })\n }\n catch (err) {\n set('refused', (err as Error).message)\n }\n }\n\n function runUse(hostId: string): void {\n const use: { link?: ServerMachine, status: MachineUseStatus } = { status: { hostId, state: 'connecting' } }\n uses.set(hostId, use)\n const set = (state: MachineLinkState, detail?: string, machineName?: string): void => {\n use.status = { hostId, state, ...(detail && state !== 'connected' ? { detail } : {}), ...(machineName ?? use.status.machineName ? { machineName: machineName ?? use.status.machineName } : {}) }\n publish()\n }\n try {\n const server = entry(hostId)\n const token = hosts.token(hostId)\n if (!token)\n throw new Error(`No token saved for ${server.name}: add it again.`)\n use.link = startServerMachine({ machines, url: server.url, token, expectHostId: hostId, localHostId: instance, ...pacing, ...(options.appVersion ? { appVersion: options.appVersion } : {}), onState: set })\n }\n catch (err) {\n set('refused', (err as Error).message)\n }\n }\n\n const stopShare = async (hostId: string): Promise<void> => {\n const share = shares.get(hostId)\n shares.delete(hostId)\n await share?.agent?.stop()\n }\n\n /** Bring the running links in line with the saved config. */\n function reconcile(): void {\n if (closed)\n return\n const config = readConfig(configFile)\n holder = lock.acquire()\n if (!holder) {\n // Lost the lock (or never had it): another process runs this computer's shares, never two.\n for (const hostId of [...shares.keys()])\n void stopShare(hostId)\n }\n else {\n const wanted = new Map(config.shares.map(s => [s.hostId, s]))\n for (const [hostId, share] of shares) {\n const want = wanted.get(hostId)\n if (!want || JSON.stringify(want) !== JSON.stringify(share.config))\n void stopShare(hostId)\n }\n for (const share of config.shares) {\n if (!shares.has(share.hostId))\n runShare(share)\n }\n }\n const wantedUses = new Set(config.uses)\n for (const [hostId, use] of uses) {\n if (!wantedUses.has(hostId)) {\n use.link?.stop()\n uses.delete(hostId)\n }\n }\n for (const hostId of config.uses) {\n if (!uses.has(hostId))\n runUse(hostId)\n }\n publish()\n }\n\n const writeConfig = (mutate: (config: LinksConfig) => void): MachineLinksStatus => {\n const config = readConfig(configFile)\n mutate(config)\n writeJsonFileAtomic(configFile, config, OWNER_ONLY)\n reconcile()\n return status()\n }\n\n reconcile()\n const timer = setInterval(reconcile, options.pollMs ?? POLL_MS)\n timer.unref?.()\n\n return {\n status,\n startShare(input) {\n checkShareInput(input, dataDir)\n const server = entry(input.hostId)\n const token = input.token?.trim()\n if (token)\n writeCredentialEntry(dataDir, machineCredentialKey(input.hostId), { kind: 'apikey', value: token })\n else if (readCredentialEntry(dataDir, machineCredentialKey(input.hostId))?.kind !== 'apikey')\n throw new Error(`No machine token for ${server.name}: create one there (zidane server token create --scope machine) or let this window create it.`)\n return writeConfig((config) => {\n config.shares = [...config.shares.filter(s => s.hostId !== input.hostId), { hostId: input.hostId, name: input.name, roots: [...input.roots] }]\n })\n },\n stopShare(hostId) {\n return writeConfig((config) => {\n config.shares = config.shares.filter(s => s.hostId !== hostId)\n })\n },\n setUse(hostId, enabled) {\n if (enabled) {\n const server = entry(hostId)\n if (!hosts.token(hostId))\n throw new Error(`No token saved for ${server.name}: add it again.`)\n }\n return writeConfig((config) => {\n config.uses = enabled ? [...new Set([...config.uses, hostId])] : config.uses.filter(u => u !== hostId)\n })\n },\n async close() {\n if (closed)\n return\n closed = true\n clearInterval(timer)\n for (const use of uses.values())\n use.link?.stop()\n uses.clear()\n await Promise.all([...shares.keys()].map(stopShare))\n // Only the holder's own files: a process that lost the lock leaves the new holder's alone.\n if (lock.release())\n rmSync(statusFile, { force: true })\n },\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { l as formatDuration, m as shortId, n as ageString, o as fmtTokens, u as formatElapsed } from "./format-YgaIcuQ5.js";
2
- import { S as parseModelSetting } from "./spaces-CyjMh5Zf.js";
2
+ import { S as parseModelSetting } from "./spaces-C_x2bvEl.js";
3
3
  import { a as searchHighlightRanges } from "./history-search-CbDaXIfU.js";
4
4
  import { Fzf } from "fzf";
5
5
  //#region src/chat/turn-selection.ts
@@ -1795,4 +1795,4 @@ function enabledMcpsWithout(list, name) {
1795
1795
  //#endregion
1796
1796
  export { isKillSwitchEngaged as $, buildTextModel as A, createChatsCompletionProvider as B, BROWSER_OPS as C, retagUserPromptEvents as Ct, BROWSER_WAIT_TIMEOUT_MS as D, streamEventSearchText as Dt, BROWSER_TEXT_MAX_CHARS as E, selectionAfterTurnRemoval as Et, CONTINUE_PROMPT as F, compactingParts as G, cacheBreakChip as H, mayHaveStreamFailure as I, runStatusCells as J, liveSessionCost as K, splitStreamFailure as L, currentModelKeys as M, currentModels as N, BrowserOpError as O, turnSelectionOwnership as Ot, parseModelId as P, engagedKillSwitches as Q, CHATS_TRIGGER as R, BROWSER_OPEN_MOUNT_MS as S, promptTurnRefs as St, BROWSER_REQUEST_SLACK_MS as T, selectableTurnIds as Tt, cacheBreakNoticeText as U, uniqueChatIdsFromReferences as V, compactingLabel as W, daemonGate as X, FEATURE_GATES as Y, defineFeatureGate as Z, TERMINAL_VIEW_SCROLLBACK as _, isUserBashEvent as _t, EMPTY_MODES as a, isValidMachineName as at, BROWSER_EVALUATE_MAX_BYTES as b, messageSelectionOwnership as bt, modePrefixLength as c, parseMachineKind as ct, TERMINAL_MAX_PER_SCOPE as d, CACHE_NOTICE_LABEL as dt, remoteHostsGate as et, TERMINAL_MAX_PER_SESSION as f, EDIT_TOOL_NAMES as ft, TERMINAL_VIEW_MAX_LINES as g, isTurnHighlighted as gt, TERMINAL_TEXT_MAX_LINES as h, isEditErrorResult as ht, sliceEnd as i, MachineErrorCode as it, compareModelVersion as j, autoTextModelId as k, scanModes as l, toMachineName as lt, TERMINAL_READ_MAX_CHARS as m, isCacheNotice as mt, enabledMcpsWithout as n, MACHINE_PROTOCOL_VERSION as nt, isMouseTracking as o, machineCredentialKey as ot, TERMINAL_OUTPUT_WINDOW as p, REASONING_NOTICE_LABEL as pt, retryLabel as q, OutputHistory as r, MACHINE_STREAM_CHUNK_BYTES as rt, modePrefix as s, machineKind as st, enabledMcpsWith as t, MACHINE_IDEMPOTENT_METHODS as tt, TERMINAL_HISTORY_MAX_CHARS as u, BASH_CALL_ID_PREFIX as ut, TerminalError as v, isVisible as vt, BROWSER_OUTLINE_MAX_ELEMENTS as w, retagUserPromptEventsFromRefs as wt, BROWSER_MAX_WAIT_MS as x, promptHistoryFromEvents as xt, BROWSER_ACTION_TIMEOUT_MS as y, isVisibleDuringSearch as yt, chatReferenceTokensFromText as z };
1797
1797
 
1798
- //# sourceMappingURL=mcp-allowlist-CLXweyxk.js.map
1798
+ //# sourceMappingURL=mcp-allowlist-BbnTBKNN.js.map