@qodeca/xezar 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +72 -39
  2. package/dist/agent-config/account-identity.d.ts +2 -8
  3. package/dist/agent-config/account-identity.js +54 -10
  4. package/dist/agent-config/account-identity.js.map +1 -1
  5. package/dist/agent-config/catalog.d.ts +49 -7
  6. package/dist/agent-config/catalog.js +107 -0
  7. package/dist/agent-config/catalog.js.map +1 -1
  8. package/dist/agent-config/model-settings/pi.d.ts +12 -6
  9. package/dist/agent-config/model-settings/pi.js +12 -6
  10. package/dist/agent-config/model-settings/pi.js.map +1 -1
  11. package/dist/agent-config/seed.js +10 -0
  12. package/dist/agent-config/seed.js.map +1 -1
  13. package/dist/contract/github.d.ts +15 -0
  14. package/dist/contract/index.d.ts +9 -0
  15. package/dist/contract/index.js +1527 -895
  16. package/dist/contract/mcp-api-reference.d.ts +174 -0
  17. package/dist/contract/mcp-audit.d.ts +103 -0
  18. package/dist/contract/mcp-discovery.d.ts +257 -0
  19. package/dist/contract/mcp-event-catalog.d.ts +121 -0
  20. package/dist/contract/mcp-event-catalog.test.d.ts +1 -0
  21. package/dist/contract/mcp-idempotency.d.ts +200 -0
  22. package/dist/contract/mcp-journal.d.ts +268 -0
  23. package/dist/contract/mcp-leader.d.ts +88 -0
  24. package/dist/contract/mcp-ownership.d.ts +78 -0
  25. package/dist/contract/mcp-versioning.d.ts +139 -0
  26. package/dist/contract/runs.d.ts +81 -0
  27. package/dist/core/agent-profiles.d.ts +15 -4
  28. package/dist/core/agent-profiles.js +22 -8
  29. package/dist/core/agent-profiles.js.map +1 -1
  30. package/dist/core/opencode-server-runner.js +71 -11
  31. package/dist/core/opencode-server-runner.js.map +1 -1
  32. package/dist/core/secret-redaction.d.ts +7 -1
  33. package/dist/core/secret-redaction.js +44 -10
  34. package/dist/core/secret-redaction.js.map +1 -1
  35. package/dist/index.js +138 -27
  36. package/dist/index.js.map +1 -1
  37. package/dist/machine-identity.d.ts +66 -0
  38. package/dist/machine-identity.js +96 -0
  39. package/dist/machine-identity.js.map +1 -0
  40. package/dist/mcp/adapters/claude-code.d.ts +41 -0
  41. package/dist/mcp/adapters/claude-code.js +40 -0
  42. package/dist/mcp/adapters/claude-code.js.map +1 -0
  43. package/dist/mcp/adapters/codex.d.ts +140 -0
  44. package/dist/mcp/adapters/codex.js +323 -0
  45. package/dist/mcp/adapters/codex.js.map +1 -0
  46. package/dist/mcp/adapters/opencode.d.ts +148 -0
  47. package/dist/mcp/adapters/opencode.js +528 -0
  48. package/dist/mcp/adapters/opencode.js.map +1 -0
  49. package/dist/mcp/adapters/pi-link.d.ts +102 -0
  50. package/dist/mcp/adapters/pi-link.js +235 -0
  51. package/dist/mcp/adapters/pi-link.js.map +1 -0
  52. package/dist/mcp/adapters/pi.d.ts +188 -0
  53. package/dist/mcp/adapters/pi.js +418 -0
  54. package/dist/mcp/adapters/pi.js.map +1 -0
  55. package/dist/mcp/api-reference.d.ts +26 -0
  56. package/dist/mcp/api-reference.js +133 -0
  57. package/dist/mcp/api-reference.js.map +1 -0
  58. package/dist/mcp/audit-trail.d.ts +162 -0
  59. package/dist/mcp/audit-trail.js +306 -0
  60. package/dist/mcp/audit-trail.js.map +1 -0
  61. package/dist/mcp/bridge.d.ts +81 -0
  62. package/dist/mcp/bridge.js +407 -0
  63. package/dist/mcp/bridge.js.map +1 -0
  64. package/dist/mcp/connection-file.d.ts +49 -0
  65. package/dist/mcp/connection-file.js +63 -0
  66. package/dist/mcp/connection-file.js.map +1 -0
  67. package/dist/mcp/echo-guard.d.ts +94 -0
  68. package/dist/mcp/echo-guard.js +160 -0
  69. package/dist/mcp/echo-guard.js.map +1 -0
  70. package/dist/mcp/event-catalog.d.ts +120 -0
  71. package/dist/mcp/event-catalog.js +306 -0
  72. package/dist/mcp/event-catalog.js.map +1 -0
  73. package/dist/mcp/event-controller.d.ts +230 -0
  74. package/dist/mcp/event-controller.js +596 -0
  75. package/dist/mcp/event-controller.js.map +1 -0
  76. package/dist/mcp/event-journal.d.ts +57 -0
  77. package/dist/mcp/event-journal.js +423 -0
  78. package/dist/mcp/event-journal.js.map +1 -0
  79. package/dist/mcp/index.d.ts +63 -0
  80. package/dist/mcp/index.js +386 -0
  81. package/dist/mcp/index.js.map +1 -0
  82. package/dist/mcp/ipc.d.ts +202 -0
  83. package/dist/mcp/ipc.js +206 -0
  84. package/dist/mcp/ipc.js.map +1 -0
  85. package/dist/mcp/leader-delivery.d.ts +119 -0
  86. package/dist/mcp/leader-delivery.js +450 -0
  87. package/dist/mcp/leader-delivery.js.map +1 -0
  88. package/dist/mcp/operation-receipts.d.ts +210 -0
  89. package/dist/mcp/operation-receipts.js +723 -0
  90. package/dist/mcp/operation-receipts.js.map +1 -0
  91. package/dist/mcp/project-catalogs.d.ts +23 -0
  92. package/dist/mcp/project-catalogs.js +25 -0
  93. package/dist/mcp/project-catalogs.js.map +1 -0
  94. package/dist/mcp/project-leaders.d.ts +26 -0
  95. package/dist/mcp/project-leaders.js +17 -0
  96. package/dist/mcp/project-leaders.js.map +1 -0
  97. package/dist/mcp/protocol.d.ts +50 -0
  98. package/dist/mcp/protocol.js +47 -0
  99. package/dist/mcp/protocol.js.map +1 -0
  100. package/dist/mcp/reconnect.d.ts +297 -0
  101. package/dist/mcp/reconnect.js +465 -0
  102. package/dist/mcp/reconnect.js.map +1 -0
  103. package/dist/mcp/resource-ownership.d.ts +228 -0
  104. package/dist/mcp/resource-ownership.js +390 -0
  105. package/dist/mcp/resource-ownership.js.map +1 -0
  106. package/dist/mcp/service-adapter.d.ts +16488 -0
  107. package/dist/mcp/service-adapter.js +148 -0
  108. package/dist/mcp/service-adapter.js.map +1 -0
  109. package/dist/mcp/service.d.ts +83 -0
  110. package/dist/mcp/service.js +233 -0
  111. package/dist/mcp/service.js.map +1 -0
  112. package/dist/mcp/session-binding.d.ts +105 -0
  113. package/dist/mcp/session-binding.js +163 -0
  114. package/dist/mcp/session-binding.js.map +1 -0
  115. package/dist/mcp/stale-write.d.ts +145 -0
  116. package/dist/mcp/stale-write.js +219 -0
  117. package/dist/mcp/stale-write.js.map +1 -0
  118. package/dist/mcp/tool.d.ts +48 -0
  119. package/dist/mcp/tool.js +24 -0
  120. package/dist/mcp/tool.js.map +1 -0
  121. package/dist/mcp/tools/discovery.d.ts +40 -0
  122. package/dist/mcp/tools/discovery.js +232 -0
  123. package/dist/mcp/tools/discovery.js.map +1 -0
  124. package/dist/mcp/tools/execution-control.d.ts +171 -0
  125. package/dist/mcp/tools/execution-control.js +513 -0
  126. package/dist/mcp/tools/execution-control.js.map +1 -0
  127. package/dist/mcp/tools/handoff-git.d.ts +26 -0
  128. package/dist/mcp/tools/handoff-git.js +544 -0
  129. package/dist/mcp/tools/handoff-git.js.map +1 -0
  130. package/dist/mcp/tools/index.d.ts +2 -0
  131. package/dist/mcp/tools/index.js +23 -0
  132. package/dist/mcp/tools/index.js.map +1 -0
  133. package/dist/mcp/tools/leader-events.d.ts +46 -0
  134. package/dist/mcp/tools/leader-events.js +126 -0
  135. package/dist/mcp/tools/leader-events.js.map +1 -0
  136. package/dist/mcp/tools/local-handoff.d.ts +102 -0
  137. package/dist/mcp/tools/local-handoff.js +306 -0
  138. package/dist/mcp/tools/local-handoff.js.map +1 -0
  139. package/dist/mcp/tools/project-config.d.ts +340 -0
  140. package/dist/mcp/tools/project-config.js +1075 -0
  141. package/dist/mcp/tools/project-config.js.map +1 -0
  142. package/dist/mcp/tools/results-evidence.d.ts +112 -0
  143. package/dist/mcp/tools/results-evidence.js +1045 -0
  144. package/dist/mcp/tools/results-evidence.js.map +1 -0
  145. package/dist/mcp/tools/task-create.d.ts +70 -0
  146. package/dist/mcp/tools/task-create.js +473 -0
  147. package/dist/mcp/tools/task-create.js.map +1 -0
  148. package/dist/mcp/tools/task-reads.d.ts +102 -0
  149. package/dist/mcp/tools/task-reads.js +689 -0
  150. package/dist/mcp/tools/task-reads.js.map +1 -0
  151. package/dist/mcp/tools/work-organisation.d.ts +2 -0
  152. package/dist/mcp/tools/work-organisation.js +502 -0
  153. package/dist/mcp/tools/work-organisation.js.map +1 -0
  154. package/dist/paths.d.ts +17 -7
  155. package/dist/paths.js +18 -8
  156. package/dist/paths.js.map +1 -1
  157. package/dist/runs/project-writer.js +76 -11
  158. package/dist/runs/project-writer.js.map +1 -1
  159. package/dist/runs/retention.d.ts +25 -2
  160. package/dist/runs/retention.js +63 -2
  161. package/dist/runs/retention.js.map +1 -1
  162. package/dist/server/forge/github.js +44 -0
  163. package/dist/server/forge/github.js.map +1 -1
  164. package/dist/server/forge/types.d.ts +16 -0
  165. package/dist/server/project-context.d.ts +81 -8
  166. package/dist/server/project-context.js +143 -35
  167. package/dist/server/project-context.js.map +1 -1
  168. package/dist/server/server.d.ts +1050 -53
  169. package/dist/server/server.js +326 -120
  170. package/dist/server/server.js.map +1 -1
  171. package/dist/server/validators.d.ts +26 -6
  172. package/dist/server/validators.js +17 -0
  173. package/dist/server/validators.js.map +1 -1
  174. package/dist/skills-remote.js +64 -6
  175. package/dist/skills-remote.js.map +1 -1
  176. package/dist/workflows/run.d.ts +277 -12
  177. package/dist/workflows/run.js +599 -79
  178. package/dist/workflows/run.js.map +1 -1
  179. package/dist/workspace/agent-profiles.d.ts +30 -3
  180. package/dist/workspace/agent-profiles.js +39 -4
  181. package/dist/workspace/agent-profiles.js.map +1 -1
  182. package/dist/workspace/config.js +25 -4
  183. package/dist/workspace/config.js.map +1 -1
  184. package/dist/workspace/project-owner.d.ts +210 -0
  185. package/dist/workspace/project-owner.js +518 -0
  186. package/dist/workspace/project-owner.js.map +1 -0
  187. package/package.json +5 -3
  188. package/scripts/pi-leader-extension.ts +420 -0
  189. package/scripts/sync-readme.mjs +83 -2
  190. package/web/dist/assets/{alert-dialog-BVsVcbFy.js → alert-dialog-D6rP6d39.js} +1 -1
  191. package/web/dist/assets/arrow-down-DMOfFIzW.js +1 -0
  192. package/web/dist/assets/arrow-left-C4Ec2BUg.js +1 -0
  193. package/web/dist/assets/{centered-state-Djgly6Ni.js → centered-state-B0ZqrPaa.js} +12 -12
  194. package/web/dist/assets/chevron-right-mHJ-crvn.js +1 -0
  195. package/web/dist/assets/{collapsible-DZ4D-DHG.js → collapsible-DchEHgUM.js} +1 -1
  196. package/web/dist/assets/{commit-list-B-z1JL9G.js → commit-list-BpMim1Rv.js} +1 -1
  197. package/web/dist/assets/{compare-variants-CdS8r8E2.js → compare-variants-D5ItQbAq.js} +1 -1
  198. package/web/dist/assets/{diff-BKew9rQX.js → diff-HqqZ_WDR.js} +2 -2
  199. package/web/dist/assets/{diff-stat-D0mc5V6Z.js → diff-stat-NsU-3ecr.js} +1 -1
  200. package/web/dist/assets/{diff-view-xpIcuLIC.js → diff-view-B7c11Xux.js} +1 -1
  201. package/web/dist/assets/{dropdown-menu-CEQziH7X.js → dropdown-menu-CtTpnyod.js} +1 -1
  202. package/web/dist/assets/{editable-title-BCX3T2BY.js → editable-title-BXtuGQry.js} +1 -1
  203. package/web/dist/assets/{ellipsis-vertical-ByVdDc4z.js → ellipsis-vertical-MHtGZSH6.js} +1 -1
  204. package/web/dist/assets/{file-xBSB5hXO.js → file-DKLw5SOb.js} +1 -1
  205. package/web/dist/assets/{folder-CldgbZ5A.js → folder-Drw6-ufH.js} +1 -1
  206. package/web/dist/assets/{git-toolbar-BYlnf4qf.js → git-toolbar-BQH8bORV.js} +1 -1
  207. package/web/dist/assets/github-ynnpKFCJ.js +1 -0
  208. package/web/dist/assets/{image-preview-Cv5mZr0n.js → image-preview-DLhMN_R9.js} +1 -1
  209. package/web/dist/assets/index-BEsrNdXt.js +29 -0
  210. package/web/dist/assets/index-Da8DOBau.css +2 -0
  211. package/web/dist/assets/{markdown-CgEEE2ND.js → markdown-q365eN6Y.js} +1 -1
  212. package/web/dist/assets/{new-task-form-1r2bgsdD.js → new-task-form-CW-hNIWQ.js} +1 -1
  213. package/web/dist/assets/{pill-q-x22_ej.js → pill-B_TFulXa.js} +1 -1
  214. package/web/dist/assets/{project-router-VDN4jQM8.js → project-router-CLPc-ImZ.js} +1 -1
  215. package/web/dist/assets/{prompt-templates-BmZtCe-L.js → prompt-templates-CLc4RxyO.js} +1 -1
  216. package/web/dist/assets/{repo-git-C2On8SlI.js → repo-git-Is7R6-Tj.js} +1 -1
  217. package/web/dist/assets/{run-diff-vguEMJzh.js → run-diff-DHMvDlVo.js} +2 -2
  218. package/web/dist/assets/run-header-DvZJbQ8I.js +1 -0
  219. package/web/dist/assets/{search-x-QmhaAbg5.js → search-x-Ce2MMetu.js} +1 -1
  220. package/web/dist/assets/{skill-empty-hint-BrzUwByZ.js → skill-empty-hint-C_xCSvzB.js} +1 -1
  221. package/web/dist/assets/skills-CivqD4si.js +1 -0
  222. package/web/dist/assets/{sparkles-CP8QDOgX.js → sparkles-BCqPoj5g.js} +1 -1
  223. package/web/dist/assets/{square-terminal-CGypshSD.js → square-terminal-ikj5g-oK.js} +1 -1
  224. package/web/dist/assets/{tab-link-D4lFzzI1.js → tab-link-VsEjFV1Y.js} +1 -1
  225. package/web/dist/assets/{task-changes-6aopqAmV.js → task-changes-CH-E81-_.js} +1 -1
  226. package/web/dist/assets/{task-commits-u-dwaCyw.js → task-commits-DqfkMFxp.js} +1 -1
  227. package/web/dist/assets/{task-files-IMOZnIUK.js → task-files-D_nkHZ_a.js} +2 -2
  228. package/web/dist/assets/task-thread-bxQompxJ.js +9 -0
  229. package/web/dist/assets/{textarea-CyBCyKIk.js → textarea-xajuW1L_.js} +1 -1
  230. package/web/dist/assets/{thread-loading-Cuq2jchC.js → thread-loading-C3JQZbIE.js} +1 -1
  231. package/web/dist/assets/{trash-2-B6Dp3ZNs.js → trash-2-hJrBhpUD.js} +1 -1
  232. package/web/dist/assets/{triangle-alert-xBya8FUp.js → triangle-alert-D1u82y1a.js} +1 -1
  233. package/web/dist/assets/{upload-D6MXEB9p.js → upload-Bn6FBZ59.js} +1 -1
  234. package/web/dist/assets/{use-desktop-fLDKEJoB.js → use-desktop-DOIMfHfY.js} +1 -1
  235. package/web/dist/assets/{use-submit-shortcut-JTpNzU-u.js → use-submit-shortcut-Dj4DHTdE.js} +1 -1
  236. package/web/dist/assets/utils-YwBpOoRN.js +64 -0
  237. package/web/dist/assets/{workflows-BY3KTkFw.js → workflows-CTaAKz2m.js} +3 -3
  238. package/web/dist/assets/{zoomable-image-GTYwLtkS.js → zoomable-image-C1-6P3tg.js} +1 -1
  239. package/web/dist/index.html +23 -23
  240. package/web/dist/assets/arrow-down-CYgAlZtW.js +0 -1
  241. package/web/dist/assets/arrow-left-BOqE0a3k.js +0 -1
  242. package/web/dist/assets/chevron-right-Cc9BWnbN.js +0 -1
  243. package/web/dist/assets/github-CqGK62Lw.js +0 -1
  244. package/web/dist/assets/index-3nxVmdn5.css +0 -2
  245. package/web/dist/assets/index-qX7n11FX.js +0 -9
  246. package/web/dist/assets/refresh-cw-DcNCj2nu.js +0 -1
  247. package/web/dist/assets/run-header-DM7MiKj9.js +0 -1
  248. package/web/dist/assets/skills-Bj-uG_Hz.js +0 -1
  249. package/web/dist/assets/task-thread-CbMPmrWq.js +0 -9
  250. package/web/dist/assets/utils--z9hRtl3.js +0 -64
