zidane 7.2.5 → 8.0.4
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/LICENSE +21 -0
- package/README.md +34 -6
- package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
- package/dist/acp-DQDhi2dj.js.map +1 -0
- package/dist/acp-cli.d.ts +1 -1
- package/dist/acp-cli.js +9 -66
- package/dist/acp-cli.js.map +1 -1
- package/dist/acp.d.ts +10 -590
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +3 -3
- package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
- package/dist/agent-DSLNmBp0.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +3 -2
- package/dist/aliasing-lVFbm81n.js +217 -0
- package/dist/aliasing-lVFbm81n.js.map +1 -0
- package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
- package/dist/anthropic-CD00LhUP.js.map +1 -0
- package/dist/artifacts-CV2hUKnL.js.map +1 -1
- package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
- package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
- package/dist/call-tool-dHlbGOIY.js +70 -0
- package/dist/call-tool-dHlbGOIY.js.map +1 -0
- package/dist/chat/pure.d.ts +372 -6
- package/dist/chat/pure.d.ts.map +1 -1
- package/dist/chat/pure.js +35 -244
- package/dist/chat/pure.js.map +1 -1
- package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
- package/dist/chat-JPJJ8uu5.js.map +1 -0
- package/dist/chat.d.ts +4 -6
- package/dist/chat.js +23 -12
- package/dist/cli-shared-C-YCaglL.js +159 -0
- package/dist/cli-shared-C-YCaglL.js.map +1 -0
- package/dist/client-core-BQSyDALT.d.ts +2115 -0
- package/dist/client-core-BQSyDALT.d.ts.map +1 -0
- package/dist/codemode-worker.d.ts +1 -0
- package/dist/codemode-worker.js +155 -0
- package/dist/codemode-worker.js.map +1 -0
- package/dist/codemode.d.ts +352 -0
- package/dist/codemode.d.ts.map +1 -0
- package/dist/codemode.js +5 -0
- package/dist/connection-Dr-Js4yZ.d.ts +28 -0
- package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
- package/dist/connection-loop-gdh22LTI.js +112 -0
- package/dist/connection-loop-gdh22LTI.js.map +1 -0
- package/dist/context-segments-BHKlagL6.js +1016 -0
- package/dist/context-segments-BHKlagL6.js.map +1 -0
- package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
- package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +19 -8
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +1 -440
- package/dist/contexts/docker.d.ts +36 -7
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +1 -456
- package/dist/contexts/e2b.d.ts +68 -13
- package/dist/contexts/e2b.d.ts.map +1 -1
- package/dist/contexts/e2b.js +1 -382
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/contexts/sandbox.js +5 -0
- package/dist/contexts/sandbox.js.map +1 -1
- package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
- package/dist/contexts-d7UolYot.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/cred-keys-GpBg71cP.js +105 -0
- package/dist/cred-keys-GpBg71cP.js.map +1 -0
- package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
- package/dist/credentials-JttEuRON.js.map +1 -0
- package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
- package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
- package/dist/daemon-cli.d.ts +1 -1
- package/dist/daemon-cli.js +3 -2
- package/dist/daemon-cli.js.map +1 -1
- package/dist/daytona-DG3omipZ.js +513 -0
- package/dist/daytona-DG3omipZ.js.map +1 -0
- package/dist/defaults-Cx3zvDOV.js +23 -0
- package/dist/defaults-Cx3zvDOV.js.map +1 -0
- package/dist/dispatcher-BMlu2BmZ.js +4941 -0
- package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
- package/dist/docker-BZhMMJUp.js +727 -0
- package/dist/docker-BZhMMJUp.js.map +1 -0
- package/dist/e2b-CEVOdRib.js +480 -0
- package/dist/e2b-CEVOdRib.js.map +1 -0
- package/dist/edit-utils-EGosADZq.js +234 -0
- package/dist/edit-utils-EGosADZq.js.map +1 -0
- package/dist/environment-8wsKE2Ln.js +214 -0
- package/dist/environment-8wsKE2Ln.js.map +1 -0
- package/dist/eval-BNcvle_J.js +1595 -0
- package/dist/eval-BNcvle_J.js.map +1 -0
- package/dist/eval.d.ts +2 -2
- package/dist/eval.js +1 -1489
- package/dist/executor-Cx5-8yqi.js +621 -0
- package/dist/executor-Cx5-8yqi.js.map +1 -0
- package/dist/extension-authoring-DyGGmq9V.js +385 -0
- package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +3 -3
- package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
- package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
- package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
- package/dist/format-C2NQEVUH.js.map +1 -0
- package/dist/format-bytes-C7tfc3Zm.js +55 -0
- package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
- package/dist/headless-C_rNUuYC.js +960 -0
- package/dist/headless-C_rNUuYC.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -413
- package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
- package/dist/history-search-CcYzF8-0.js.map +1 -0
- package/dist/history-search-worker.d.ts +1 -1
- package/dist/history-search-worker.js +1 -1
- package/dist/host-D7NrI03V.js +378 -0
- package/dist/host-D7NrI03V.js.map +1 -0
- package/dist/host-signals-BcP5QKUD.js +118 -0
- package/dist/host-signals-BcP5QKUD.js.map +1 -0
- package/dist/identity-CwfO8K-t.js +26 -0
- package/dist/identity-CwfO8K-t.js.map +1 -0
- package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
- package/dist/index-BGe1fQT7.d.ts.map +1 -0
- package/dist/index-BVQsIKGn.d.ts +448 -0
- package/dist/index-BVQsIKGn.d.ts.map +1 -0
- package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
- package/dist/index-CHABJd-L.d.ts.map +1 -0
- package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
- package/dist/index-D6a7qXpg.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +27 -23
- package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
- package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
- package/dist/ipc/remote.d.ts +313 -0
- package/dist/ipc/remote.d.ts.map +1 -0
- package/dist/ipc/remote.js +4 -0
- package/dist/ipc.d.ts +6 -964
- package/dist/ipc.js +403 -4
- package/dist/ipc.js.map +1 -0
- package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
- package/dist/isolation-D9L650W5.d.ts.map +1 -0
- package/dist/json-rpc--HXQti1E.js +350 -0
- package/dist/json-rpc--HXQti1E.js.map +1 -0
- package/dist/links-j8rED1sR.js +603 -0
- package/dist/links-j8rED1sR.js.map +1 -0
- package/dist/local-terminal-D8QThwo_.js +252 -0
- package/dist/local-terminal-D8QThwo_.js.map +1 -0
- package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
- package/dist/logger-Gtqlayx8.js.map +1 -0
- package/dist/logs-view-CWG8aUAG.js.map +1 -1
- package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
- package/dist/mcp-_wecH9Qe.js.map +1 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +2 -2
- package/dist/media-sniff-UKijEaiQ.js.map +1 -1
- package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
- package/dist/messages-BqiwcD2Q.js.map +1 -0
- package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
- package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
- package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
- package/dist/openai-compat-CFrpH7uq.js.map +1 -0
- package/dist/orchestration-BCGmB8Ps.js +511 -0
- package/dist/orchestration-BCGmB8Ps.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -2
- package/dist/output/stream-json.d.ts.map +1 -1
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +1 -2
- package/dist/output/terminal.d.ts.map +1 -1
- package/dist/output/terminal.js +13 -7
- package/dist/output/terminal.js.map +1 -1
- package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
- package/dist/plugins-DS7mahzZ.js.map +1 -0
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +1 -2
- package/dist/prelude-Cxo4t74B.js +517 -0
- package/dist/prelude-Cxo4t74B.js.map +1 -0
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -2
- package/dist/project-root-B8zADr4o.js +167 -0
- package/dist/project-root-B8zADr4o.js.map +1 -0
- package/dist/project-user-paths-BSgtrRxl.js +58 -0
- package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
- package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
- package/dist/prompts-DYcvqShf.d.ts.map +1 -0
- package/dist/protocol-DnhqqSmE.js +13 -0
- package/dist/protocol-DnhqqSmE.js.map +1 -0
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +1 -2
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +3 -2
- package/dist/providers/cerebras.js.map +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -2
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +15 -12
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +3 -2
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +3 -3
- package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
- package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/registry-D6ShBWJU.js +5234 -0
- package/dist/registry-D6ShBWJU.js.map +1 -0
- package/dist/remote-hosts-BtA_9ZGp.js +97 -0
- package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
- package/dist/restate.d.ts +2 -3
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +631 -572
- package/dist/restate.js.map +1 -1
- package/dist/run-summary-CklLiZ4y.js +288 -0
- package/dist/run-summary-CklLiZ4y.js.map +1 -0
- package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
- package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
- package/dist/session/sqlite.d.ts +1 -2
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +1 -616
- package/dist/session-factory-BUGY5P40.js +1576 -0
- package/dist/session-factory-BUGY5P40.js.map +1 -0
- package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
- package/dist/session-xh9JXu6i.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/settings-keys-Ckf29NeM.js +677 -0
- package/dist/settings-keys-Ckf29NeM.js.map +1 -0
- package/dist/shell-path-Dqaoow2J.js +412 -0
- package/dist/shell-path-Dqaoow2J.js.map +1 -0
- package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
- package/dist/skills-DKqZJm4X.js.map +1 -0
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -2
- package/dist/source-mPExaUm8.js +381 -0
- package/dist/source-mPExaUm8.js.map +1 -0
- package/dist/sqlite-DV1DQzN2.js +698 -0
- package/dist/sqlite-DV1DQzN2.js.map +1 -0
- package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
- package/dist/src-BRMYRJib.js.map +1 -0
- package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
- package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
- package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
- package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
- package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
- package/dist/timeout-DEaqslLO.d.ts.map +1 -1
- package/dist/todo-edits-Dn251YUP.js +1790 -0
- package/dist/todo-edits-Dn251YUP.js.map +1 -0
- package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
- package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
- package/dist/tool-state-Dku6YqAB.js +989 -0
- package/dist/tool-state-Dku6YqAB.js.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -2
- package/dist/tools/fetch-url.d.ts.map +1 -1
- package/dist/tools/fetch-url.js +1 -2
- package/dist/tools/prompts.d.ts +1 -1
- package/dist/tools/prompts.js +7 -7
- package/dist/tools/prompts.js.map +1 -1
- package/dist/tools/web-search.d.ts +1 -2
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +2 -2
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools-_DnT3AGM.js +578 -0
- package/dist/tools-_DnT3AGM.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +4 -2
- package/dist/tui.d.ts +534 -517
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +34933 -27243
- package/dist/tui.js.map +1 -1
- package/dist/turn-operations-B8sokwi5.js +333 -0
- package/dist/turn-operations-B8sokwi5.js.map +1 -0
- package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
- package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
- package/dist/types-C7ZFOztE.d.ts +132 -0
- package/dist/types-C7ZFOztE.d.ts.map +1 -0
- package/dist/types-CH5XpIHw.js +114 -0
- package/dist/types-CH5XpIHw.js.map +1 -0
- package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
- package/dist/types-Cu0ZGDu9.js.map +1 -0
- package/dist/types-IqegbJEE.d.ts +624 -0
- package/dist/types-IqegbJEE.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.js +1 -1
- package/dist/url-D8HCl65b.js +1259 -0
- package/dist/url-D8HCl65b.js.map +1 -0
- package/dist/wasm-BVT-j5cl.js +33 -0
- package/dist/wasm-BVT-j5cl.js.map +1 -0
- package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
- package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
- package/dist/ws-CIHMvlSh.js +952 -0
- package/dist/ws-CIHMvlSh.js.map +1 -0
- package/dist/ws-client-N7NxzvQp.js +230 -0
- package/dist/ws-client-N7NxzvQp.js.map +1 -0
- package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
- package/dist/xai-CvJTeLrI.js.map +1 -0
- package/dist/xdg-zweed0-t.js +117 -0
- package/dist/xdg-zweed0-t.js.map +1 -0
- package/docs/ACP.md +52 -0
- package/docs/ARCHITECTURE.md +103 -6
- package/docs/ARTIFACTS.md +8 -4
- package/docs/CHAT.md +165 -14
- package/docs/EXECUTION_CONTEXT.md +8 -0
- package/docs/EXTENSIONS.md +123 -2
- package/docs/HEADLESS.md +2 -0
- package/docs/IPC.md +775 -64
- package/docs/MACHINES.md +456 -0
- package/docs/PANELS.md +3 -0
- package/docs/README.md +4 -0
- package/docs/RESTATE.md +3 -2
- package/docs/RUN_IN_BACKGROUND.md +10 -2
- package/docs/SERVER.md +1085 -0
- package/docs/SKILL.md +6 -1
- package/docs/TERMINAL.md +75 -0
- package/docs/TUI.md +65 -14
- package/package.json +58 -26
- package/scripts/eval.ts +4 -0
- package/dist/acp-D-tVyUjw.js.map +0 -1
- package/dist/agent-D0zZnGbt.js.map +0 -1
- package/dist/anthropic-C2aole0G.js.map +0 -1
- package/dist/approvals-DKaJZYlm.js.map +0 -1
- package/dist/atomic-write-B7zr1ddF.js.map +0 -1
- package/dist/auth-BG2rVKSt.js.map +0 -1
- package/dist/completion-core-BRo86R_L.js.map +0 -1
- package/dist/contexts/daytona.js.map +0 -1
- package/dist/contexts/docker.js.map +0 -1
- package/dist/contexts/e2b.js.map +0 -1
- package/dist/contexts-CRE9pw07.js.map +0 -1
- package/dist/eval.js.map +0 -1
- package/dist/format-BF745pLL.js.map +0 -1
- package/dist/glob-DluQFSw1.js +0 -270
- package/dist/glob-DluQFSw1.js.map +0 -1
- package/dist/headless.js.map +0 -1
- package/dist/history-search-C4bk1Wz_.js.map +0 -1
- package/dist/index-BgN1T3lk.d.ts.map +0 -1
- package/dist/index-C37_ppWv.d.ts.map +0 -1
- package/dist/index-C6NEd5y_.d.ts.map +0 -1
- package/dist/ipc-BKVIIOqH.js +0 -331
- package/dist/ipc-BKVIIOqH.js.map +0 -1
- package/dist/ipc.d.ts.map +0 -1
- package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
- package/dist/json-rpc-rSWMUh9b.js +0 -192
- package/dist/json-rpc-rSWMUh9b.js.map +0 -1
- package/dist/logger--C1VQjwJ.js.map +0 -1
- package/dist/mcp-CgNd-f3F.js.map +0 -1
- package/dist/messages-2jcOA_5R.js.map +0 -1
- package/dist/openai-compat-Dy56weK8.js.map +0 -1
- package/dist/output-cap-C9zukcCj.js +0 -100
- package/dist/output-cap-C9zukcCj.js.map +0 -1
- package/dist/panels-B6gRk4bf.js +0 -789
- package/dist/panels-B6gRk4bf.js.map +0 -1
- package/dist/plugins-gbI7SBA6.js.map +0 -1
- package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
- package/dist/run-summary-BU_NgwUT.js +0 -269
- package/dist/run-summary-BU_NgwUT.js.map +0 -1
- package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
- package/dist/session/sqlite.js.map +0 -1
- package/dist/session-BqRP1hS7.js.map +0 -1
- package/dist/shell-path-DfdXrGg9.js +0 -1549
- package/dist/shell-path-DfdXrGg9.js.map +0 -1
- package/dist/skills-BC3dodWp.js.map +0 -1
- package/dist/src-CkNQKFKr.js.map +0 -1
- package/dist/todo-edits-UOwgxGKu.js +0 -528
- package/dist/todo-edits-UOwgxGKu.js.map +0 -1
- package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
- package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
- package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
- package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
- package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
- package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
- package/dist/turn-operations-DrbJlpq9.js.map +0 -1
- package/dist/types-CWgENVLY.js.map +0 -1
- package/dist/xai-CazjStOK.js.map +0 -1
|
@@ -1,6 +1,131 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.js";
|
|
2
2
|
import { t as generatePkce } from "./pkce-t8BcPyEi.js";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
|
+
//#region src/chat/oauth-page/server.ts
|
|
5
|
+
const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
|
|
6
|
+
/** Providers known to honor `callbackServer: false` (zidane's own loopback flows). */
|
|
7
|
+
const REMOTE_CALLBACK = /* @__PURE__ */ new WeakSet();
|
|
8
|
+
/** Mark a provider that binds no listener under `callbackServer: false`; returns it. */
|
|
9
|
+
function honorsRemoteCallback(provider) {
|
|
10
|
+
REMOTE_CALLBACK.add(provider);
|
|
11
|
+
return provider;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Whether a provider can sign in for a client of a remote host without
|
|
15
|
+
* listening on the host's machine: it honors `callbackServer: false`, or it
|
|
16
|
+
* never listens (`usesCallbackServer: false`: paste, device or polling flows).
|
|
17
|
+
* Others (pi-ai's stock providers, a third party's) would bind a loopback
|
|
18
|
+
* port on the server, which the client's browser can't reach.
|
|
19
|
+
*/
|
|
20
|
+
function canSignInRemotely(provider) {
|
|
21
|
+
return REMOTE_CALLBACK.has(provider) || provider.usesCallbackServer === false;
|
|
22
|
+
}
|
|
23
|
+
/** Whether a provider should bind its loopback callback listener for these callbacks. */
|
|
24
|
+
function listensForCallback(callbacks) {
|
|
25
|
+
return callbacks.callbackServer !== false;
|
|
26
|
+
}
|
|
27
|
+
function buildRequestHandler(opts, pending) {
|
|
28
|
+
return (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(req.url || "", "http://localhost");
|
|
31
|
+
if (url.pathname !== opts.path) {
|
|
32
|
+
respondError(res, 404, opts, "Callback route not found.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const error = url.searchParams.get("error");
|
|
36
|
+
if (error) {
|
|
37
|
+
respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const code = url.searchParams.get("code");
|
|
41
|
+
const state = url.searchParams.get("state");
|
|
42
|
+
if (!code || !state) {
|
|
43
|
+
respondError(res, 400, opts, "Missing code or state parameter.");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (state !== opts.expectedState) {
|
|
47
|
+
respondError(res, 400, opts, "State mismatch.");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
51
|
+
res.end(opts.renderPage({
|
|
52
|
+
kind: "success",
|
|
53
|
+
provider: opts.providerName,
|
|
54
|
+
message: opts.successMessage
|
|
55
|
+
}));
|
|
56
|
+
pending.settle({
|
|
57
|
+
code,
|
|
58
|
+
state
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
respondError(res, 500, opts, "Internal error while processing the callback.");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function respondError(res, status, opts, message, details) {
|
|
66
|
+
res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
|
|
67
|
+
res.end(opts.renderPage({
|
|
68
|
+
kind: "error",
|
|
69
|
+
provider: opts.providerName,
|
|
70
|
+
message,
|
|
71
|
+
details
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
function buildStubHandle(redirectUri, server) {
|
|
75
|
+
return {
|
|
76
|
+
redirectUri,
|
|
77
|
+
waitForCode: async () => null,
|
|
78
|
+
cancelWait: () => {},
|
|
79
|
+
close: () => {
|
|
80
|
+
try {
|
|
81
|
+
server?.close();
|
|
82
|
+
} catch {}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Start the loopback HTTP callback server. The returned handle is the
|
|
88
|
+
* caller's contract — they `await waitForCode()`, race it against manual
|
|
89
|
+
* paste, then `close()` in a `finally`.
|
|
90
|
+
*/
|
|
91
|
+
async function startCallbackServer(opts) {
|
|
92
|
+
const onListenError = opts.onListenError ?? "reject";
|
|
93
|
+
const redirectUri = `http://localhost:${opts.port}${opts.path}`;
|
|
94
|
+
if (opts.listen === false) return buildStubHandle(redirectUri, null);
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
let settled = false;
|
|
97
|
+
const pending = { settle: () => {} };
|
|
98
|
+
const waitPromise = new Promise((resolveWait) => {
|
|
99
|
+
pending.settle = (value) => {
|
|
100
|
+
if (settled) return;
|
|
101
|
+
settled = true;
|
|
102
|
+
resolveWait(value);
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
const server = createServer(buildRequestHandler(opts, pending));
|
|
106
|
+
server.on("error", (err) => {
|
|
107
|
+
if (onListenError === "resolveWithStub") {
|
|
108
|
+
pending.settle(null);
|
|
109
|
+
resolve(buildStubHandle(redirectUri, server));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
reject(err);
|
|
113
|
+
});
|
|
114
|
+
server.listen(opts.port, CALLBACK_HOST, () => {
|
|
115
|
+
resolve({
|
|
116
|
+
redirectUri,
|
|
117
|
+
waitForCode: () => waitPromise,
|
|
118
|
+
cancelWait: () => pending.settle(null),
|
|
119
|
+
close: () => {
|
|
120
|
+
try {
|
|
121
|
+
server.close();
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
4
129
|
//#region src/chat/oauth-page/xai.ts
|
|
5
130
|
var xai_exports = /* @__PURE__ */ __exportAll({
|
|
6
131
|
XAI_OAUTH_PROVIDER_ID: () => XAI_OAUTH_PROVIDER_ID,
|
|
@@ -87,6 +212,41 @@ async function discover() {
|
|
|
87
212
|
};
|
|
88
213
|
}
|
|
89
214
|
/**
|
|
215
|
+
* A remote sign-in (`callbackServer: false`): nothing listens here; the
|
|
216
|
+
* client's loopback catches the redirect and its URL comes back through
|
|
217
|
+
* `onManualCodeInput`. Same redirect URI as the default listener's.
|
|
218
|
+
*/
|
|
219
|
+
function remoteCallback(callbacks) {
|
|
220
|
+
return {
|
|
221
|
+
redirectUri: `http://${xaiOAuthCallbackHost()}:${xaiOAuthCallbackPort()}${CALLBACK_PATH}`,
|
|
222
|
+
waitForCallback: async () => {
|
|
223
|
+
if (!callbacks.onManualCodeInput) return {
|
|
224
|
+
error: "no-callback",
|
|
225
|
+
errorDescription: "No way to receive the xAI OAuth callback."
|
|
226
|
+
};
|
|
227
|
+
const input = (await callbacks.onManualCodeInput()).trim();
|
|
228
|
+
try {
|
|
229
|
+
const url = new URL(input);
|
|
230
|
+
const error = url.searchParams.get("error");
|
|
231
|
+
return error ? {
|
|
232
|
+
error,
|
|
233
|
+
...url.searchParams.get("error_description") ? { errorDescription: url.searchParams.get("error_description") } : {}
|
|
234
|
+
} : {
|
|
235
|
+
...url.searchParams.get("code") ? { code: url.searchParams.get("code") } : {},
|
|
236
|
+
...url.searchParams.get("state") ? { state: url.searchParams.get("state") } : {}
|
|
237
|
+
};
|
|
238
|
+
} catch {
|
|
239
|
+
const params = new URLSearchParams(input.includes("=") ? input : `code=${input}`);
|
|
240
|
+
return {
|
|
241
|
+
...params.get("code") ? { code: params.get("code") } : {},
|
|
242
|
+
...params.get("state") ? { state: params.get("state") } : {}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
close: () => void 0
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
90
250
|
* Start the loopback callback server on `127.0.0.1:56121`. Falls back to an
|
|
91
251
|
* OS-assigned port if 56121 is taken — xAI accepts any loopback port as long
|
|
92
252
|
* as the `redirect_uri` we send on the authorize URL matches the listener.
|
|
@@ -215,7 +375,7 @@ async function loginXai(renderPage, callbacks) {
|
|
|
215
375
|
const { verifier, challenge } = await generatePkce();
|
|
216
376
|
const state = randomToken();
|
|
217
377
|
const nonce = randomToken();
|
|
218
|
-
const server = await startXaiCallbackServer(renderPage);
|
|
378
|
+
const server = listensForCallback(callbacks) ? await startXaiCallbackServer(renderPage) : remoteCallback(callbacks);
|
|
219
379
|
try {
|
|
220
380
|
const authUrl = new URL(discovery.authorization_endpoint);
|
|
221
381
|
authUrl.searchParams.set("response_type", "code");
|
|
@@ -281,16 +441,16 @@ async function refreshXaiToken(credentials) {
|
|
|
281
441
|
* so the TUI doesn't need to prompt for a manual code paste.
|
|
282
442
|
*/
|
|
283
443
|
function createXaiOAuthProvider(renderPage) {
|
|
284
|
-
return {
|
|
444
|
+
return honorsRemoteCallback({
|
|
285
445
|
id: XAI_OAUTH_PROVIDER_ID,
|
|
286
446
|
name: "xAI Grok (SuperGrok / X Premium+)",
|
|
287
447
|
usesCallbackServer: true,
|
|
288
448
|
login: (callbacks) => loginXai(renderPage, callbacks),
|
|
289
449
|
refreshToken: refreshXaiToken,
|
|
290
450
|
getApiKey: (credentials) => credentials.access
|
|
291
|
-
};
|
|
451
|
+
});
|
|
292
452
|
}
|
|
293
453
|
//#endregion
|
|
294
|
-
export { xai_exports as i, createXaiOAuthProvider as n, xaiBaseUrl as r, XAI_OAUTH_PROVIDER_ID as t };
|
|
454
|
+
export { canSignInRemotely as a, startCallbackServer as c, xai_exports as i, createXaiOAuthProvider as n, honorsRemoteCallback as o, xaiBaseUrl as r, listensForCallback as s, XAI_OAUTH_PROVIDER_ID as t };
|
|
295
455
|
|
|
296
|
-
//# sourceMappingURL=xai-
|
|
456
|
+
//# sourceMappingURL=xai-CvJTeLrI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xai-CvJTeLrI.js","names":[],"sources":["../src/chat/oauth-page/server.ts","../src/chat/oauth-page/xai.ts"],"sourcesContent":["/**\n * Local HTTP callback server primitive shared by the Anthropic + Codex\n * login flows. Owns three concerns:\n *\n * 1. Bind a loopback listener (configurable port + host via\n * `PI_OAUTH_CALLBACK_HOST`, matching pi-ai's semantics).\n * 2. Parse the redirect query string into `{ code, state }` or surface\n * a structured error.\n * 3. Render the result page through the caller-supplied renderer.\n *\n * Each provider clones the lifecycle wrapper (`waitForCode` / `cancelWait`\n * / `close`) so the higher-level login code reads the same on either\n * side. The only thing the server itself decides is the HTML.\n */\n\nimport type { OAuthLoginCallbacks } from '@earendil-works/pi-ai/oauth'\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { createServer } from 'node:http'\n\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || '127.0.0.1'\n\nexport interface CallbackServerOptions {\n /** Loopback port. Must match the `redirect_uri` registered with the provider. */\n port: number\n /** Path component of the redirect URI (e.g. `/callback`, `/auth/callback`). */\n path: string\n /**\n * Expected `state` value. The server rejects any callback whose `state`\n * doesn't match, exactly. For Anthropic this is the PKCE verifier; for\n * Codex it's a random 16-byte hex string.\n */\n expectedState: string\n /** Display name passed through to {@link OAuthCallbackPageRenderer}. */\n providerName: string\n /** HTML renderer for both success + error pages. */\n renderPage: OAuthCallbackPageRenderer\n /** Success-page body shown after a clean redirect. */\n successMessage: string\n /**\n * How to react when `server.listen` itself fails (typically `EADDRINUSE`\n * when another instance is mid-flow). `\"reject\"` propagates the error to\n * the awaiting `start` caller; `\"resolveWithStub\"` returns a degraded\n * handle whose `waitForCode` immediately resolves to `null`, so the\n * caller can fall back to manual paste. Anthropic uses `\"reject\"`,\n * Codex uses `\"resolveWithStub\"` to match pi-ai's per-provider behavior.\n */\n onListenError?: 'reject' | 'resolveWithStub'\n /**\n * `false`: bind nothing, return the stub handle (`waitForCode` resolves\n * `null` at once) for {@link redirectUri}. A remote sign-in: the browser\n * redirects to the CLIENT's loopback, and the code comes back through the\n * provider's manual-input path (see {@link ZidaneOAuthLoginCallbacks}).\n */\n listen?: boolean\n}\n\n/**\n * pi-ai's login callbacks plus zidane's `callbackServer: false`: the\n * redirect lands on another machine (a client of a remote host), which\n * forwards the full callback URL through `onManualCodeInput`. zidane's own\n * providers then skip their loopback listener; others ignore the flag.\n */\nexport type ZidaneOAuthLoginCallbacks = OAuthLoginCallbacks & { callbackServer?: false }\n\n/** Providers known to honor `callbackServer: false` (zidane's own loopback flows). */\nconst REMOTE_CALLBACK = new WeakSet<object>()\n\n/** Mark a provider that binds no listener under `callbackServer: false`; returns it. */\nexport function honorsRemoteCallback<T extends object>(provider: T): T {\n REMOTE_CALLBACK.add(provider)\n return provider\n}\n\n/**\n * Whether a provider can sign in for a client of a remote host without\n * listening on the host's machine: it honors `callbackServer: false`, or it\n * never listens (`usesCallbackServer: false`: paste, device or polling flows).\n * Others (pi-ai's stock providers, a third party's) would bind a loopback\n * port on the server, which the client's browser can't reach.\n */\nexport function canSignInRemotely(provider: { usesCallbackServer?: boolean }): boolean {\n return REMOTE_CALLBACK.has(provider) || provider.usesCallbackServer === false\n}\n\n/** Whether a provider should bind its loopback callback listener for these callbacks. */\nexport function listensForCallback(callbacks: OAuthLoginCallbacks): boolean {\n return (callbacks as ZidaneOAuthLoginCallbacks).callbackServer !== false\n}\n\nexport interface CallbackServerHandle {\n /** Fully-qualified redirect URI to advertise to the provider. */\n redirectUri: string\n /**\n * Resolves when the redirect lands (with `{ code, state? }`) or when\n * {@link cancelWait} is invoked (with `null`). Never throws.\n */\n waitForCode: () => Promise<{ code: string, state?: string } | null>\n /** Unblock `waitForCode` early (e.g. manual paste won the race). */\n cancelWait: () => void\n /** Close the listener. Idempotent. */\n close: () => void\n}\n\ninterface PendingResolution {\n settle: (value: { code: string, state?: string } | null) => void\n}\n\nfunction buildRequestHandler(opts: CallbackServerOptions, pending: PendingResolution) {\n return (req: IncomingMessage, res: ServerResponse) => {\n try {\n const url = new URL(req.url || '', 'http://localhost')\n\n if (url.pathname !== opts.path) {\n respondError(res, 404, opts, 'Callback route not found.')\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`)\n return\n }\n\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code || !state) {\n respondError(res, 400, opts, 'Missing code or state parameter.')\n return\n }\n\n if (state !== opts.expectedState) {\n respondError(res, 400, opts, 'State mismatch.')\n return\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(opts.renderPage({\n kind: 'success',\n provider: opts.providerName,\n message: opts.successMessage,\n }))\n pending.settle({ code, state })\n }\n catch {\n // We intentionally swallow the actual error here: surfacing a stack\n // to the user's browser would leak internals, and the login flow\n // is about to fall back to manual paste anyway.\n respondError(res, 500, opts, 'Internal error while processing the callback.')\n }\n }\n}\n\nfunction respondError(\n res: ServerResponse,\n status: number,\n opts: CallbackServerOptions,\n message: string,\n details?: string,\n) {\n res.writeHead(status, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(opts.renderPage({\n kind: 'error',\n provider: opts.providerName,\n message,\n details,\n }))\n}\n\nfunction buildStubHandle(redirectUri: string, server: Server | null): CallbackServerHandle {\n return {\n redirectUri,\n waitForCode: async () => null,\n cancelWait: () => {},\n close: () => {\n try { server?.close() }\n catch { /* already closed */ }\n },\n }\n}\n\n/**\n * Start the loopback HTTP callback server. The returned handle is the\n * caller's contract — they `await waitForCode()`, race it against manual\n * paste, then `close()` in a `finally`.\n */\nexport async function startCallbackServer(opts: CallbackServerOptions): Promise<CallbackServerHandle> {\n const onListenError = opts.onListenError ?? 'reject'\n const redirectUri = `http://localhost:${opts.port}${opts.path}`\n if (opts.listen === false)\n return buildStubHandle(redirectUri, null)\n\n return new Promise<CallbackServerHandle>((resolve, reject) => {\n let settled = false\n const pending: PendingResolution = {\n settle: () => {},\n }\n const waitPromise = new Promise<{ code: string, state?: string } | null>((resolveWait) => {\n pending.settle = (value) => {\n if (settled)\n return\n settled = true\n resolveWait(value)\n }\n })\n\n const server = createServer(buildRequestHandler(opts, pending))\n\n server.on('error', (err) => {\n if (onListenError === 'resolveWithStub') {\n pending.settle(null)\n resolve(buildStubHandle(redirectUri, server))\n return\n }\n reject(err)\n })\n\n server.listen(opts.port, CALLBACK_HOST, () => {\n resolve({\n redirectUri,\n waitForCode: () => waitPromise,\n cancelWait: () => pending.settle(null),\n close: () => {\n try { server.close() }\n catch { /* already closed */ }\n },\n })\n })\n })\n}\n","/**\n * xAI Grok OAuth flow (SuperGrok / X Premium+).\n *\n * xAI is **not** built into pi-ai, so — like Cursor — we implement the flow\n * here and register it with pi-ai's OAuth registry (`registerXaiOAuthProvider`)\n * so `getOAuthApiKey('xai-oauth', …)` resolves to it during lazy token refresh.\n *\n * Auth shape: OAuth 2.0 + PKCE with a loopback callback server, against xAI's\n * OIDC issuer (`https://auth.x.ai`). Differs from the Anthropic / Codex flows\n * (which use the shared `startCallbackServer`) on three points, so it gets its\n * own login impl rather than reusing that primitive:\n *\n * 1. The `redirect_uri` must be `http://127.0.0.1:56121/callback` verbatim\n * (xAI matches the registered loopback IP, not `localhost`).\n * 2. Endpoints come from OIDC discovery, not hard-coded constants.\n * 3. Token exchange + refresh are `application/x-www-form-urlencoded`\n * (the Anthropic flow posts JSON).\n *\n * The callback page still routes through {@link OAuthCallbackPageRenderer} so\n * the post-redirect HTML matches the rest of zidane's themed flows.\n *\n * Flow + constants adapted from the public reference implementations\n * (`stnly/pi-grok`, `BlockedPath/pi-xai-oauth`) and the Hermes Agent docs.\n * xAI's OAuth surface is unofficial/undocumented — re-verify the client id,\n * scopes, and callback port if login starts failing.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n} from '@earendil-works/pi-ai/oauth'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { OAuthProviderInterface } from '../../providers/oauth-registry'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { createServer } from 'node:http'\nimport { generatePkce } from './pkce'\nimport { honorsRemoteCallback, listensForCallback } from './server'\n\n/** pi-ai / zidane OAuth provider id. Also the credentials-file key. */\nexport const XAI_OAUTH_PROVIDER_ID = 'xai-oauth'\n\nconst DEFAULT_BASE_URL = 'https://api.x.ai/v1'\nconst ISSUER = 'https://auth.x.ai'\nconst DISCOVERY_URL = `${ISSUER}/.well-known/openid-configuration`\n/** Public client id used by the Grok CLI OAuth surface. */\nconst DEFAULT_CLIENT_ID = 'b1a00492-073a-47ea-816f-4c329264a828'\nconst DEFAULT_SCOPE = 'openid profile email offline_access grok-cli:access api:access'\n/** xAI matches the registered loopback IP exactly — `localhost` is rejected. */\nconst DEFAULT_CALLBACK_HOST = '127.0.0.1'\nconst DEFAULT_CALLBACK_PORT = 56121\nconst CALLBACK_PATH = '/callback'\nconst PROVIDER_NAME = 'xAI Grok'\n/** Refresh slightly early so requests don't race expiry. */\nconst REFRESH_SKEW_MS = 120_000\nconst CALLBACK_TIMEOUT_MS = 180_000\n\nfunction xaiOAuthClientId(): string {\n return process.env.PI_XAI_OAUTH_CLIENT_ID || DEFAULT_CLIENT_ID\n}\n\nfunction xaiOAuthScope(): string {\n return process.env.PI_XAI_OAUTH_SCOPE || DEFAULT_SCOPE\n}\n\nfunction xaiOAuthCallbackHost(): string {\n return process.env.PI_XAI_OAUTH_CALLBACK_HOST || DEFAULT_CALLBACK_HOST\n}\n\nfunction xaiOAuthCallbackPort(): number {\n const parsed = Number.parseInt(process.env.PI_XAI_OAUTH_CALLBACK_PORT || String(DEFAULT_CALLBACK_PORT), 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CALLBACK_PORT\n}\n\n/** Resolve the xAI API base URL (env override → default). */\nexport function xaiBaseUrl(): string {\n return (process.env.PI_XAI_BASE_URL || process.env.XAI_BASE_URL || DEFAULT_BASE_URL)\n .replace(/\\/+$/, '')\n}\n\ninterface XaiDiscovery {\n authorization_endpoint: string\n token_endpoint: string\n}\n\n/**\n * xAI OAuth credentials. Extends the base `{ access, refresh, expires }` with\n * the resolved `token_endpoint` so refresh doesn't need a second discovery\n * round-trip, plus the cached discovery doc for validation.\n */\nexport interface XaiOAuthCredentials extends OAuthCredentials {\n tokenEndpoint?: string\n discovery?: XaiDiscovery\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n let binary = ''\n for (const b of bytes)\n binary += String.fromCharCode(b)\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction randomToken(byteLength = 16): string {\n return base64Url(crypto.getRandomValues(new Uint8Array(byteLength)))\n}\n\n/**\n * Refuse any OIDC endpoint that isn't HTTPS on an xAI origin.\n *\n * The discovery doc is cached long-term in `credentials.json`. A single MITM\n * during initial login could otherwise pin a malicious `token_endpoint` that\n * receives every subsequent refresh token. Validating scheme + host keeps the\n * endpoint on `x.ai` / `*.x.ai`.\n */\nfunction validateEndpoint(value: string, field: string): string {\n let url: URL\n try {\n url = new URL(value)\n }\n catch {\n throw new Error(`xAI OAuth discovery returned an invalid ${field}: ${value}`)\n }\n if (url.protocol !== 'https:')\n throw new Error(`xAI OAuth ${field} must use HTTPS: ${value}`)\n const host = url.hostname.toLowerCase()\n if (host !== 'x.ai' && !host.endsWith('.x.ai'))\n throw new Error(`Refusing non-xAI OAuth ${field}: ${value}`)\n return url.toString()\n}\n\nasync function discover(): Promise<XaiDiscovery> {\n let response: Response\n try {\n response = await fetch(DISCOVERY_URL, {\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(15_000),\n })\n }\n catch (cause) {\n throw new Error(`xAI OIDC discovery failed: ${cause instanceof Error ? cause.message : String(cause)}`)\n }\n if (!response.ok)\n throw new Error(`xAI OIDC discovery returned ${response.status}`)\n\n const payload = await response.json() as Record<string, unknown>\n return {\n authorization_endpoint: validateEndpoint(String(payload.authorization_endpoint ?? ''), 'authorization_endpoint'),\n token_endpoint: validateEndpoint(String(payload.token_endpoint ?? ''), 'token_endpoint'),\n }\n}\n\ninterface CallbackResult {\n code?: string\n state?: string\n error?: string\n errorDescription?: string\n}\n\ninterface XaiCallbackServer {\n redirectUri: string\n waitForCallback: (timeoutMs: number) => Promise<CallbackResult>\n close: () => void\n}\n\n/**\n * A remote sign-in (`callbackServer: false`): nothing listens here; the\n * client's loopback catches the redirect and its URL comes back through\n * `onManualCodeInput`. Same redirect URI as the default listener's.\n */\nfunction remoteCallback(callbacks: OAuthLoginCallbacks): XaiCallbackServer {\n return {\n redirectUri: `http://${xaiOAuthCallbackHost()}:${xaiOAuthCallbackPort()}${CALLBACK_PATH}`,\n waitForCallback: async () => {\n if (!callbacks.onManualCodeInput)\n return { error: 'no-callback', errorDescription: 'No way to receive the xAI OAuth callback.' }\n const input = (await callbacks.onManualCodeInput()).trim()\n try {\n const url = new URL(input)\n const error = url.searchParams.get('error')\n return error\n ? { error, ...(url.searchParams.get('error_description') ? { errorDescription: url.searchParams.get('error_description')! } : {}) }\n : { ...(url.searchParams.get('code') ? { code: url.searchParams.get('code')! } : {}), ...(url.searchParams.get('state') ? { state: url.searchParams.get('state')! } : {}) }\n }\n catch {\n const params = new URLSearchParams(input.includes('=') ? input : `code=${input}`)\n return { ...(params.get('code') ? { code: params.get('code')! } : {}), ...(params.get('state') ? { state: params.get('state')! } : {}) }\n }\n },\n close: () => undefined,\n }\n}\n\n/**\n * Start the loopback callback server on `127.0.0.1:56121`. Falls back to an\n * OS-assigned port if 56121 is taken — xAI accepts any loopback port as long\n * as the `redirect_uri` we send on the authorize URL matches the listener.\n */\nasync function startXaiCallbackServer(renderPage: OAuthCallbackPageRenderer): Promise<XaiCallbackServer> {\n const callbackHost = xaiOAuthCallbackHost()\n const callbackPort = xaiOAuthCallbackPort()\n let settle: ((value: CallbackResult) => void) | undefined\n let settled = false\n const callbackPromise = new Promise<CallbackResult>((resolve) => {\n settle = (value) => {\n if (settled)\n return\n settled = true\n resolve(value)\n }\n })\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n try {\n // accounts.x.ai may issue a CORS preflight against the loopback listener.\n const origin = req.headers.origin\n if (origin === 'https://accounts.x.ai' || origin === 'https://auth.x.ai') {\n res.setHeader('Access-Control-Allow-Origin', origin)\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.setHeader('Access-Control-Allow-Private-Network', 'true')\n res.setHeader('Vary', 'Origin')\n }\n if (req.method === 'OPTIONS') {\n res.statusCode = 204\n res.end()\n return\n }\n\n const url = new URL(req.url ?? '/', `http://${callbackHost}`)\n if (url.pathname !== CALLBACK_PATH) {\n res.statusCode = 404\n res.end('Not found')\n return\n }\n\n const result: CallbackResult = {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n error: url.searchParams.get('error') ?? undefined,\n errorDescription: url.searchParams.get('error_description') ?? undefined,\n }\n\n res.statusCode = result.error ? 400 : 200\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n res.end(renderPage(result.error\n ? {\n kind: 'error',\n provider: PROVIDER_NAME,\n message: `${PROVIDER_NAME} authentication did not complete.`,\n details: result.errorDescription ?? result.error,\n }\n : {\n kind: 'success',\n provider: PROVIDER_NAME,\n message: 'xAI authentication completed. You can close this window.',\n }))\n settle?.(result)\n }\n catch {\n res.statusCode = 500\n res.end('Internal error')\n }\n })\n\n const listen = (port: number) => new Promise<number>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, callbackHost, () => {\n server.removeListener('error', reject)\n const addr = server.address()\n resolve(typeof addr === 'object' && addr ? addr.port : port)\n })\n })\n\n let actualPort: number\n try {\n actualPort = await listen(callbackPort)\n }\n catch {\n actualPort = await listen(0)\n }\n\n return {\n redirectUri: `http://${callbackHost}:${actualPort}${CALLBACK_PATH}`,\n waitForCallback: timeoutMs => Promise.race([\n callbackPromise,\n new Promise<CallbackResult>(resolve =>\n setTimeout(\n resolve,\n timeoutMs,\n { error: 'timeout', errorDescription: 'Timed out waiting for the xAI OAuth callback.' },\n )),\n ]),\n close: () => {\n try { server.close() }\n catch { /* already closed */ }\n },\n }\n}\n\nfunction expiryFromPayload(payload: Record<string, unknown>): number {\n const expiresIn = typeof payload.expires_in === 'number'\n ? payload.expires_in\n : Number(payload.expires_in ?? 3600)\n return Date.now() + expiresIn * 1000 - REFRESH_SKEW_MS\n}\n\nasync function exchangeCode(\n tokenEndpoint: string,\n code: string,\n redirectUri: string,\n verifier: string,\n discovery: XaiDiscovery,\n): Promise<XaiOAuthCredentials> {\n const clientId = xaiOAuthClientId()\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: clientId,\n code,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n }),\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`xAI token exchange failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n const payload = await response.json() as Record<string, unknown>\n const access = String(payload.access_token ?? '')\n const refresh = String(payload.refresh_token ?? '')\n if (!access)\n throw new Error('xAI token exchange did not return an access_token.')\n if (!refresh)\n throw new Error('xAI token exchange did not return a refresh_token.')\n\n return { access, refresh, expires: expiryFromPayload(payload), tokenEndpoint, discovery }\n}\n\nexport async function loginXai(\n renderPage: OAuthCallbackPageRenderer,\n callbacks: OAuthLoginCallbacks,\n): Promise<XaiOAuthCredentials> {\n const discovery = await discover()\n const { verifier, challenge } = await generatePkce()\n const state = randomToken()\n const nonce = randomToken()\n const server = listensForCallback(callbacks) ? await startXaiCallbackServer(renderPage) : remoteCallback(callbacks)\n\n try {\n const authUrl = new URL(discovery.authorization_endpoint)\n authUrl.searchParams.set('response_type', 'code')\n authUrl.searchParams.set('client_id', xaiOAuthClientId())\n authUrl.searchParams.set('redirect_uri', server.redirectUri)\n authUrl.searchParams.set('scope', xaiOAuthScope())\n authUrl.searchParams.set('code_challenge', challenge)\n authUrl.searchParams.set('code_challenge_method', 'S256')\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('nonce', nonce)\n // Opt into xAI's generic OAuth plan tier (SuperGrok / X Premium+).\n authUrl.searchParams.set('plan', 'generic')\n authUrl.searchParams.set('referrer', 'zidane')\n\n callbacks.onAuth({\n url: authUrl.toString(),\n instructions: `Sign in to xAI and approve access. If the browser is on another machine, the callback listener is ${server.redirectUri}.`,\n })\n\n const result = await server.waitForCallback(CALLBACK_TIMEOUT_MS)\n if (result.error)\n throw new Error(result.errorDescription ?? result.error)\n if (result.state !== state)\n throw new Error('xAI OAuth state mismatch (possible CSRF). Retry the login.')\n if (!result.code)\n throw new Error('xAI OAuth callback did not include an authorization code.')\n\n callbacks.onProgress?.('Exchanging authorization code for tokens...')\n return await exchangeCode(discovery.token_endpoint, result.code, server.redirectUri, verifier, discovery)\n }\n finally {\n server.close()\n }\n}\n\nexport async function refreshXaiToken(credentials: OAuthCredentials): Promise<XaiOAuthCredentials> {\n const xai = credentials as XaiOAuthCredentials\n const clientId = xaiOAuthClientId()\n if (!credentials.refresh)\n throw new Error('xAI credentials are missing a refresh token. Log in again.')\n\n const tokenEndpoint = xai.tokenEndpoint\n ?? xai.discovery?.token_endpoint\n ?? (await discover()).token_endpoint\n validateEndpoint(tokenEndpoint, 'token_endpoint')\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: clientId,\n refresh_token: credentials.refresh,\n }),\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`xAI token refresh failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n const payload = await response.json() as Record<string, unknown>\n const access = String(payload.access_token ?? '')\n if (!access)\n throw new Error('xAI token refresh did not return an access_token.')\n\n return {\n ...xai,\n access,\n // xAI may omit a rotated refresh token — keep the existing one.\n refresh: String(payload.refresh_token ?? credentials.refresh),\n expires: expiryFromPayload(payload),\n tokenEndpoint,\n }\n}\n\n/**\n * Build an xAI `OAuthProviderInterface`. Shape matches Anthropic / Codex /\n * Cursor so it drops into `BUILTIN_PROVIDERS` and `src/auth.ts` unchanged.\n *\n * `usesCallbackServer: true` — the loopback flow has a real callback server,\n * so the TUI doesn't need to prompt for a manual code paste.\n */\nexport function createXaiOAuthProvider(renderPage: OAuthCallbackPageRenderer): OAuthProviderInterface {\n // Honors `callbackServer: false`: a remote sign-in binds nothing on this machine.\n return honorsRemoteCallback({\n id: XAI_OAUTH_PROVIDER_ID,\n name: 'xAI Grok (SuperGrok / X Premium+)',\n usesCallbackServer: true,\n login: callbacks => loginXai(renderPage, callbacks),\n refreshToken: refreshXaiToken,\n getApiKey: credentials => credentials.access,\n })\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
//#region src/chat/xdg.ts
|
|
5
|
+
/**
|
|
6
|
+
* XDG Base Directory resolution for zidane.
|
|
7
|
+
*
|
|
8
|
+
* The user's storage spreads across four logical slots:
|
|
9
|
+
*
|
|
10
|
+
* - **config** — credentials, mcp-credentials, keybindings, config.json,
|
|
11
|
+
* mcps.json, skills (user-editable surface).
|
|
12
|
+
* - **data** — sessions.db (long-lived chat history).
|
|
13
|
+
* - **state** — state.json (per-machine UI bookkeeping: last provider,
|
|
14
|
+
* last model, last agent, settings).
|
|
15
|
+
* - **cache** — persisted tool results, background-task logs,
|
|
16
|
+
* auto-update registry cache (regenerable, safe to wipe).
|
|
17
|
+
*
|
|
18
|
+
* Historically all four collapsed to `~/.zidane/`. This module adds
|
|
19
|
+
* XDG-Base-Directory-aware defaults so a Linux user with no existing
|
|
20
|
+
* `~/.zidane/` lands in the conventional `~/.config/zidane/`,
|
|
21
|
+
* `~/.local/share/zidane/`, `~/.local/state/zidane/`, `~/.cache/zidane/`
|
|
22
|
+
* trio. Existing setups (anyone with a `~/.zidane/` directory or
|
|
23
|
+
* `ZIDANE_STORAGE_DIR` set) keep the single-dir layout unchanged.
|
|
24
|
+
*
|
|
25
|
+
* Resolution flow — applied in this order:
|
|
26
|
+
*
|
|
27
|
+
* 1. Caller passed an explicit `storageDir` (`ChatOptions.storageDir`)
|
|
28
|
+
* OR `ZIDANE_STORAGE_DIR` is set → **legacy-explicit** single-dir
|
|
29
|
+
* layout under `<storageDir>/<prefix>`. The four slots collapse to
|
|
30
|
+
* the same directory.
|
|
31
|
+
* 2. `~/<prefix>/` already holds storage on disk → **legacy-existing**
|
|
32
|
+
* single-dir layout under that directory. Upgrades never silently
|
|
33
|
+
* shift files around. A dir holding only user-authored, home-anchored
|
|
34
|
+
* content (`extensions/`, `skills/`, `mcps.json`, `SYSTEM.md`,
|
|
35
|
+
* `AGENTS.md`, plus macOS's `.DS_Store`) does not count: discovery reads
|
|
36
|
+
* those from `~/<prefix>/` in every mode, and scaffolding one must not
|
|
37
|
+
* flip an XDG install.
|
|
38
|
+
* 3. Any `XDG_*_HOME` env var is set OR `process.platform === 'linux'`
|
|
39
|
+
* → **XDG split**. Each slot lands in its own dir. Per-slot
|
|
40
|
+
* `ZIDANE_{CONFIG,DATA,CACHE,STATE}_DIR` overrides beat the XDG
|
|
41
|
+
* vars for surgical tweaks.
|
|
42
|
+
* 4. Otherwise (macOS/Windows, no XDG signal, no existing dir) →
|
|
43
|
+
* **default** single-dir layout at `~/<prefix>/`. Day-one behavior
|
|
44
|
+
* on these platforms is unchanged.
|
|
45
|
+
*
|
|
46
|
+
* The `userDir` legacy alias always equals `configDir` so existing code
|
|
47
|
+
* that still references `paths.userDir` keeps reading credentials, mcps,
|
|
48
|
+
* etc. from the right place.
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the four storage slots according to the precedence above.
|
|
52
|
+
*
|
|
53
|
+
* Pure aside from one filesystem probe (`readdirSync(home/<prefix>)`,
|
|
54
|
+
* see {@link holdsLegacyStorage}). No directories are created here; the
|
|
55
|
+
* relevant write paths (state store, credentials writer, persist
|
|
56
|
+
* dir) handle their own lazy `mkdirSync` already.
|
|
57
|
+
*/
|
|
58
|
+
function resolveStorageDirs(opts = {}) {
|
|
59
|
+
const env = opts.env ?? process.env;
|
|
60
|
+
const home = opts.home ?? homedir();
|
|
61
|
+
const platform = opts.platform ?? process.platform;
|
|
62
|
+
const rawPrefix = opts.prefix ?? ".zidane";
|
|
63
|
+
const prefixBare = rawPrefix.replace(/^\./, "");
|
|
64
|
+
const explicitStorageDir = opts.storageDir ?? env.ZIDANE_STORAGE_DIR;
|
|
65
|
+
if (explicitStorageDir) return single(resolve(explicitStorageDir, rawPrefix), "legacy-explicit");
|
|
66
|
+
const legacyHomeDir = resolve(home, rawPrefix);
|
|
67
|
+
if (holdsLegacyStorage(legacyHomeDir)) return single(legacyHomeDir, "legacy-existing");
|
|
68
|
+
if (platform === "linux" || !!env.XDG_CONFIG_HOME || !!env.XDG_DATA_HOME || !!env.XDG_CACHE_HOME || !!env.XDG_STATE_HOME || !!env.ZIDANE_CONFIG_DIR || !!env.ZIDANE_DATA_DIR || !!env.ZIDANE_STATE_DIR || !!env.ZIDANE_CACHE_DIR) return {
|
|
69
|
+
configDir: env.ZIDANE_CONFIG_DIR ?? resolve(env.XDG_CONFIG_HOME || resolve(home, ".config"), prefixBare),
|
|
70
|
+
dataDir: env.ZIDANE_DATA_DIR ?? resolve(env.XDG_DATA_HOME || resolve(home, ".local", "share"), prefixBare),
|
|
71
|
+
stateDir: env.ZIDANE_STATE_DIR ?? resolve(env.XDG_STATE_HOME || resolve(home, ".local", "state"), prefixBare),
|
|
72
|
+
cacheDir: env.ZIDANE_CACHE_DIR ?? resolve(env.XDG_CACHE_HOME || resolve(home, ".cache"), prefixBare),
|
|
73
|
+
mode: "xdg"
|
|
74
|
+
};
|
|
75
|
+
return single(legacyHomeDir, "default");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Entries discovery reads from `~/<prefix>/` regardless of the storage
|
|
79
|
+
* mode, plus Finder's `.DS_Store`. Creating one (e.g. the create-extension
|
|
80
|
+
* shortcut writing `~/.zidane/extensions/<name>`) must not be mistaken for
|
|
81
|
+
* a legacy install.
|
|
82
|
+
*/
|
|
83
|
+
const HOME_ANCHORED_ENTRIES = /* @__PURE__ */ new Set([
|
|
84
|
+
"extensions",
|
|
85
|
+
"skills",
|
|
86
|
+
"mcps.json",
|
|
87
|
+
"SYSTEM.md",
|
|
88
|
+
"AGENTS.md",
|
|
89
|
+
".DS_Store"
|
|
90
|
+
]);
|
|
91
|
+
/**
|
|
92
|
+
* True when `dir` exists and holds anything beyond home-anchored authored
|
|
93
|
+
* content. An unreadable dir counts as legacy: staying put beats silently
|
|
94
|
+
* moving a user's storage.
|
|
95
|
+
*/
|
|
96
|
+
function holdsLegacyStorage(dir) {
|
|
97
|
+
let entries;
|
|
98
|
+
try {
|
|
99
|
+
entries = readdirSync(dir);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
return err.code !== "ENOENT";
|
|
102
|
+
}
|
|
103
|
+
return entries.some((name) => !HOME_ANCHORED_ENTRIES.has(name));
|
|
104
|
+
}
|
|
105
|
+
function single(base, mode) {
|
|
106
|
+
return {
|
|
107
|
+
configDir: base,
|
|
108
|
+
dataDir: base,
|
|
109
|
+
stateDir: base,
|
|
110
|
+
cacheDir: base,
|
|
111
|
+
mode
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { resolveStorageDirs as t };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=xdg-zweed0-t.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xdg-zweed0-t.js","names":[],"sources":["../src/chat/xdg.ts"],"sourcesContent":["/**\n * XDG Base Directory resolution for zidane.\n *\n * The user's storage spreads across four logical slots:\n *\n * - **config** — credentials, mcp-credentials, keybindings, config.json,\n * mcps.json, skills (user-editable surface).\n * - **data** — sessions.db (long-lived chat history).\n * - **state** — state.json (per-machine UI bookkeeping: last provider,\n * last model, last agent, settings).\n * - **cache** — persisted tool results, background-task logs,\n * auto-update registry cache (regenerable, safe to wipe).\n *\n * Historically all four collapsed to `~/.zidane/`. This module adds\n * XDG-Base-Directory-aware defaults so a Linux user with no existing\n * `~/.zidane/` lands in the conventional `~/.config/zidane/`,\n * `~/.local/share/zidane/`, `~/.local/state/zidane/`, `~/.cache/zidane/`\n * trio. Existing setups (anyone with a `~/.zidane/` directory or\n * `ZIDANE_STORAGE_DIR` set) keep the single-dir layout unchanged.\n *\n * Resolution flow — applied in this order:\n *\n * 1. Caller passed an explicit `storageDir` (`ChatOptions.storageDir`)\n * OR `ZIDANE_STORAGE_DIR` is set → **legacy-explicit** single-dir\n * layout under `<storageDir>/<prefix>`. The four slots collapse to\n * the same directory.\n * 2. `~/<prefix>/` already holds storage on disk → **legacy-existing**\n * single-dir layout under that directory. Upgrades never silently\n * shift files around. A dir holding only user-authored, home-anchored\n * content (`extensions/`, `skills/`, `mcps.json`, `SYSTEM.md`,\n * `AGENTS.md`, plus macOS's `.DS_Store`) does not count: discovery reads\n * those from `~/<prefix>/` in every mode, and scaffolding one must not\n * flip an XDG install.\n * 3. Any `XDG_*_HOME` env var is set OR `process.platform === 'linux'`\n * → **XDG split**. Each slot lands in its own dir. Per-slot\n * `ZIDANE_{CONFIG,DATA,CACHE,STATE}_DIR` overrides beat the XDG\n * vars for surgical tweaks.\n * 4. Otherwise (macOS/Windows, no XDG signal, no existing dir) →\n * **default** single-dir layout at `~/<prefix>/`. Day-one behavior\n * on these platforms is unchanged.\n *\n * The `userDir` legacy alias always equals `configDir` so existing code\n * that still references `paths.userDir` keeps reading credentials, mcps,\n * etc. from the right place.\n */\n\nimport { readdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { resolve } from 'node:path'\n\n/** The four slot kinds we resolve. Names match the XDG spec. */\nexport type StorageSlot = 'config' | 'data' | 'state' | 'cache'\n\n/** Which resolution branch produced the layout — surfaced for debugging + tests. */\nexport type StorageMode = 'legacy-explicit' | 'legacy-existing' | 'xdg' | 'default'\n\n/**\n * Per-slot directories. In legacy / default modes the four fields all\n * point at the same path; in XDG mode they fan out into their\n * spec-conventional locations.\n *\n * `mode` carries the resolution branch so a host that wants to print\n * a startup banner (\"zidane is using XDG paths\") can detect the\n * layout without re-running the logic.\n */\nexport interface StorageDirs {\n configDir: string\n dataDir: string\n stateDir: string\n cacheDir: string\n mode: StorageMode\n}\n\nexport interface ResolveStorageDirsOptions {\n /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */\n prefix?: string\n /**\n * Explicit storage root (`ChatOptions.storageDir`). When set, forces\n * legacy-explicit mode regardless of the platform / XDG signals.\n */\n storageDir?: string\n /** Override `os.homedir()` for tests. */\n home?: string\n /** Override `process.platform` for tests. */\n platform?: NodeJS.Platform\n /** Override `process.env` for tests. */\n env?: Readonly<Record<string, string | undefined>>\n}\n\n/**\n * Resolve the four storage slots according to the precedence above.\n *\n * Pure aside from one filesystem probe (`readdirSync(home/<prefix>)`,\n * see {@link holdsLegacyStorage}). No directories are created here; the\n * relevant write paths (state store, credentials writer, persist\n * dir) handle their own lazy `mkdirSync` already.\n */\nexport function resolveStorageDirs(opts: ResolveStorageDirsOptions = {}): StorageDirs {\n const env = opts.env ?? process.env\n const home = opts.home ?? homedir()\n const platform = opts.platform ?? process.platform\n // Two prefix forms — legacy mode preserves whatever the caller\n // passed (no implicit dot insertion, so `prefix: 'myapp'` lands at\n // `<storageDir>/myapp` not `<storageDir>/.myapp` — matches the\n // pre-XDG behavior tests already rely on). XDG mode uses the de-\n // dotted form (`'.zidane'` → `'zidane'`) because the XDG dir itself\n // is already the dotfile container — `~/.config/.zidane/` would be\n // a double-dot offense.\n const rawPrefix = opts.prefix ?? '.zidane'\n const prefixBare = rawPrefix.replace(/^\\./, '')\n\n // Branch 1 — explicit storage root → legacy-explicit.\n // Host-supplied `storageDir` beats env so SDK callers can pin a\n // location for embedded usage; otherwise `ZIDANE_STORAGE_DIR` wins.\n const explicitStorageDir = opts.storageDir ?? env.ZIDANE_STORAGE_DIR\n if (explicitStorageDir) {\n const base = resolve(explicitStorageDir, rawPrefix)\n return single(base, 'legacy-explicit')\n }\n\n // Branch 2 — `~/<prefix>/` already holds storage → legacy-existing.\n // Detect ANY of the legacy locations the user might have:\n // - `~/.zidane/` (default prefix)\n // - `~/.<prefix>/` (custom prefix)\n // Skipping it for a user with a customized prefix would leak their\n // data into XDG dirs on next launch.\n const legacyHomeDir = resolve(home, rawPrefix)\n if (holdsLegacyStorage(legacyHomeDir))\n return single(legacyHomeDir, 'legacy-existing')\n\n // Branch 3 — XDG signals present → split layout.\n // The signal is generous: Linux always qualifies, any platform\n // where the user has set any `XDG_*_HOME` var opts in (a savvy\n // mac user can put zidane in their XDG hierarchy too), AND any\n // `ZIDANE_{CONFIG,DATA,STATE,CACHE}_DIR` per-slot override also\n // opts in — otherwise a user pinning JUST `ZIDANE_CACHE_DIR` on\n // macOS would have it silently ignored because none of the\n // `XDG_*_HOME` vars are set.\n const hasXdgSignal = platform === 'linux'\n || !!env.XDG_CONFIG_HOME\n || !!env.XDG_DATA_HOME\n || !!env.XDG_CACHE_HOME\n || !!env.XDG_STATE_HOME\n || !!env.ZIDANE_CONFIG_DIR\n || !!env.ZIDANE_DATA_DIR\n || !!env.ZIDANE_STATE_DIR\n || !!env.ZIDANE_CACHE_DIR\n\n if (hasXdgSignal) {\n return {\n configDir: env.ZIDANE_CONFIG_DIR\n ?? resolve(env.XDG_CONFIG_HOME || resolve(home, '.config'), prefixBare),\n dataDir: env.ZIDANE_DATA_DIR\n ?? resolve(env.XDG_DATA_HOME || resolve(home, '.local', 'share'), prefixBare),\n stateDir: env.ZIDANE_STATE_DIR\n ?? resolve(env.XDG_STATE_HOME || resolve(home, '.local', 'state'), prefixBare),\n cacheDir: env.ZIDANE_CACHE_DIR\n ?? resolve(env.XDG_CACHE_HOME || resolve(home, '.cache'), prefixBare),\n mode: 'xdg',\n }\n }\n\n // Branch 4 — default (mac/windows, no XDG signal, no existing dir).\n // Same as the day-one experience: everything under `~/<prefix>/`.\n return single(legacyHomeDir, 'default')\n}\n\n/**\n * Entries discovery reads from `~/<prefix>/` regardless of the storage\n * mode, plus Finder's `.DS_Store`. Creating one (e.g. the create-extension\n * shortcut writing `~/.zidane/extensions/<name>`) must not be mistaken for\n * a legacy install.\n */\nconst HOME_ANCHORED_ENTRIES = new Set(['extensions', 'skills', 'mcps.json', 'SYSTEM.md', 'AGENTS.md', '.DS_Store'])\n\n/**\n * True when `dir` exists and holds anything beyond home-anchored authored\n * content. An unreadable dir counts as legacy: staying put beats silently\n * moving a user's storage.\n */\nfunction holdsLegacyStorage(dir: string): boolean {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n }\n catch (err) {\n return (err as NodeJS.ErrnoException).code !== 'ENOENT'\n }\n return entries.some(name => !HOME_ANCHORED_ENTRIES.has(name))\n}\n\nfunction single(base: string, mode: StorageMode): StorageDirs {\n return {\n configDir: base,\n dataDir: base,\n stateDir: base,\n cacheDir: base,\n mode,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiGA,SAAgB,mBAAmB,OAAkC,CAAC,GAAgB;CACpF,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,OAAO,KAAK,QAAQ,QAAQ;CAClC,MAAM,WAAW,KAAK,YAAY,QAAQ;CAQ1C,MAAM,YAAY,KAAK,UAAU;CACjC,MAAM,aAAa,UAAU,QAAQ,OAAO,EAAE;CAK9C,MAAM,qBAAqB,KAAK,cAAc,IAAI;CAClD,IAAI,oBAEF,OAAO,OADM,QAAQ,oBAAoB,SACxB,GAAG,iBAAiB;CASvC,MAAM,gBAAgB,QAAQ,MAAM,SAAS;CAC7C,IAAI,mBAAmB,aAAa,GAClC,OAAO,OAAO,eAAe,iBAAiB;CAoBhD,IAVqB,aAAa,WAC7B,CAAC,CAAC,IAAI,mBACN,CAAC,CAAC,IAAI,iBACN,CAAC,CAAC,IAAI,kBACN,CAAC,CAAC,IAAI,kBACN,CAAC,CAAC,IAAI,qBACN,CAAC,CAAC,IAAI,mBACN,CAAC,CAAC,IAAI,oBACN,CAAC,CAAC,IAAI,kBAGT,OAAO;EACL,WAAW,IAAI,qBACV,QAAQ,IAAI,mBAAmB,QAAQ,MAAM,SAAS,GAAG,UAAU;EACxE,SAAS,IAAI,mBACR,QAAQ,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,GAAG,UAAU;EAC9E,UAAU,IAAI,oBACT,QAAQ,IAAI,kBAAkB,QAAQ,MAAM,UAAU,OAAO,GAAG,UAAU;EAC/E,UAAU,IAAI,oBACT,QAAQ,IAAI,kBAAkB,QAAQ,MAAM,QAAQ,GAAG,UAAU;EACtE,MAAM;CACR;CAKF,OAAO,OAAO,eAAe,SAAS;AACxC;;;;;;;AAQA,MAAM,wCAAwB,IAAI,IAAI;CAAC;CAAc;CAAU;CAAa;CAAa;CAAa;AAAW,CAAC;;;;;;AAOlH,SAAS,mBAAmB,KAAsB;CAChD,IAAI;CACJ,IAAI;EACF,UAAU,YAAY,GAAG;CAC3B,SACO,KAAK;EACV,OAAQ,IAA8B,SAAS;CACjD;CACA,OAAO,QAAQ,MAAK,SAAQ,CAAC,sBAAsB,IAAI,IAAI,CAAC;AAC9D;AAEA,SAAS,OAAO,MAAc,MAAgC;CAC5D,OAAO;EACL,WAAW;EACX,SAAS;EACT,UAAU;EACV,UAAU;EACV;CACF;AACF"}
|
package/docs/ACP.md
CHANGED
|
@@ -235,6 +235,58 @@ process.once('SIGTERM', () => {
|
|
|
235
235
|
The subpath also exports `createAcpServer`, JSON-RPC helpers, content mapping
|
|
236
236
|
helpers, and ACP TypeScript types for custom transports and tests.
|
|
237
237
|
|
|
238
|
+
## Client-Declared Tools
|
|
239
|
+
|
|
240
|
+
A zidane extension to ACP: a client can hand the agent its own tools (editor
|
|
241
|
+
commands, UI queries) and run them in-process. `initialize` advertises support
|
|
242
|
+
as `agentCapabilities._meta.zidane.clientTools: true`. The Vim/Neovim plugin in
|
|
243
|
+
`editors/vim` is built on it.
|
|
244
|
+
|
|
245
|
+
Declare tools in `session/new`, `session/load`, or `session/resume`:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"cwd": "/repo",
|
|
250
|
+
"mcpServers": [],
|
|
251
|
+
"_meta": {
|
|
252
|
+
"zidane": {
|
|
253
|
+
"clientTools": [
|
|
254
|
+
{
|
|
255
|
+
"name": "vim_exec",
|
|
256
|
+
"description": "Run an Ex command and return its output.",
|
|
257
|
+
"inputSchema": { "type": "object", "properties": { "command": { "type": "string" } }, "required": ["command"] },
|
|
258
|
+
"mutating": true
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
When the model calls one, the agent sends a request to the client:
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{ "jsonrpc": "2.0", "id": 7, "method": "_zidane/tool_call",
|
|
270
|
+
"params": { "sessionId": "…", "toolCallId": "…", "name": "vim_exec", "input": { "command": "ls" } } }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The client replies `{ "content": "<text>", "isError": false }`. `isError: true`
|
|
274
|
+
fails the tool call with `content` as the message.
|
|
275
|
+
|
|
276
|
+
Rules:
|
|
277
|
+
|
|
278
|
+
- `name` starts with a letter, uses letters, digits, `_` or `-`, is at most 64
|
|
279
|
+
characters, and must not collide with a host tool. Violations fail the
|
|
280
|
+
session request with `-32602`.
|
|
281
|
+
- `mutating` (default `false`) tools always go through
|
|
282
|
+
`session/request_permission` when permissions are enabled, and are hidden
|
|
283
|
+
and blocked in Plan mode. Non-mutating tools stay available in Plan mode.
|
|
284
|
+
- A live session cannot be resumed with a different tool set; omit `_meta` on
|
|
285
|
+
resume to keep the existing one.
|
|
286
|
+
- Prompt cancellation abandons the pending `_zidane/tool_call` and sends a
|
|
287
|
+
`_zidane/tool_call_cancel` notification with `{ sessionId, toolCallId }` so
|
|
288
|
+
the client can stop the work. Clients that run tools synchronously can ignore it.
|
|
289
|
+
|
|
238
290
|
## Protocol Coverage
|
|
239
291
|
|
|
240
292
|
The adapter targets ACP protocol version 1. It implements initialization,
|