@@ -0,0 +1,420 @@
1
+ /**
2
+ * xezar's pi leader extension (#330 WP2).
3
+ *
4
+ * WHAT IT IS FOR. xezar's event controller can push a significant event to a leader running in
5
+ * OpenCode, because `opencode serve` has an address. pi has none: its RPC is stdio-only and
6
+ * spawn-only — no port, no socket, no attach mode (`pi --help` and `docs/rpc.md` on 0.85.1) — and
7
+ * xezar never starts an agent process for a leader. So a pi that YOU started in YOUR terminal could
8
+ * not be reached at all, and its events waited until you typed something. That is what this closes.
9
+ *
10
+ * It runs inside your pi, opens one Unix socket, and writes a small descriptor into the project's
11
+ * own xezar directory saying where that socket is. xezar reads the descriptor when you attach, dials
12
+ * the socket, and from then on speaks pi's OWN RPC vocabulary down it — `prompt`, `steer`,
13
+ * `get_state`, `get_messages` — which this file translates into the pi extension API. xezar's side
14
+ * therefore does not know or care that a socket is involved (`src/mcp/adapters/pi-link.ts`).
15
+ *
16
+ * WHAT IT DOES NOT DO, deliberately:
17
+ * - It sends nothing to xezar on its own. It answers commands and forwards pi's events. It never
18
+ * reads your files, your settings or your credentials, and it opens no network socket. The
19
+ * Unix socket lives inside a directory this extension creates with mode 0700, which is what
20
+ * keeps other accounts on the machine out — see `makePrivateSocketDir` for why the DIRECTORY
21
+ * and not the socket file is the guard.
22
+ * - It starts no turn of its own accord. A turn happens only when xezar hands over an event, which
23
+ * only happens for a project you attached, in a session you started.
24
+ * - It never edits, retries or invents. A command it does not understand is refused by name.
25
+ *
26
+ * INSTALL. Either point pi at it once:
27
+ * pi --extension /path/to/xezar/scripts/pi-leader-extension.ts
28
+ * or copy it into `~/.pi/agent/extensions/` (global) or `<project>/.pi/extensions/` (project-local),
29
+ * which also makes `/reload` work on it. Then, in the cockpit, attach the pi leader.
30
+ *
31
+ * WHY THE SOCKET IS OPENED IN `session_start` AND NOT IN THE FACTORY. pi documents it plainly
32
+ * (`docs/extensions.md` § Long-lived resources and shutdown): a factory may run in an invocation
33
+ * that never starts a session, so background resources belong to `session_start`, with an IDEMPOTENT
34
+ * `session_shutdown` to close them. pi tears the runtime down and rebuilds it on `/new`, `/resume`,
35
+ * `/fork`, `/clone` and `/reload` — not only on quit — so the socket path carries the session id and
36
+ * the teardown is safe to run twice.
37
+ */
38
+
39
+ import { createServer, type Server, type Socket } from 'node:net';
40
+ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
41
+ import { dirname, join, resolve } from 'node:path';
42
+ import { tmpdir } from 'node:os';
43
+
44
+ // The extension API types are pi's, resolved from the pi installation that loads this file. They are
45
+ // imported as types only, so this file needs no dependency of its own and no `package.json`.
46
+ type TextContent = { type: 'text'; text: string };
47
+ type AgentMessage = { role?: unknown; content?: unknown };
48
+ type SessionEntry = { type?: unknown; message?: AgentMessage };
49
+
50
+ interface ReadonlySessionManagerLike {
51
+ getSessionId(): string;
52
+ getCwd(): string;
53
+ getBranch(fromId?: string): SessionEntry[];
54
+ }
55
+
56
+ interface ExtensionContextLike {
57
+ cwd: string;
58
+ sessionManager: ReadonlySessionManagerLike;
59
+ isIdle(): boolean;
60
+ hasPendingMessages(): boolean;
61
+ }
62
+
63
+ interface ExtensionApiLike {
64
+ on(event: string, handler: (event: unknown, ctx: ExtensionContextLike) => unknown): void;
65
+ sendUserMessage(content: string | TextContent[], options?: { deliverAs?: 'steer' | 'followUp' }): void;
66
+ }
67
+
68
+ /** Additive changes keep version 1; an incompatible one bumps it. Must match `pi-link.ts`. */
69
+ const SCHEMA_VERSION = 1;
70
+ const DESCRIPTOR_FILE = 'pi-leader.json';
71
+ const XEZAR_DATA_DIR = join('.local', 'xezar');
72
+ /** A Unix socket path is limited to ~104 bytes on macOS, so it never lives under the repository. */
73
+ const SOCKET_PREFIX = 'xez-pi-';
74
+ const SOCKET_FILE = 'leader.sock';
75
+
76
+ /**
77
+ * A private `0700` directory to put the socket in, or `undefined` if we cannot have one.
78
+ *
79
+ * THE DIRECTORY IS THE GUARD, AND IT HAS TO BE. An earlier version of this file put the socket
80
+ * straight into `os.tmpdir()` and claimed in a comment that only this user could reach it. That was
81
+ * false, and a QA on #358 measured it: with no `TMPDIR` set, Linux's `os.tmpdir()` is `/tmp`, mode
82
+ * `1777`, and the socket landed there as `0755`. Any other local account could then read the whole
83
+ * leader conversation with `get_messages` and write `prompt`/`steer` into a pi that has bash and
84
+ * edit tools. macOS hid it, because it gives each user a `0700` `/var/folders/…/T` — so the bug was
85
+ * invisible on the machine this was written on and live on the ordinary Linux box.
86
+ *
87
+ * A mode on the socket FILE is not the fix: Linux enforces permissions on a Unix socket, macOS and
88
+ * the BSDs do not, so only the containing directory is portable. `/tmp` being sticky (`1777`) is
89
+ * what makes creating our own directory there safe — another user cannot remove or rename it.
90
+ *
91
+ * Three things are therefore deliberate:
92
+ * - the directory is created NON-recursively, so an existing path is an error we handle rather
93
+ * than something we silently adopt;
94
+ * - anything already at the path is removed first, so a leftover from a crashed pi (or a planted
95
+ * symlink) cannot be inherited;
96
+ * - the result is VERIFIED with `lstat` before it is used — the mode is checked, and a symlink is
97
+ * refused, because `mkdir -p` over a symlink-to-directory succeeds and `chmod` would follow it.
98
+ */
99
+ function makePrivateSocketDir(sessionId: string): { dir: string; socket: string } | undefined {
100
+ const dir = join(tmpdir(), `${SOCKET_PREFIX}${sessionId}`);
101
+ try {
102
+ rmSync(dir, { recursive: true, force: true });
103
+ mkdirSync(dir, { mode: 0o700 });
104
+ // mkdir's mode is masked by the process umask, so ask for it again explicitly.
105
+ chmodSync(dir, 0o700);
106
+ const stat = lstatSync(dir);
107
+ if (!stat.isDirectory()) return undefined;
108
+ if ((stat.mode & 0o077) !== 0) return undefined;
109
+ return { dir, socket: join(dir, SOCKET_FILE) };
110
+ } catch {
111
+ // No private directory, no socket. Failing closed here costs push delivery and nothing else:
112
+ // the events stay in xezar's journal and the leader still reads them with `leader_events`.
113
+ return undefined;
114
+ }
115
+ }
116
+
117
+ export default function xezarLeaderExtension(pi: ExtensionApiLike): void {
118
+ let server: Server | undefined;
119
+ let descriptorPath: string | undefined;
120
+ let socketPath: string | undefined;
121
+ let socketDir: string | undefined;
122
+ const clients = new Set<Socket>();
123
+
124
+ /**
125
+ * pi's `on` has no unsubscribe, so these are registered ONCE for the whole life of the extension
126
+ * and broadcast to whatever sockets are open at the time. Registering them per connection would
127
+ * add four handlers to the person's pi on every attach and never take them away — a slow leak
128
+ * inside somebody else's editor, which is not a thing to ship.
129
+ *
130
+ * Only these four. The adapter needs the busy boundary and the user message that marks a real
131
+ * reaction; forwarding more would put session content on a socket for no reason.
132
+ */
133
+ for (const type of ['agent_start', 'agent_settled', 'message_start', 'message_end'] as const) {
134
+ pi.on(type, (event: unknown) => {
135
+ if (clients.size === 0) return;
136
+ const message = (event as { message?: AgentMessage } | undefined)?.message;
137
+ const frame = `${JSON.stringify(message === undefined ? { type } : { type, message })}\n`;
138
+ for (const client of clients) {
139
+ if (client.destroyed) continue;
140
+ try {
141
+ client.write(frame);
142
+ } catch {
143
+ /* A peer that went away is not worth surfacing into someone's coding session. */
144
+ }
145
+ }
146
+ });
147
+ }
148
+
149
+ /** Idempotent, and it must stay that way: pi calls it on quit AND on every session replacement. */
150
+ const teardown = (): void => {
151
+ for (const client of clients) client.destroy();
152
+ clients.clear();
153
+ server?.close();
154
+ server = undefined;
155
+ if (descriptorPath !== undefined) {
156
+ try {
157
+ rmSync(descriptorPath, { force: true });
158
+ } catch {
159
+ /* Leaving a stale file behind is not worth failing a shutdown over; xezar detects it. */
160
+ }
161
+ }
162
+ // The whole private directory, not just the socket inside it.
163
+ if (socketDir !== undefined) {
164
+ try {
165
+ rmSync(socketDir, { recursive: true, force: true });
166
+ } catch {
167
+ /* Same: a leftover empty 0700 directory harms nothing. */
168
+ }
169
+ }
170
+ descriptorPath = undefined;
171
+ socketPath = undefined;
172
+ socketDir = undefined;
173
+ };
174
+
175
+ pi.on('session_start', async (_event: unknown, ctx: ExtensionContextLike) => {
176
+ // A replacement session fires `session_shutdown` first, but run teardown anyway: it is cheap,
177
+ // it is idempotent, and a half-open server here would fail the bind below.
178
+ teardown();
179
+
180
+ const dataDir = findProjectDataDir(ctx.cwd);
181
+ if (dataDir === undefined) return; // Not a xezar project. Do nothing at all, quietly.
182
+
183
+ const sessionId = safeSessionId(ctx);
184
+ const place = makePrivateSocketDir(sessionId);
185
+ if (place === undefined) return; // Could not get a private directory: open nothing at all.
186
+ socketDir = place.dir;
187
+ socketPath = place.socket;
188
+
189
+ const next = createServer((socket) => serve(socket, pi, ctx, clients));
190
+ next.on('error', () => {
191
+ // A leader that cannot listen must not take pi down with it. xezar keeps the rows and the
192
+ // person reads them with `leader_events`, exactly as before this extension existed.
193
+ teardown();
194
+ });
195
+
196
+ await new Promise<void>((done) => {
197
+ next.once('listening', () => done());
198
+ next.once('error', () => done());
199
+ next.listen(socketPath);
200
+ });
201
+ if (!next.listening) {
202
+ teardown();
203
+ return;
204
+ }
205
+ server = next;
206
+
207
+ // Written only once the socket really listens, so the file never names nothing — the same
208
+ // ordering rule xezar's own connection file follows.
209
+ descriptorPath = join(dataDir, DESCRIPTOR_FILE);
210
+ writeDescriptor(descriptorPath, { socket: socketPath, sessionId });
211
+ });
212
+
213
+ pi.on('session_shutdown', () => teardown());
214
+ }
215
+
216
+ /** One connection, JSONL both ways: xezar's commands in, pi's answers and events out. */
217
+ function serve(socket: Socket, pi: ExtensionApiLike, ctx: ExtensionContextLike, clients: Set<Socket>): void {
218
+ clients.add(socket);
219
+ socket.setNoDelay(true);
220
+ socket.setEncoding('utf8');
221
+
222
+ const write = (frame: Record<string, unknown>): void => {
223
+ if (socket.destroyed) return;
224
+ try {
225
+ socket.write(`${JSON.stringify(frame)}\n`);
226
+ } catch {
227
+ /* A peer that went away is not an error worth surfacing into someone's coding session. */
228
+ }
229
+ };
230
+
231
+ // pi's events reach this socket through the one set of handlers registered in the factory, which
232
+ // broadcasts to `clients`. Nothing is subscribed per connection, on purpose — see there.
233
+ let buffer = '';
234
+ socket.on('data', (chunk: string) => {
235
+ buffer += chunk;
236
+ // A peer that never sends a newline must not grow this without bound.
237
+ if (buffer.length > 1_000_000) {
238
+ socket.destroy();
239
+ return;
240
+ }
241
+ let nl: number;
242
+ // Split on `\n` only and strip a trailing `\r` — pi's own framing rule (`docs/rpc.md`).
243
+ while ((nl = buffer.indexOf('\n')) >= 0) {
244
+ const line = buffer.slice(0, nl).replace(/\r$/, '');
245
+ buffer = buffer.slice(nl + 1);
246
+ if (!line.trim()) continue;
247
+ let command: Record<string, unknown>;
248
+ try {
249
+ command = JSON.parse(line) as Record<string, unknown>;
250
+ } catch {
251
+ continue;
252
+ }
253
+ handle(command, pi, ctx, write);
254
+ }
255
+ });
256
+
257
+ const done = (): void => {
258
+ clients.delete(socket);
259
+ };
260
+ socket.on('close', done);
261
+ socket.on('error', done);
262
+ }
263
+
264
+ /**
265
+ * One command. The vocabulary is pi's RPC, so xezar's adapter is unchanged by this file existing.
266
+ *
267
+ * `success` means ACCEPTED, never "the turn went well" — pi's own rule, and the one xezar's adapter
268
+ * is written against.
269
+ */
270
+ function handle(
271
+ command: Record<string, unknown>,
272
+ pi: ExtensionApiLike,
273
+ ctx: ExtensionContextLike,
274
+ write: (frame: Record<string, unknown>) => void,
275
+ ): void {
276
+ const id = typeof command.id === 'string' ? command.id : undefined;
277
+ const type = typeof command.type === 'string' ? command.type : '';
278
+ const reply = (frame: Record<string, unknown>): void => write({ type: 'response', ...(id === undefined ? {} : { id }), command: type, ...frame });
279
+ const text = typeof command.message === 'string' ? command.message : '';
280
+
281
+ try {
282
+ if (type === 'get_state') {
283
+ reply({
284
+ success: true,
285
+ data: {
286
+ isStreaming: !ctx.isIdle(),
287
+ // The extension API answers "are any queued" as a boolean, not a count (pi has no
288
+ // `pendingMessageCount` for extensions). A truthful lower bound: xezar only ever asks
289
+ // whether it is above zero, and this never claims a number it does not know.
290
+ pendingMessageCount: ctx.hasPendingMessages() ? 1 : 0,
291
+ sessionId: safeSessionId(ctx),
292
+ },
293
+ });
294
+ return;
295
+ }
296
+ if (type === 'get_messages') {
297
+ reply({ success: true, data: { messages: conversation(ctx) } });
298
+ return;
299
+ }
300
+ if (type === 'prompt') {
301
+ // Real pi refuses a plain `prompt` during a turn and says so in this exact wording; xezar's
302
+ // adapter matches on it and falls back to `steer`. Answering the same way keeps the two
303
+ // transports interchangeable. `sendUserMessage` cannot report this itself — while streaming
304
+ // it fails into pi's extension-error channel rather than throwing here — so it is checked.
305
+ if (!ctx.isIdle()) {
306
+ reply({
307
+ success: false,
308
+ error: { message: "Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message." },
309
+ });
310
+ return;
311
+ }
312
+ pi.sendUserMessage(text);
313
+ reply({ success: true });
314
+ return;
315
+ }
316
+ if (type === 'steer') {
317
+ pi.sendUserMessage(text, { deliverAs: 'steer' });
318
+ reply({ success: true });
319
+ return;
320
+ }
321
+ reply({ success: false, error: { message: `the xezar leader extension does not implement ${type || '(no type)'}` } });
322
+ } catch (err) {
323
+ reply({ success: false, error: { message: err instanceof Error ? err.message : String(err) } });
324
+ }
325
+ }
326
+
327
+ /**
328
+ * The conversation as it stands, oldest first, in `get_messages` shape.
329
+ *
330
+ * `getBranch()` and not `getEntries()`: entries are the raw append-only file, so after a `/fork` it
331
+ * still carries messages from branches the person walked away from. Not `buildContextEntries()`
332
+ * either: that drops pre-compaction messages, and xezar reads this to find out what it has ALREADY
333
+ * told pi — a dropped marker would make it hand the same event over twice.
334
+ */
335
+ function conversation(ctx: ExtensionContextLike): { role: string; content: TextContent[] }[] {
336
+ let entries: SessionEntry[];
337
+ try {
338
+ entries = ctx.sessionManager.getBranch();
339
+ } catch {
340
+ return [];
341
+ }
342
+ const out: { role: string; content: TextContent[] }[] = [];
343
+ for (const entry of entries) {
344
+ if (entry?.type !== 'message') continue;
345
+ const message = entry.message;
346
+ const role = typeof message?.role === 'string' ? message.role : undefined;
347
+ if (role === undefined) continue;
348
+ out.push({ role, content: textOf(message?.content) });
349
+ }
350
+ return out;
351
+ }
352
+
353
+ /** `UserMessage.content` is `string | (TextContent | ImageContent)[]`. Both are real; handle both. */
354
+ function textOf(content: unknown): TextContent[] {
355
+ if (typeof content === 'string') return [{ type: 'text', text: content }];
356
+ if (!Array.isArray(content)) return [];
357
+ const parts: TextContent[] = [];
358
+ for (const part of content) {
359
+ const text = (part as { type?: unknown; text?: unknown } | null)?.text;
360
+ if ((part as { type?: unknown } | null)?.type === 'text' && typeof text === 'string') parts.push({ type: 'text', text });
361
+ }
362
+ return parts;
363
+ }
364
+
365
+ function safeSessionId(ctx: ExtensionContextLike): string {
366
+ let id: string;
367
+ try {
368
+ id = ctx.sessionManager.getSessionId();
369
+ } catch {
370
+ return `pid-${process.pid}`;
371
+ }
372
+ // Never let a session id become a path of its own.
373
+ const clean = id.replace(/[^A-Za-z0-9_-]/g, '').slice(0, 40);
374
+ return clean.length > 0 ? clean : `pid-${process.pid}`;
375
+ }
376
+
377
+ /**
378
+ * Walk up from pi's working directory for a xezar project's `.local/xezar`. Discovery, not
379
+ * configuration: the person tells this extension nothing, and a directory that is not a xezar
380
+ * project simply gets no leader socket.
381
+ */
382
+ function findProjectDataDir(cwd: string): string | undefined {
383
+ let dir = resolve(cwd);
384
+ for (let depth = 0; depth < 40; depth += 1) {
385
+ const candidate = join(dir, XEZAR_DATA_DIR);
386
+ if (existsSync(candidate)) return candidate;
387
+ const parent = dirname(dir);
388
+ if (parent === dir) return undefined;
389
+ dir = parent;
390
+ }
391
+ return undefined;
392
+ }
393
+
394
+ /** Atomic (tmp + rename) at 0600, like every other descriptor xezar writes. */
395
+ function writeDescriptor(path: string, input: { socket: string; sessionId: string }): void {
396
+ const descriptor = {
397
+ schemaVersion: SCHEMA_VERSION,
398
+ session: { pid: process.pid, startedAt: new Date().toISOString(), sessionId: input.sessionId },
399
+ endpoint: { socket: input.socket },
400
+ };
401
+ const tmp = `${path}.tmp-${process.pid}`;
402
+ try {
403
+ mkdirSync(dirname(path), { recursive: true });
404
+ writeFileSync(tmp, `${JSON.stringify(descriptor, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
405
+ renameSync(tmp, path);
406
+ } catch {
407
+ try {
408
+ rmSync(tmp, { force: true });
409
+ } catch {
410
+ /* Nothing further to do: without a descriptor xezar simply reports no pi leader. */
411
+ }
412
+ }
413
+ }
414
+
415
+ /** Exported for xezar's own tests; pi only ever uses the default export. */
416
+ export const __internals = { conversation, textOf, handle, findProjectDataDir, writeDescriptor, readDescriptor, makePrivateSocketDir, safeSessionId, serve };
417
+
418
+ function readDescriptor(path: string): unknown {
419
+ return JSON.parse(readFileSync(path, 'utf8'));
420
+ }
@@ -7,14 +7,95 @@
7
7
  // package, so the one source is copied in rather than referenced out — the copy is gitignored,
8
8
  // regenerated by every build, and never edited by hand.
9
9
  //
10
+ // The copy is not byte-for-byte: every relative link and image source is rewritten to an
11
+ // absolute GitHub URL (#287). The root README's `docs/...` and `LICENSE` links are right on
12
+ // GitHub, but the published package contains neither, so on npmjs.com every screenshot and
13
+ // every guide link was broken. Links inside code (fenced blocks, inline code spans) are left
14
+ // alone, because there they are examples, not links.
15
+ //
10
16
  // Runs as `prebuild`, so `npm run build` (and the release pipeline, which builds first) always
11
17
  // packs a current copy. `check:pack` is the gate that notices if this ever stops happening.
12
18
 
13
- import { copyFileSync } from 'node:fs';
19
+ import { readFileSync, realpathSync, writeFileSync } from 'node:fs';
14
20
  import path from 'node:path';
15
21
  import { fileURLToPath } from 'node:url';
16
22
 
17
23
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
18
24
  const repoRoot = path.resolve(packageRoot, '../..');
19
25
 
20
- copyFileSync(path.join(repoRoot, 'README.md'), path.join(packageRoot, 'README.md'));
26
+ /** The branch the absolute links point at: the docs a reader follows are the current ones. */
27
+ const REF = 'main';
28
+ const IMAGE = /\.(png|jpe?g|gif|svg|webp|avif)$/i;
29
+
30
+ /** `git+https://github.com/owner/name.git` (the manifest's `repository.url`) → `https://github.com/owner/name`. */
31
+ export function githubRepoUrl(repositoryUrl) {
32
+ const match = /github\.com[/:]([^/]+)\/([^/#?]+?)(?:\.git)?\/?$/.exec(String(repositoryUrl ?? ''));
33
+ if (!match) throw new Error(`sync-readme: repository.url is not a GitHub repository: ${repositoryUrl}`);
34
+ return `https://github.com/${match[1]}/${match[2]}`;
35
+ }
36
+
37
+ function isRelative(target) {
38
+ return !(target.startsWith('#') || target.startsWith('//') || /^[a-z][a-z0-9+.-]*:/i.test(target));
39
+ }
40
+
41
+ function absolute(target, repoUrl) {
42
+ if (!isRelative(target)) return target;
43
+ const hash = target.indexOf('#');
44
+ const file = path.posix.normalize(hash === -1 ? target : target.slice(0, hash)).replace(/^(\.\/|\/)+/, '');
45
+ const fragment = hash === -1 ? '' : target.slice(hash);
46
+ if (file === '.' || file.startsWith('../')) return target; // outside the repository: leave it for the test to flag
47
+ if (IMAGE.test(file)) return `${repoUrl.replace('https://github.com/', 'https://raw.githubusercontent.com/')}/${REF}/${file}${fragment}`;
48
+ return `${repoUrl}/blob/${REF}/${file}${fragment}`;
49
+ }
50
+
51
+ function rewriteProse(text, repoUrl) {
52
+ return text
53
+ .replace(/(\]\()(\s*)([^)\s]+)/g, (_, open, space, target) => `${open}${space}${absolute(target, repoUrl)}`)
54
+ .replace(/(\s(?:src|href)=")([^"]+)(")/gi, (_, open, target, close) => `${open}${absolute(target, repoUrl)}${close}`)
55
+ .replace(/^( {0,3}\[(?!\^)[^\]]+\]:[ \t]*)(\S+)/gm, (_, open, target) => `${open}${absolute(target, repoUrl)}`);
56
+ }
57
+
58
+ /**
59
+ * The README as npm should show it: every relative Markdown link, image, reference definition and
60
+ * HTML `src`/`href` made absolute against `repoUrl`, and everything inside code left untouched.
61
+ */
62
+ export function absolutizeReadmeLinks(markdown, repoUrl) {
63
+ let fence = null;
64
+ return markdown
65
+ .split('\n')
66
+ .map((line) => {
67
+ const marker = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
68
+ if (fence) {
69
+ if (marker && marker[0] === fence[0] && marker.length >= fence.length) fence = null;
70
+ return line;
71
+ }
72
+ if (marker) {
73
+ fence = marker;
74
+ return line;
75
+ }
76
+ // Odd pieces of the split are inline code spans.
77
+ return line
78
+ .split(/(`+[^`]*`+)/)
79
+ .map((piece, i) => (i % 2 === 1 ? piece : rewriteProse(piece, repoUrl)))
80
+ .join('');
81
+ })
82
+ .join('\n');
83
+ }
84
+
85
+ function main() {
86
+ const manifest = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
87
+ const readme = readFileSync(path.join(repoRoot, 'README.md'), 'utf8');
88
+ writeFileSync(path.join(packageRoot, 'README.md'), absolutizeReadmeLinks(readme, githubRepoUrl(manifest.repository?.url)));
89
+ }
90
+
91
+ // Imported by its test for the pure functions above; only a direct run writes the copy. Real paths
92
+ // on both sides, because the script may be reached through a symlinked directory.
93
+ function isDirectRun() {
94
+ try {
95
+ return Boolean(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ if (isDirectRun()) main();
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{i as t,t as n}from"./react-runtime-CCIEwYL0.js";import{D as r,_ as i,b as a,c as o,d as s,f as c,g as l,h as u,l as d,o as f,p as ee,s as te,u as ne,w as re,x as p,y as m}from"./centered-state-Djgly6Ni.js";import{t as h}from"./utils--z9hRtl3.js";var g=e(t(),1),_=n(),v=`Dialog`,[y,b]=a(v),[ie,x]=y(v),S=e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:a,onOpenChange:o,modal:s=!0}=e,c=g.useRef(null),l=g.useRef(null),[d,f]=i({prop:r,defaultProp:a??!1,onChange:o,caller:v});return(0,_.jsx)(ie,{scope:t,triggerRef:c,contentRef:l,contentId:u(),titleId:u(),descriptionId:u(),open:d,onOpenChange:f,onOpenToggle:g.useCallback(()=>f(e=>!e),[f]),modal:s,children:n})};S.displayName=v;var C=`DialogTrigger`,w=g.forwardRef((e,t)=>{let{__scopeDialog:n,...i}=e,a=x(C,n),o=r(t,a.triggerRef);return(0,_.jsx)(p.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":a.open,"aria-controls":a.open?a.contentId:void 0,"data-state":B(a.open),...i,ref:o,onClick:m(e.onClick,a.onOpenToggle)})});w.displayName=C;var T=`DialogPortal`,[E,D]=y(T,{forceMount:void 0}),O=e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=x(T,t);return(0,_.jsx)(E,{scope:t,forceMount:n,children:g.Children.map(r,e=>(0,_.jsx)(l,{present:n||a.open,children:(0,_.jsx)(ne,{asChild:!0,container:i,children:e})}))})};O.displayName=T;var k=`DialogOverlay`,A=g.forwardRef((e,t)=>{let n=D(k,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=x(k,e.__scopeDialog);return a.modal?(0,_.jsx)(l,{present:r||a.open,children:(0,_.jsx)(oe,{...i,ref:t})}):null});A.displayName=k;var ae=re(`DialogOverlay.RemoveScroll`),oe=g.forwardRef((e,t)=>{let{__scopeDialog:n,...i}=e,a=x(k,n),s=r(t,ee());return(0,_.jsx)(o,{as:ae,allowPinchZoom:!0,shards:[a.contentRef],children:(0,_.jsx)(p.div,{"data-state":B(a.open),...i,ref:s,style:{pointerEvents:`auto`,...i.style}})})}),j=`DialogContent`,M=g.forwardRef((e,t)=>{let n=D(j,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=x(j,e.__scopeDialog);return(0,_.jsx)(l,{present:r||a.open,children:a.modal?(0,_.jsx)(se,{...i,ref:t}):(0,_.jsx)(ce,{...i,ref:t})})});M.displayName=j;var se=g.forwardRef((e,t)=>{let n=x(j,e.__scopeDialog),i=g.useRef(null),a=r(t,n.contentRef,i);return g.useEffect(()=>{let e=i.current;if(e)return te(e)},[]),(0,_.jsx)(N,{...e,ref:a,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:m(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:m(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:m(e.onFocusOutside,e=>e.preventDefault())})}),ce=g.forwardRef((e,t)=>{let n=x(j,e.__scopeDialog),r=g.useRef(!1),i=g.useRef(!1);return(0,_.jsx)(N,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})}),N=g.forwardRef((e,t)=>{let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,l=x(j,n);return d(),(0,_.jsx)(_.Fragment,{children:(0,_.jsx)(s,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,_.jsx)(c,{role:`dialog`,id:l.contentId,"aria-describedby":l.descriptionId,"aria-labelledby":l.titleId,"data-state":B(l.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>l.onOpenChange(!1)})})})}),P=`DialogTitle`,F=g.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=x(P,n);return(0,_.jsx)(p.h2,{id:i.titleId,...r,ref:t})});F.displayName=P;var I=`DialogDescription`,L=g.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=x(I,n);return(0,_.jsx)(p.p,{id:i.descriptionId,...r,ref:t})});L.displayName=I;var R=`DialogClose`,z=g.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=x(R,n);return(0,_.jsx)(p.button,{type:`button`,...r,ref:t,onClick:m(e.onClick,()=>i.onOpenChange(!1))})});z.displayName=R;function B(e){return e?`open`:`closed`}var V=`AlertDialog`,[le,ue]=a(V,[b]),H=b(),U=e=>{let{__scopeAlertDialog:t,...n}=e,r=H(t);return(0,_.jsx)(S,{...r,...n,modal:!0})};U.displayName=V;var de=`AlertDialogTrigger`,fe=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(w,{...i,...r,ref:t})});fe.displayName=de;var pe=`AlertDialogPortal`,W=e=>{let{__scopeAlertDialog:t,...n}=e,r=H(t);return(0,_.jsx)(O,{...r,...n})};W.displayName=pe;var me=`AlertDialogOverlay`,G=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(A,{...i,...r,ref:t})});G.displayName=me;var K=`AlertDialogContent`,[he,ge]=le(K),q=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,children:i,...a}=e,o=H(n),s=r(t,g.useRef(null)),c=g.useRef(null);return(0,_.jsx)(he,{scope:n,cancelRef:c,children:(0,_.jsx)(M,{role:`alertdialog`,...o,...a,ref:s,onOpenAutoFocus:m(a.onOpenAutoFocus,e=>{e.preventDefault(),c.current?.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:i})})});q.displayName=K;var _e=`AlertDialogTitle`,J=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(F,{...i,...r,ref:t})});J.displayName=_e;var ve=`AlertDialogDescription`,Y=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(L,{...i,...r,ref:t})});Y.displayName=ve;var ye=`AlertDialogAction`,X=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(z,{...i,...r,ref:t})});X.displayName=ye;var Z=`AlertDialogCancel`,Q=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...i}=e,{cancelRef:a}=ge(Z,n),o=H(n),s=r(t,a);return(0,_.jsx)(z,{...o,...i,ref:s})});Q.displayName=Z;var be=U,xe=W,Se=G,Ce=q,we=X,Te=Q,Ee=J,De=Y;function Oe({...e}){return(0,_.jsx)(be,{"data-slot":`alert-dialog`,...e})}function ke({...e}){return(0,_.jsx)(xe,{"data-slot":`alert-dialog-portal`,...e})}function $({className:e,...t}){return(0,_.jsx)(Se,{"data-slot":`alert-dialog-overlay`,className:h(`fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0`,e),...t})}function Ae({className:e,...t}){return(0,_.jsxs)(ke,{children:[(0,_.jsx)($,{}),(0,_.jsx)(Ce,{"data-slot":`alert-dialog-content`,className:h(`fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-card p-6 shadow-modal duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-md`,e),...t})]})}function je({className:e,...t}){return(0,_.jsx)(`div`,{"data-slot":`alert-dialog-header`,className:h(`flex flex-col gap-2 text-center sm:text-left`,e),...t})}function Me({className:e,...t}){return(0,_.jsx)(`div`,{"data-slot":`alert-dialog-footer`,className:h(`flex flex-col-reverse gap-2 sm:flex-row sm:justify-end`,e),...t})}function Ne({className:e,...t}){return(0,_.jsx)(Ee,{"data-slot":`alert-dialog-title`,className:h(`text-lg leading-none font-semibold`,e),...t})}function Pe({className:e,...t}){return(0,_.jsx)(De,{"data-slot":`alert-dialog-description`,className:h(`text-sm text-muted-foreground`,e),...t})}function Fe({className:e,...t}){return(0,_.jsx)(we,{"data-slot":`alert-dialog-action`,className:h(f({variant:`contrast`}),e),...t})}function Ie({className:e,...t}){return(0,_.jsx)(Te,{"data-slot":`alert-dialog-cancel`,className:h(f({variant:`outline`}),e),...t})}export{Pe as a,Ne as c,M as d,L as f,w as g,F as h,Ae as i,S as l,O as m,Fe as n,Me as o,A as p,Ie as r,je as s,Oe as t,z as u};
1
+ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{i as t,t as n}from"./react-runtime-CCIEwYL0.js";import{D as r,_ as i,b as a,c as o,d as s,f as c,g as l,h as u,l as d,o as f,p as ee,s as te,u as ne,w as re,x as p,y as m}from"./centered-state-B0ZqrPaa.js";import{t as h}from"./utils-YwBpOoRN.js";var g=e(t(),1),_=n(),v=`Dialog`,[y,b]=a(v),[ie,x]=y(v),S=e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:a,onOpenChange:o,modal:s=!0}=e,c=g.useRef(null),l=g.useRef(null),[d,f]=i({prop:r,defaultProp:a??!1,onChange:o,caller:v});return(0,_.jsx)(ie,{scope:t,triggerRef:c,contentRef:l,contentId:u(),titleId:u(),descriptionId:u(),open:d,onOpenChange:f,onOpenToggle:g.useCallback(()=>f(e=>!e),[f]),modal:s,children:n})};S.displayName=v;var C=`DialogTrigger`,w=g.forwardRef((e,t)=>{let{__scopeDialog:n,...i}=e,a=x(C,n),o=r(t,a.triggerRef);return(0,_.jsx)(p.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":a.open,"aria-controls":a.open?a.contentId:void 0,"data-state":B(a.open),...i,ref:o,onClick:m(e.onClick,a.onOpenToggle)})});w.displayName=C;var T=`DialogPortal`,[E,D]=y(T,{forceMount:void 0}),O=e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=x(T,t);return(0,_.jsx)(E,{scope:t,forceMount:n,children:g.Children.map(r,e=>(0,_.jsx)(l,{present:n||a.open,children:(0,_.jsx)(ne,{asChild:!0,container:i,children:e})}))})};O.displayName=T;var k=`DialogOverlay`,A=g.forwardRef((e,t)=>{let n=D(k,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=x(k,e.__scopeDialog);return a.modal?(0,_.jsx)(l,{present:r||a.open,children:(0,_.jsx)(oe,{...i,ref:t})}):null});A.displayName=k;var ae=re(`DialogOverlay.RemoveScroll`),oe=g.forwardRef((e,t)=>{let{__scopeDialog:n,...i}=e,a=x(k,n),s=r(t,ee());return(0,_.jsx)(o,{as:ae,allowPinchZoom:!0,shards:[a.contentRef],children:(0,_.jsx)(p.div,{"data-state":B(a.open),...i,ref:s,style:{pointerEvents:`auto`,...i.style}})})}),j=`DialogContent`,M=g.forwardRef((e,t)=>{let n=D(j,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=x(j,e.__scopeDialog);return(0,_.jsx)(l,{present:r||a.open,children:a.modal?(0,_.jsx)(se,{...i,ref:t}):(0,_.jsx)(ce,{...i,ref:t})})});M.displayName=j;var se=g.forwardRef((e,t)=>{let n=x(j,e.__scopeDialog),i=g.useRef(null),a=r(t,n.contentRef,i);return g.useEffect(()=>{let e=i.current;if(e)return te(e)},[]),(0,_.jsx)(N,{...e,ref:a,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:m(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:m(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:m(e.onFocusOutside,e=>e.preventDefault())})}),ce=g.forwardRef((e,t)=>{let n=x(j,e.__scopeDialog),r=g.useRef(!1),i=g.useRef(!1);return(0,_.jsx)(N,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})}),N=g.forwardRef((e,t)=>{let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,l=x(j,n);return d(),(0,_.jsx)(_.Fragment,{children:(0,_.jsx)(s,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,_.jsx)(c,{role:`dialog`,id:l.contentId,"aria-describedby":l.descriptionId,"aria-labelledby":l.titleId,"data-state":B(l.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>l.onOpenChange(!1)})})})}),P=`DialogTitle`,F=g.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=x(P,n);return(0,_.jsx)(p.h2,{id:i.titleId,...r,ref:t})});F.displayName=P;var I=`DialogDescription`,L=g.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=x(I,n);return(0,_.jsx)(p.p,{id:i.descriptionId,...r,ref:t})});L.displayName=I;var R=`DialogClose`,z=g.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=x(R,n);return(0,_.jsx)(p.button,{type:`button`,...r,ref:t,onClick:m(e.onClick,()=>i.onOpenChange(!1))})});z.displayName=R;function B(e){return e?`open`:`closed`}var V=`AlertDialog`,[le,ue]=a(V,[b]),H=b(),U=e=>{let{__scopeAlertDialog:t,...n}=e,r=H(t);return(0,_.jsx)(S,{...r,...n,modal:!0})};U.displayName=V;var de=`AlertDialogTrigger`,fe=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(w,{...i,...r,ref:t})});fe.displayName=de;var pe=`AlertDialogPortal`,W=e=>{let{__scopeAlertDialog:t,...n}=e,r=H(t);return(0,_.jsx)(O,{...r,...n})};W.displayName=pe;var me=`AlertDialogOverlay`,G=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(A,{...i,...r,ref:t})});G.displayName=me;var K=`AlertDialogContent`,[he,ge]=le(K),q=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,children:i,...a}=e,o=H(n),s=r(t,g.useRef(null)),c=g.useRef(null);return(0,_.jsx)(he,{scope:n,cancelRef:c,children:(0,_.jsx)(M,{role:`alertdialog`,...o,...a,ref:s,onOpenAutoFocus:m(a.onOpenAutoFocus,e=>{e.preventDefault(),c.current?.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:i})})});q.displayName=K;var _e=`AlertDialogTitle`,J=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(F,{...i,...r,ref:t})});J.displayName=_e;var ve=`AlertDialogDescription`,Y=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(L,{...i,...r,ref:t})});Y.displayName=ve;var ye=`AlertDialogAction`,X=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...r}=e,i=H(n);return(0,_.jsx)(z,{...i,...r,ref:t})});X.displayName=ye;var Z=`AlertDialogCancel`,Q=g.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...i}=e,{cancelRef:a}=ge(Z,n),o=H(n),s=r(t,a);return(0,_.jsx)(z,{...o,...i,ref:s})});Q.displayName=Z;var be=U,xe=W,Se=G,Ce=q,we=X,Te=Q,Ee=J,De=Y;function Oe({...e}){return(0,_.jsx)(be,{"data-slot":`alert-dialog`,...e})}function ke({...e}){return(0,_.jsx)(xe,{"data-slot":`alert-dialog-portal`,...e})}function $({className:e,...t}){return(0,_.jsx)(Se,{"data-slot":`alert-dialog-overlay`,className:h(`fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0`,e),...t})}function Ae({className:e,...t}){return(0,_.jsxs)(ke,{children:[(0,_.jsx)($,{}),(0,_.jsx)(Ce,{"data-slot":`alert-dialog-content`,className:h(`fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-card p-6 shadow-modal duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-md`,e),...t})]})}function je({className:e,...t}){return(0,_.jsx)(`div`,{"data-slot":`alert-dialog-header`,className:h(`flex flex-col gap-2 text-center sm:text-left`,e),...t})}function Me({className:e,...t}){return(0,_.jsx)(`div`,{"data-slot":`alert-dialog-footer`,className:h(`flex flex-col-reverse gap-2 sm:flex-row sm:justify-end`,e),...t})}function Ne({className:e,...t}){return(0,_.jsx)(Ee,{"data-slot":`alert-dialog-title`,className:h(`text-lg leading-none font-semibold`,e),...t})}function Pe({className:e,...t}){return(0,_.jsx)(De,{"data-slot":`alert-dialog-description`,className:h(`text-sm text-muted-foreground`,e),...t})}function Fe({className:e,...t}){return(0,_.jsx)(we,{"data-slot":`alert-dialog-action`,className:h(f({variant:`contrast`}),e),...t})}function Ie({className:e,...t}){return(0,_.jsx)(Te,{"data-slot":`alert-dialog-cancel`,className:h(f({variant:`outline`}),e),...t})}export{Pe as a,Ne as c,M as d,L as f,w as g,F as h,Ae as i,S as l,O as m,Fe as n,Me as o,A as p,Ie as r,je as s,Oe as t,z as u};
@@ -0,0 +1 @@
1
+ import{r as e}from"./utils-YwBpOoRN.js";var t=e(`arrow-down`,[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]);export{t};
@@ -0,0 +1 @@
1
+ import{r as e}from"./utils-YwBpOoRN.js";var t=e(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]);export{t};