@wjarka/cezarion 0.14.6 → 0.14.7

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 (123) hide show
  1. package/README.md +37 -2
  2. package/dist/agent-config/model-settings/shared.d.ts +2 -1
  3. package/dist/agent-config/model-settings/shared.js +1 -1
  4. package/dist/agent-config/model-settings/shared.js.map +1 -1
  5. package/dist/ci-wait/client.d.ts +4 -0
  6. package/dist/ci-wait/client.js +67 -0
  7. package/dist/ci-wait/client.js.map +1 -0
  8. package/dist/ci-wait/controller.d.ts +78 -0
  9. package/dist/ci-wait/controller.js +148 -0
  10. package/dist/ci-wait/controller.js.map +1 -0
  11. package/dist/ci-wait/github.d.ts +61 -0
  12. package/dist/ci-wait/github.js +209 -0
  13. package/dist/ci-wait/github.js.map +1 -0
  14. package/dist/ci-wait/injection.d.ts +3 -0
  15. package/dist/ci-wait/injection.js +26 -0
  16. package/dist/ci-wait/injection.js.map +1 -0
  17. package/dist/ci-wait/mcp.d.ts +97 -0
  18. package/dist/ci-wait/mcp.js +35 -0
  19. package/dist/ci-wait/mcp.js.map +1 -0
  20. package/dist/ci-wait/resources.d.ts +7 -0
  21. package/dist/ci-wait/resources.js +32 -0
  22. package/dist/ci-wait/resources.js.map +1 -0
  23. package/dist/ci-wait/supervisor.d.ts +21 -0
  24. package/dist/ci-wait/supervisor.js +163 -0
  25. package/dist/ci-wait/supervisor.js.map +1 -0
  26. package/dist/contract/ci-wait.d.ts +151 -0
  27. package/dist/contract/conversations.d.ts +7 -0
  28. package/dist/contract/delegation.d.ts +2 -0
  29. package/dist/contract/events.d.ts +3 -0
  30. package/dist/contract/index.d.ts +1 -0
  31. package/dist/contract/index.js +1016 -935
  32. package/dist/contract/runs.d.ts +461 -0
  33. package/dist/contract/skills.d.ts +92 -0
  34. package/dist/core/agent-env.js +2 -0
  35. package/dist/core/agent-env.js.map +1 -1
  36. package/dist/core/agent-runner.d.ts +6 -0
  37. package/dist/core/agent-runner.js +1 -0
  38. package/dist/core/agent-runner.js.map +1 -1
  39. package/dist/core/claude-cli-runner.js +10 -0
  40. package/dist/core/claude-cli-runner.js.map +1 -1
  41. package/dist/core/codex-app-server-runner.js +5 -1
  42. package/dist/core/codex-app-server-runner.js.map +1 -1
  43. package/dist/core/codex-app-server-transport.js +3 -1
  44. package/dist/core/codex-app-server-transport.js.map +1 -1
  45. package/dist/core/cursor-acp-runner.js +9 -1
  46. package/dist/core/cursor-acp-runner.js.map +1 -1
  47. package/dist/core/opencode-server-runner.js +6 -2
  48. package/dist/core/opencode-server-runner.js.map +1 -1
  49. package/dist/core/pi-runner.js +7 -1
  50. package/dist/core/pi-runner.js.map +1 -1
  51. package/dist/delegation/cli.js +8 -4
  52. package/dist/delegation/cli.js.map +1 -1
  53. package/dist/delegation/conversations.js +2 -1
  54. package/dist/delegation/conversations.js.map +1 -1
  55. package/dist/delegation/input.d.ts +6 -0
  56. package/dist/delegation/input.js +22 -0
  57. package/dist/delegation/input.js.map +1 -1
  58. package/dist/delegation/provision.js +3 -1
  59. package/dist/delegation/provision.js.map +1 -1
  60. package/dist/delegation/routes.d.ts +11 -0
  61. package/dist/delegation/service.js +31 -7
  62. package/dist/delegation/service.js.map +1 -1
  63. package/dist/delegation/transport.d.ts +11 -0
  64. package/dist/runs/delegation-state.d.ts +2 -0
  65. package/dist/runs/run-index.js +2 -3
  66. package/dist/runs/run-index.js.map +1 -1
  67. package/dist/runs/store.d.ts +514 -3
  68. package/dist/runs/store.js +85 -6
  69. package/dist/runs/store.js.map +1 -1
  70. package/dist/server/server.d.ts +1376 -12
  71. package/dist/workflows/run.d.ts +25 -1
  72. package/dist/workflows/run.js +470 -43
  73. package/dist/workflows/run.js.map +1 -1
  74. package/package.json +4 -3
  75. package/scripts/mock-ci-tool.mjs +53 -0
  76. package/scripts/mock-claude.mjs +8 -0
  77. package/scripts/mock-codex-app-server.mjs +212 -0
  78. package/scripts/mock-cursor-acp.mjs +6 -2
  79. package/scripts/mock-opencode-serve.mjs +392 -0
  80. package/scripts/mock-pi-rpc.mjs +7 -0
  81. package/scripts/pi-ci-wait.mjs +14 -0
  82. package/web/dist/assets/{agent-accounts-lP_xK83o.js → agent-accounts-BZm_2WAv.js} +1 -1
  83. package/web/dist/assets/{alert-dialog-CmDuefYh.js → alert-dialog-B422RI-k.js} +1 -1
  84. package/web/dist/assets/{collapsible-BqX8REmY.js → collapsible-Cy_Fmkrd.js} +1 -1
  85. package/web/dist/assets/{commit-list-BWvv1H7A.js → commit-list-CLxXMZvP.js} +1 -1
  86. package/web/dist/assets/{compare-variants-Bg7eLW3z.js → compare-variants-D2dRj6H_.js} +1 -1
  87. package/web/dist/assets/{diff-controls-HwWHHait.js → diff-controls-BGrXGi7O.js} +1 -1
  88. package/web/dist/assets/{diff-stat-mNkhAc5m.js → diff-stat-Cgnl_hYF.js} +1 -1
  89. package/web/dist/assets/{diff-0GBum40Q2.js → diff-tRE59UNb2.js} +2 -2
  90. package/web/dist/assets/{diff-view-DW64doDY.js → diff-view-DN1ort7Q.js} +1 -1
  91. package/web/dist/assets/{dropdown-menu-sZjYPcLh.js → dropdown-menu-dwDGT8MM.js} +1 -1
  92. package/web/dist/assets/{git-toolbar-DBW5EIcY.js → git-toolbar-UiHnGvE0.js} +1 -1
  93. package/web/dist/assets/{github-CkZ4hWI5.js → github-CaGjGQnW.js} +1 -1
  94. package/web/dist/assets/{image-preview-DZicGNkU.js → image-preview-C2JGcBlC.js} +1 -1
  95. package/web/dist/assets/index-CXZukB1O.css +1 -0
  96. package/web/dist/assets/{index-DWWI-B9n.js → index-D0RwD9-c.js} +3 -3
  97. package/web/dist/assets/{markdown-AY2K3kIV.js → markdown-C_IcKVQd.js} +1 -1
  98. package/web/dist/assets/{pill-CcJqA1cL.js → pill-BsbO6G7y.js} +1 -1
  99. package/web/dist/assets/{project-router-SwsXRKXs.js → project-router-DbLkll25.js} +1 -1
  100. package/web/dist/assets/{prompt-templates-iV2WD2jy.js → prompt-templates-BlTGJB5W.js} +1 -1
  101. package/web/dist/assets/{repo-git-BW3TOs3U.js → repo-git-CWD2dO8S.js} +1 -1
  102. package/web/dist/assets/{run-diff-DPUNj56q.js → run-diff-CyG0Nciv.js} +1 -1
  103. package/web/dist/assets/{run-header-CSaJIGXl.css → run-header-BJQfFL2l.css} +1 -1
  104. package/web/dist/assets/run-header-IFyIGCey.js +1 -0
  105. package/web/dist/assets/{skills-Dv8Vwv5D.js → skills-e7wHnuoc.js} +1 -1
  106. package/web/dist/assets/{tab-link-DSftvkZS.js → tab-link-BWNp3mLY.js} +1 -1
  107. package/web/dist/assets/{task-changes-CTCuVls1.js → task-changes-CpJImtq8.js} +1 -1
  108. package/web/dist/assets/{task-commits-weJQ5GSl.js → task-commits-hujrykPI.js} +1 -1
  109. package/web/dist/assets/{task-files-B4bUimY8.js → task-files-CjTiGtjt.js} +1 -1
  110. package/web/dist/assets/{task-thread-BuXrVB9K.js → task-thread-DDPFDS8C.js} +4 -4
  111. package/web/dist/assets/task-thread-Dc3wplva.css +1 -0
  112. package/web/dist/assets/{textarea-CxRdTWlq.js → textarea-FjaU370i.js} +1 -1
  113. package/web/dist/assets/thread-loading-77sJ8Yu_.js +1 -0
  114. package/web/dist/assets/{token-metrics-Z3UFYbuD.js → token-metrics-Clxh03bE.js} +1 -1
  115. package/web/dist/assets/utils-PYYcpCFZ.js +64 -0
  116. package/web/dist/assets/{workflows-vA27Yi8W.js → workflows-DGUQmWy7.js} +1 -1
  117. package/web/dist/assets/{zoomable-image-DUE1KFJ0.js → zoomable-image-Z3JDnm0b.js} +1 -1
  118. package/web/dist/index.html +14 -14
  119. package/web/dist/assets/index-DK0Mkv2m.css +0 -1
  120. package/web/dist/assets/run-header-DbDluWoV.js +0 -1
  121. package/web/dist/assets/task-thread-DMRD0IVt.css +0 -1
  122. package/web/dist/assets/thread-loading-JlP6dUpX.js +0 -1
  123. package/web/dist/assets/utils-CUnaAifW.js +0 -64
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env node
2
+ // Bundled dry-run mock of `opencode serve` — speaks just enough of the HTTP+SSE
3
+ // API (§4 of agent-event-protocols.md) for the runner wiring test in
4
+ // `opencode-ui-mapper.test.ts`: POST /session, GET /event (SSE bus), one
5
+ // scripted prompt turn. Like the real server's `prompt_async`, the HTTP
6
+ // response resolves immediately — every part and the closing `session.idle`
7
+ // arrive over SSE afterwards, so a correct stream (v1 and v2 alike) must
8
+ // take its turn-end from `session.idle`, never from the HTTP response.
9
+ import { appendFileSync } from 'node:fs';
10
+ import { createServer } from 'node:http';
11
+
12
+ if (process.env.CEZ_MOCK_ARGS_FILE && process.env.OPENCODE_CONFIG_CONTENT) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, JSON.stringify({ type: 'runtime-config', config: JSON.parse(process.env.OPENCODE_CONFIG_CONTENT) }) + '\n');
13
+ if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('opencode', JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? '{}')); }
14
+ const args = process.argv.slice(2);
15
+ const arg = (flag, fallback) => {
16
+ const i = args.indexOf(flag);
17
+ return i >= 0 && args[i + 1] !== undefined ? args[i + 1] : fallback;
18
+ };
19
+ const hostname = arg('--hostname', '127.0.0.1');
20
+
21
+ const SESSION_ID = 'ses_mock_1';
22
+ const MESSAGE_ID = 'msg_mock_1';
23
+
24
+ let sse = null;
25
+ const send = (event) => {
26
+ if (sse) sse.write(`data: ${JSON.stringify(event)}\n\n`);
27
+ };
28
+
29
+ /** Questions the runner can look up an id for. `replyQuestion`/`rejectQuestion`
30
+ * resolve the id through `GET /question` before answering (see
31
+ * `opencode-server-runner.ts`), so a native ask needs an entry here. */
32
+ const pendingQuestions = [];
33
+ let echoSerial = 0;
34
+ let lateQuestionReply = false;
35
+ let answerDone = false;
36
+
37
+ /** The question tool arrives as a `tool` part whose state.input holds it —
38
+ * the shape `opencode-server-runner.test.ts`'s sendQuestion helper drives. */
39
+ const sendQuestionPart = (input) =>
40
+ send({
41
+ type: 'message.part.updated',
42
+ properties: {
43
+ part: {
44
+ id: 'prt_mock_question',
45
+ messageID: MESSAGE_ID,
46
+ sessionID: SESSION_ID,
47
+ type: 'tool',
48
+ callID: 'call_mock_question',
49
+ tool: 'question',
50
+ state: { status: 'running', input },
51
+ },
52
+ },
53
+ });
54
+ const info = (extra) => ({
55
+ id: MESSAGE_ID,
56
+ sessionID: SESSION_ID,
57
+ role: 'assistant',
58
+ time: { created: 1760000000000 },
59
+ modelID: 'mock-model',
60
+ providerID: 'mock',
61
+ mode: 'build',
62
+ path: { cwd: '/repo', root: '/repo' },
63
+ cost: 0,
64
+ tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
65
+ ...extra,
66
+ });
67
+
68
+ const server = createServer((req, res) => {
69
+ const url = req.url ?? '';
70
+ if (req.method === 'GET' && url === '/question') {
71
+ res.writeHead(200, { 'content-type': 'application/json' });
72
+ res.end(JSON.stringify(pendingQuestions));
73
+ return;
74
+ }
75
+ if (req.method === 'GET' && url.startsWith('/event')) {
76
+ res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' });
77
+ sse = res;
78
+ send({ type: 'server.connected', properties: {} });
79
+ return;
80
+ }
81
+ let body = '';
82
+ req.on('data', (chunk) => (body += chunk));
83
+ req.on('end', async () => {
84
+ if (process.env.CEZ_MOCK_ARGS_FILE) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, `${JSON.stringify({ method: req.method, url, body: body ? JSON.parse(body) : undefined })}\n`);
85
+ if (req.method === 'POST' && /^\/question\/[^/]+\/(reply|reject)$/.test(url)) {
86
+ // Native question reply resumes the held turn, then its own session.idle
87
+ // ends it (same SSE lifecycle shape as the baseline; never the HTTP ack).
88
+ const answered = pendingQuestions.some(question => question.id === 'q_mock_1');
89
+ pendingQuestions.length = 0;
90
+ if (answered && url.endsWith('/reply')) setTimeout(() => {
91
+ send({ type: 'message.part.updated', properties: { part: {
92
+ id: 'prt_answer', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: `human answer: ${body}${answerDone ? '\n\nCEZ:DONE' : ''}`,
93
+ } } });
94
+ send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
95
+ }, 30);
96
+ const acknowledge = () => { res.writeHead(200, { 'content-type': 'application/json' }); res.end('{}'); };
97
+ if (lateQuestionReply) setTimeout(acknowledge, 250);
98
+ else acknowledge();
99
+ return;
100
+ }
101
+ if (req.method === 'POST' && url === '/session') {
102
+ res.writeHead(200, { 'content-type': 'application/json' });
103
+ res.end(JSON.stringify({ id: SESSION_ID, title: 'cezar task' }));
104
+ return;
105
+ }
106
+ if (
107
+ req.method === 'POST' &&
108
+ (url === `/session/${SESSION_ID}/prompt_async` || url === `/session/${SESSION_ID}/message`)
109
+ ) {
110
+ if (body.includes('mock:reject-agent-post')) {
111
+ res.writeHead(503, { 'content-type': 'application/json' });
112
+ res.end(JSON.stringify({ error: 'agent prompt rejected' }));
113
+ return;
114
+ }
115
+ // `prompt_async` semantics: acknowledge now, stream the turn over SSE.
116
+ res.writeHead(200, { 'content-type': 'application/json' });
117
+ res.end(JSON.stringify({ info: info({}), parts: [] }));
118
+ // The raw body is enough to spot a `mock:` marker — the prompt text is
119
+ // inside it whatever part shape the runner used to wrap it.
120
+ if (body.includes('mock:ci-wait')) {
121
+ const { ciPrompt } = await import('./mock-ci-tool.mjs');
122
+ const text = await ciPrompt('opencode', JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? '{}'), body);
123
+ send({ type: 'message.part.updated', properties: { part: { id: 'ci-result', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text } } });
124
+ send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
125
+ return;
126
+ }
127
+ if (body.includes('mock:agent-echo')) {
128
+ send({ type: 'message.updated', properties: { info: info({}) } });
129
+ send({ type: 'message.part.updated', properties: { part: {
130
+ id: `prt_echo_${++echoSerial}`, messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
131
+ text: JSON.parse(body).parts.map(part => part.text ?? '').join('\n'),
132
+ } } });
133
+ send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
134
+ return;
135
+ }
136
+ if (body.includes('mock:subagent')) {
137
+ // Wire shape from `__fixtures__/opencode/subtask-nested.ndjson`: a
138
+ // `subtask` part on the parent message, then a child message whose info
139
+ // carries `parentID`, its parts, and the CHILD's own `session.idle`.
140
+ // That child idle arrives first on purpose — the parent turn has to
141
+ // survive it and close only on its own (#5, #600; harness parity S9).
142
+ send({ type: 'message.updated', properties: { info: info({}) } });
143
+ send({ type: 'message.part.updated', properties: { part: {
144
+ id: 'prt_mock_subt', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'subtask',
145
+ prompt: 'Trace every caller of resolveSession', description: 'Trace resolveSession callers',
146
+ agent: 'general',
147
+ } } });
148
+ send({ type: 'message.updated', properties: { info: {
149
+ ...info({}), id: 'msg_mock_child', sessionID: 'ses_mock_child',
150
+ parentID: SESSION_ID, mode: 'subagent',
151
+ } } });
152
+ send({ type: 'message.part.updated', properties: { part: {
153
+ id: 'prt_mock_ctxt', messageID: 'msg_mock_child', sessionID: 'ses_mock_child',
154
+ type: 'text', text: 'Two callers: router.ts and session.ts.',
155
+ time: { start: 1760000008800, end: 1760000008900 },
156
+ } } });
157
+ send({ type: 'session.idle', properties: { sessionID: 'ses_mock_child' } });
158
+ // A second child is still running when the parent declares monitoring.
159
+ send({ type: 'message.part.updated', properties: { part: {
160
+ id: 'prt_mock_late_subt', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'subtask',
161
+ prompt: 'Review the callers', description: 'Review callers', agent: 'general',
162
+ } } });
163
+ send({ type: 'message.updated', properties: { info: {
164
+ ...info({}), id: 'msg_mock_late_child', sessionID: 'ses_mock_late_child',
165
+ parentID: SESSION_ID, mode: 'subagent',
166
+ } } });
167
+ setTimeout(() => {
168
+ send({ type: 'message.part.updated', properties: { part: {
169
+ id: 'prt_mock_ptxt', messageID: MESSAGE_ID, sessionID: SESSION_ID,
170
+ type: 'text', text: 'Still working after the sub-agent.\nCEZ:MONITORING',
171
+ time: { start: 1760000009000, end: 1760000009200 },
172
+ } } });
173
+ // #149: foreign-session text after the parent's marker must stay v2-only.
174
+ send({ type: 'message.part.updated', properties: { part: {
175
+ id: 'prt_mock_late_child', messageID: 'msg_mock_late_child', sessionID: 'ses_mock_late_child',
176
+ type: 'text', text: 'Child review finished.',
177
+ time: { start: 1760000009200, end: 1760000009300 },
178
+ } } });
179
+ send({ type: 'message.updated', properties: { info: info({
180
+ cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
181
+ }) } });
182
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
183
+ }, 60);
184
+ return;
185
+ }
186
+ if (body.includes('mock:provider-error')) {
187
+ // The session-level error frame, wire shape copied verbatim from
188
+ // `__fixtures__/opencode/session-error.ndjson`. #53: a bare upstream
189
+ // rejection used to read like a missing executable, and the run parked
190
+ // as "Needs You" rather than failing (harness parity S7).
191
+ send({ type: 'message.updated', properties: { info: info({}) } });
192
+ send({ type: 'message.part.updated', properties: { part: {
193
+ id: 'prt_mock_perr', messageID: MESSAGE_ID, sessionID: SESSION_ID,
194
+ type: 'text', text: 'Starting the review',
195
+ } } });
196
+ send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: {
197
+ name: 'ProviderAuthError',
198
+ data: { message: 'API key expired — run `opencode auth login`' },
199
+ } } });
200
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
201
+ return;
202
+ }
203
+ if (body.includes('mock:split-text')) {
204
+ // OpenCode streams a text part as successive GROWING snapshots of the
205
+ // same part id; only the final one carries `time.end`. A runner emitting
206
+ // one v1 `text` per snapshot publishes the torn `…CEZ:` prefix, which is
207
+ // #2 on opencode's wire (harness parity S8).
208
+ const full = 'parity split text\n\nCEZ:MONITORING';
209
+ send({ type: 'message.updated', properties: { info: info({}) } });
210
+ for (const upto of [17, 24, full.length]) {
211
+ send({ type: 'message.part.updated', properties: { part: {
212
+ id: 'prt_mock_split', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
213
+ text: full.slice(0, upto),
214
+ ...(upto === full.length ? { time: { start: 1760000000500, end: 1760000000600 } } : {}),
215
+ } } });
216
+ }
217
+ send({ type: 'message.updated', properties: { info: info({
218
+ cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
219
+ }) } });
220
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
221
+ return;
222
+ }
223
+ if (body.includes('mock:ask-bad')) {
224
+ // Malformed: `questions` is not an array, so the runner must REJECT the
225
+ // native question and finish the turn rather than wait for an answer to
226
+ // a card that can never render (#6's rejection path, harness parity R4).
227
+ send({ type: 'message.updated', properties: { info: info({}) } });
228
+ pendingQuestions.push({ id: 'q_mock_bad', sessionID: SESSION_ID });
229
+ sendQuestionPart({ questions: 'not-an-array' });
230
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 60);
231
+ return;
232
+ }
233
+ if (body.includes('mock:ask')) {
234
+ lateQuestionReply = body.includes('mock:ask-reply-late');
235
+ answerDone = body.includes('mock:ask-reply-late-done');
236
+ // The native question tool (#6). No `session.idle` follows: a real ask
237
+ // holds the turn open until the answer is routed back.
238
+ send({ type: 'message.updated', properties: { info: info({}) } });
239
+ pendingQuestions.push({ id: 'q_mock_1', sessionID: SESSION_ID });
240
+ sendQuestionPart({ questions: [{
241
+ header: 'Library',
242
+ question: 'Which test library?',
243
+ options: [
244
+ { label: 'Vitest', description: 'Use the existing test runner' },
245
+ { label: 'Node test', description: 'Use node:test' },
246
+ ],
247
+ }] });
248
+ return;
249
+ }
250
+ if (body.includes('mock:done')) {
251
+ // Declares the task complete so the run reaches cezar's review gate; a
252
+ // markerless turn-end correctly parks as `waiting` instead (harness
253
+ // parity R1).
254
+ send({ type: 'message.updated', properties: { info: info({}) } });
255
+ send({ type: 'message.part.updated', properties: { part: {
256
+ id: 'prt_mock_done', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
257
+ text: 'parity done: the task is complete\n\nCEZ:DONE',
258
+ time: { start: 1760000000500, end: 1760000000600 },
259
+ } } });
260
+ send({ type: 'message.updated', properties: { info: info({
261
+ cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
262
+ }) } });
263
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
264
+ return;
265
+ }
266
+ if (body.includes('mock:hold-ask')) {
267
+ // Portable CEZ:ASK after a pause, so a mid-turn sendMessage can queue
268
+ // and then race the park (run.ts must discard that waiter).
269
+ setTimeout(() => {
270
+ const ask = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which date library should I standardize on?","options":[{"label":"date-fns","description":"Tree-shakeable, functional"},{"label":"Luxon","description":"Immutable, tz-aware"}]}]}';
271
+ send({ type: 'message.updated', properties: { info: info({}) } });
272
+ send({ type: 'message.part.updated', properties: { part: {
273
+ id: 'prt_mock_hold_ask', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text',
274
+ text: ask,
275
+ time: { start: 1760000000500, end: 1760000000600 },
276
+ } } });
277
+ send({ type: 'message.updated', properties: { info: info({
278
+ cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
279
+ }) } });
280
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
281
+ }, 250);
282
+ return;
283
+ }
284
+ if (body.includes('mock:hold')) {
285
+ // The response above is the ack (this is the #4 wire: `prompt_async`
286
+ // resolves immediately, the turn arrives over SSE afterwards). Holding
287
+ // the content behind it is what makes harness parity S2 meaningful.
288
+ setTimeout(() => {
289
+ const held = 'parity hold: content after the pause';
290
+ send({ type: 'message.updated', properties: { info: info({}) } });
291
+ send({ type: 'message.part.updated', properties: { part: {
292
+ id: 'prt_mock_hold', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: held,
293
+ } } });
294
+ send({ type: 'message.updated', properties: { info: info({
295
+ cost: 0.0001, tokens: { input: 20, output: 10, reasoning: 0, cache: { read: 0, write: 0 } },
296
+ }) } });
297
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
298
+ }, 250);
299
+ return;
300
+ }
301
+ send({ type: 'message.updated', properties: { info: info({}) } });
302
+ send({
303
+ type: 'message.part.updated',
304
+ properties: {
305
+ part: { id: 'prt_mock_t1', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Checking the working tree.' },
306
+ },
307
+ });
308
+ send({
309
+ type: 'message.part.updated',
310
+ properties: {
311
+ part: {
312
+ id: 'prt_mock_c1',
313
+ messageID: MESSAGE_ID,
314
+ sessionID: SESSION_ID,
315
+ type: 'tool',
316
+ callID: 'call_mock_1',
317
+ tool: 'bash',
318
+ state: { status: 'pending', input: { command: 'git status --short' }, raw: '{}' },
319
+ },
320
+ },
321
+ });
322
+ send({
323
+ type: 'message.part.updated',
324
+ properties: {
325
+ part: {
326
+ id: 'prt_mock_c1',
327
+ messageID: MESSAGE_ID,
328
+ sessionID: SESSION_ID,
329
+ type: 'tool',
330
+ callID: 'call_mock_1',
331
+ tool: 'bash',
332
+ state: { status: 'running', input: { command: 'git status --short' }, title: 'git status --short', time: { start: 1760000000100 } },
333
+ },
334
+ },
335
+ });
336
+ send({
337
+ type: 'message.part.updated',
338
+ properties: {
339
+ part: {
340
+ id: 'prt_mock_c1',
341
+ messageID: MESSAGE_ID,
342
+ sessionID: SESSION_ID,
343
+ type: 'tool',
344
+ callID: 'call_mock_1',
345
+ tool: 'bash',
346
+ state: {
347
+ status: 'completed',
348
+ input: { command: 'git status --short' },
349
+ output: ' M src/example.ts\n',
350
+ title: 'git status --short',
351
+ metadata: { exit: 0 },
352
+ time: { start: 1760000000100, end: 1760000000400 },
353
+ },
354
+ },
355
+ },
356
+ });
357
+ send({
358
+ type: 'message.updated',
359
+ properties: {
360
+ info: info({ cost: 0.0021, tokens: { input: 1200, output: 300, reasoning: 0, cache: { read: 0, write: 0 } } }),
361
+ },
362
+ });
363
+ setTimeout(() => {
364
+ send({
365
+ type: 'message.part.updated',
366
+ properties: {
367
+ part: {
368
+ id: 'prt_mock_t2',
369
+ messageID: MESSAGE_ID,
370
+ sessionID: SESSION_ID,
371
+ type: 'text',
372
+ text: 'Done.',
373
+ time: { start: 1760000000500, end: 1760000000600 },
374
+ },
375
+ },
376
+ });
377
+ }, 30);
378
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 90);
379
+ return;
380
+ }
381
+ res.writeHead(200, { 'content-type': 'application/json' });
382
+ res.end('{}');
383
+ });
384
+ });
385
+
386
+ // Test sessions share a host: let the OS allocate a port instead of racing
387
+ // the runner’s random requested port against other fixtures.
388
+ server.listen(0, hostname, () => {
389
+ // The runner reads the bound URL back from stdout, like the real server.
390
+ console.log(`opencode server listening on http://${hostname}:${server.address().port}`);
391
+ });
392
+ process.on('SIGTERM', () => process.exit(0));
@@ -6,6 +6,7 @@ if (process.env.CEZ_MOCK_ARGS_FILE) appendFileSync(process.env.CEZ_MOCK_ARGS_FIL
6
6
  // Pi handles SIGTERM and reports 128 + signal, rather than a null exit code.
7
7
  process.on('SIGTERM', () => process.exit(143));
8
8
 
9
+ if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('pi', process.argv.slice(2)); }
9
10
  const sessionId = '00000000-0000-4000-8000-0000000000pi';
10
11
  const send = (value) => process.stdout.write(`${JSON.stringify(value)}\n`);
11
12
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -61,6 +62,12 @@ for await (const line of readline.createInterface({ input: process.stdin })) {
61
62
  pendingMessageCount: 0,
62
63
  },
63
64
  });
65
+ } else if (command.type === 'prompt' && command.message.includes('mock:ci-wait')) {
66
+ send({ id: command.id, type: 'response', command: 'prompt', success: true });
67
+ send({ type: 'agent_start' }); send({ type: 'turn_start' });
68
+ const { ciPrompt } = await import('./mock-ci-tool.mjs');
69
+ sendText([await ciPrompt('pi', process.argv.slice(2), command.message)]);
70
+ sendTurnEnd();
64
71
  } else if (command.type === 'prompt' && command.message.includes('mock:agent-echo')) {
65
72
  // rpc-lifecycle.ndjson's normal prompt/assistant/settled sequence.
66
73
  send({ id: command.id, type: 'response', command: 'prompt', success: true });
@@ -0,0 +1,14 @@
1
+ // Pi 0.87.0 extension API accepts JSON Schema (TypeBox-compatible parameters).
2
+ // Resolve beside this installed script; no private workspace dependency or download.
3
+ import { existsSync } from 'node:fs';
4
+ const source = new URL('../src/ci-wait/mcp.ts', import.meta.url);
5
+ const { ciToolDefinition, invokeCiTool } = await import(existsSync(source) ? source.href : new URL('../dist/ci-wait/mcp.js', import.meta.url).href);
6
+ export default function ciWaitExtension(pi) {
7
+ pi.registerTool({
8
+ name: ciToolDefinition.name,
9
+ label: 'Wait for CI',
10
+ description: ciToolDefinition.description,
11
+ parameters: ciToolDefinition.inputSchema,
12
+ async execute(_toolCallId, params) { return invokeCiTool(params); },
13
+ });
14
+ }
@@ -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{C as r,D as i,I as a,Jn as o,M as s,N as c,O as l,S as u,T as d,b as f,j as p,k as m,un as h,w as g,x as _,z as v}from"./alert-dialog-CmDuefYh.js";import{t as y}from"./utils-CUnaAifW.js";import{_ as b,g as x,h as ee,m as te,p as S}from"./dropdown-menu-sZjYPcLh.js";var C=e(t(),1),w=n(),T=`Popover`,[E,ne]=s(T,[b]),D=b(),[O,k]=E(T),A=e=>{let{__scopePopover:t,children:n,open:r,defaultOpen:a,onOpenChange:o,modal:s=!1}=e,c=D(t),l=C.useRef(null),[u,d]=C.useState(!1),[f,p]=m({prop:r,defaultProp:a??!1,onChange:o,caller:T});return(0,w.jsx)(x,{...c,children:(0,w.jsx)(O,{scope:t,contentId:i(),triggerRef:l,open:f,onOpenChange:p,onOpenToggle:C.useCallback(()=>p(e=>!e),[p]),hasCustomAnchor:u,onCustomAnchorAdd:C.useCallback(()=>d(!0),[]),onCustomAnchorRemove:C.useCallback(()=>d(!1),[]),modal:s,children:n})})};A.displayName=T;var j=`PopoverAnchor`,M=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(j,n),a=D(n),{onCustomAnchorAdd:o,onCustomAnchorRemove:s}=i;return C.useEffect(()=>(o(),()=>s()),[o,s]),(0,w.jsx)(S,{...a,...r,ref:t})});M.displayName=j;var N=`PopoverTrigger`,P=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(N,n),a=D(n),o=v(t,i.triggerRef),s=(0,w.jsx)(c.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":J(i.open),...r,ref:o,onClick:p(e.onClick,i.onOpenToggle)});return i.hasCustomAnchor?s:(0,w.jsx)(S,{asChild:!0,...a,children:s})});P.displayName=N;var F=`PopoverPortal`,[re,I]=E(F,{forceMount:void 0}),L=e=>{let{__scopePopover:t,forceMount:n,children:i,container:a}=e,o=k(F,t);return(0,w.jsx)(re,{scope:t,forceMount:n,children:(0,w.jsx)(l,{present:n||o.open,children:(0,w.jsx)(r,{asChild:!0,container:a,children:i})})})};L.displayName=F;var R=`PopoverContent`,z=C.forwardRef((e,t)=>{let n=I(R,e.__scopePopover),{forceMount:r=n.forceMount,...i}=e,a=k(R,e.__scopePopover);return(0,w.jsx)(l,{present:r||a.open,children:a.modal?(0,w.jsx)(V,{...i,ref:t}):(0,w.jsx)(H,{...i,ref:t})})});z.displayName=R;var B=a(`PopoverContent.RemoveScroll`),V=C.forwardRef((e,t)=>{let n=k(R,e.__scopePopover),r=C.useRef(null),i=v(t,r),a=C.useRef(!1);return C.useEffect(()=>{let e=r.current;if(e)return f(e)},[]),(0,w.jsx)(_,{as:B,allowPinchZoom:!0,children:(0,w.jsx)(U,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:p(e.onCloseAutoFocus,e=>{e.preventDefault(),a.current||n.triggerRef.current?.focus()}),onPointerDownOutside:p(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0,r=t.button===2||n;a.current=r},{checkForDefaultPrevented:!1}),onFocusOutside:p(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1})})})}),H=C.forwardRef((e,t)=>{let n=k(R,e.__scopePopover),r=C.useRef(!1),i=C.useRef(!1);return(0,w.jsx)(U,{...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()}})}),U=C.forwardRef((e,t)=>{let{__scopePopover:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,disableOutsidePointerEvents:o,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onInteractOutside:f,...p}=e,m=k(R,n),h=D(n);return u(),(0,w.jsx)(g,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,w.jsx)(d,{asChild:!0,disableOutsidePointerEvents:o,onInteractOutside:f,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onDismiss:()=>m.onOpenChange(!1),deferPointerDownOutside:!0,children:(0,w.jsx)(ee,{"data-state":J(m.open),role:`dialog`,id:m.contentId,...h,...p,ref:t,style:{...p.style,"--radix-popover-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-popover-content-available-width":`var(--radix-popper-available-width)`,"--radix-popover-content-available-height":`var(--radix-popper-available-height)`,"--radix-popover-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-popover-trigger-height":`var(--radix-popper-anchor-height)`}})})})}),W=`PopoverClose`,G=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(W,n);return(0,w.jsx)(c.button,{type:`button`,...r,ref:t,onClick:p(e.onClick,()=>i.onOpenChange(!1))})});G.displayName=W;var K=`PopoverArrow`,q=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=D(n);return(0,w.jsx)(te,{...i,...r,ref:t})});q.displayName=K;function J(e){return e?`open`:`closed`}var Y=A,X=M,Z=P,ie=L,ae=z;function Q(e){let t=e.visualViewport;return t?Math.max(0,Math.round(e.innerHeight-t.height-t.offsetTop)):0}function oe(e,t,n,r=250){let i=e.visualViewport;if(!i)return t(0),()=>{};let a,o=()=>{let i=Q(e);t(i),n&&(clearTimeout(a),a=setTimeout(()=>n(i),r))};return t(Q(e)),i.addEventListener(`resize`,o),i.addEventListener(`scroll`,o),()=>{clearTimeout(a),i.removeEventListener(`resize`,o),i.removeEventListener(`scroll`,o),t(0)}}function se(e){let t=e.visualViewport;return t?{top:Math.max(0,Math.round(t.offsetTop)),bottom:Q(e)}:{top:0,bottom:0}}function ce(e,t=0){let n=typeof t==`number`?{top:t,right:t,bottom:t,left:t}:t;return{top:(n.top??0)+e.top,right:n.right??0,bottom:(n.bottom??0)+e.bottom,left:n.left??0}}function le(e,t){let n=e.visualViewport;if(!n)return t({top:0,bottom:0}),()=>{};let r=()=>t(se(e));return r(),n.addEventListener(`resize`,r),n.addEventListener(`scroll`,r),()=>{n.removeEventListener(`resize`,r),n.removeEventListener(`scroll`,r)}}function ue(){let[e,t]=(0,C.useState)({top:0,bottom:0});return(0,C.useEffect)(()=>le(window,e=>t(t=>t.top===e.top&&t.bottom===e.bottom?t:e)),[]),e}function de(e){let t=(0,C.useRef)(e);t.current=e,(0,C.useEffect)(()=>{let e=document.documentElement;return oe(window,t=>e.style.setProperty(`--kb`,`${t}px`),e=>t.current?.(e))},[])}var $=C.createContext(null);function fe({...e}){let t=C.useRef(!1);return(0,w.jsx)($,{value:t,children:(0,w.jsx)(Y,{"data-slot":`popover`,...e})})}function pe({onPointerDownCapture:e,onKeyDownCapture:t,...n}){let r=C.useContext($);return(0,w.jsx)(Z,{"data-slot":`popover-trigger`,...n,onPointerDownCapture:t=>{r&&(r.current=t.pointerType===`touch`),e?.(t)},onKeyDownCapture:e=>{r&&(r.current=!1),t?.(e)}})}function me({className:e,align:t=`center`,sideOffset:n=4,collisionPadding:r,onOpenAutoFocus:i,...a}){let o=ue(),s=C.useContext($);return(0,w.jsx)(ie,{children:(0,w.jsx)(ae,{"data-slot":`popover-content`,align:t,sideOffset:n,onOpenAutoFocus:e=>{let t=s?.current;if(s&&(s.current=!1),i?.(e),e.defaultPrevented||!t)return;let n=e.target;n instanceof HTMLElement&&n.querySelector(`[cmdk-input]`)&&(e.preventDefault(),n.focus({preventScroll:!0}))},collisionPadding:ce(o,r),className:y(`z-50 w-72 max-w-(--radix-popover-content-available-width) [overflow-wrap:anywhere] origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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`,e),...a})})}function he({...e}){return(0,w.jsx)(X,{"data-slot":`popover-anchor`,...e})}function ge(){let e=h(),t=o(),n=(e.data?.profiles??[]).map(e=>({provider:e.provider,id:e.id,label:e.label,configDir:e.configDir})),r=t.data?.info?.root;return{accounts:n,repoAccount:r?e.data?.selections?.[r]:void 0}}function _e(e,t){return e.filter(e=>e.provider===t).length>1}export{me as a,he as i,ge as n,pe as o,fe as r,de as s,_e as t};
1
+ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{i as t,t as n}from"./react-runtime-CCIEwYL0.js";import{C as r,D as i,I as a,Jn as o,M as s,N as c,O as l,S as u,T as d,b as f,j as p,k as m,un as h,w as g,x as _,z as v}from"./alert-dialog-B422RI-k.js";import{t as y}from"./utils-PYYcpCFZ.js";import{_ as b,g as x,h as ee,m as te,p as S}from"./dropdown-menu-dwDGT8MM.js";var C=e(t(),1),w=n(),T=`Popover`,[E,ne]=s(T,[b]),D=b(),[O,k]=E(T),A=e=>{let{__scopePopover:t,children:n,open:r,defaultOpen:a,onOpenChange:o,modal:s=!1}=e,c=D(t),l=C.useRef(null),[u,d]=C.useState(!1),[f,p]=m({prop:r,defaultProp:a??!1,onChange:o,caller:T});return(0,w.jsx)(x,{...c,children:(0,w.jsx)(O,{scope:t,contentId:i(),triggerRef:l,open:f,onOpenChange:p,onOpenToggle:C.useCallback(()=>p(e=>!e),[p]),hasCustomAnchor:u,onCustomAnchorAdd:C.useCallback(()=>d(!0),[]),onCustomAnchorRemove:C.useCallback(()=>d(!1),[]),modal:s,children:n})})};A.displayName=T;var j=`PopoverAnchor`,M=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(j,n),a=D(n),{onCustomAnchorAdd:o,onCustomAnchorRemove:s}=i;return C.useEffect(()=>(o(),()=>s()),[o,s]),(0,w.jsx)(S,{...a,...r,ref:t})});M.displayName=j;var N=`PopoverTrigger`,P=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(N,n),a=D(n),o=v(t,i.triggerRef),s=(0,w.jsx)(c.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":J(i.open),...r,ref:o,onClick:p(e.onClick,i.onOpenToggle)});return i.hasCustomAnchor?s:(0,w.jsx)(S,{asChild:!0,...a,children:s})});P.displayName=N;var F=`PopoverPortal`,[re,I]=E(F,{forceMount:void 0}),L=e=>{let{__scopePopover:t,forceMount:n,children:i,container:a}=e,o=k(F,t);return(0,w.jsx)(re,{scope:t,forceMount:n,children:(0,w.jsx)(l,{present:n||o.open,children:(0,w.jsx)(r,{asChild:!0,container:a,children:i})})})};L.displayName=F;var R=`PopoverContent`,z=C.forwardRef((e,t)=>{let n=I(R,e.__scopePopover),{forceMount:r=n.forceMount,...i}=e,a=k(R,e.__scopePopover);return(0,w.jsx)(l,{present:r||a.open,children:a.modal?(0,w.jsx)(V,{...i,ref:t}):(0,w.jsx)(H,{...i,ref:t})})});z.displayName=R;var B=a(`PopoverContent.RemoveScroll`),V=C.forwardRef((e,t)=>{let n=k(R,e.__scopePopover),r=C.useRef(null),i=v(t,r),a=C.useRef(!1);return C.useEffect(()=>{let e=r.current;if(e)return f(e)},[]),(0,w.jsx)(_,{as:B,allowPinchZoom:!0,children:(0,w.jsx)(U,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:p(e.onCloseAutoFocus,e=>{e.preventDefault(),a.current||n.triggerRef.current?.focus()}),onPointerDownOutside:p(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0,r=t.button===2||n;a.current=r},{checkForDefaultPrevented:!1}),onFocusOutside:p(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1})})})}),H=C.forwardRef((e,t)=>{let n=k(R,e.__scopePopover),r=C.useRef(!1),i=C.useRef(!1);return(0,w.jsx)(U,{...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()}})}),U=C.forwardRef((e,t)=>{let{__scopePopover:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,disableOutsidePointerEvents:o,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onInteractOutside:f,...p}=e,m=k(R,n),h=D(n);return u(),(0,w.jsx)(g,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,w.jsx)(d,{asChild:!0,disableOutsidePointerEvents:o,onInteractOutside:f,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onDismiss:()=>m.onOpenChange(!1),deferPointerDownOutside:!0,children:(0,w.jsx)(ee,{"data-state":J(m.open),role:`dialog`,id:m.contentId,...h,...p,ref:t,style:{...p.style,"--radix-popover-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-popover-content-available-width":`var(--radix-popper-available-width)`,"--radix-popover-content-available-height":`var(--radix-popper-available-height)`,"--radix-popover-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-popover-trigger-height":`var(--radix-popper-anchor-height)`}})})})}),W=`PopoverClose`,G=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(W,n);return(0,w.jsx)(c.button,{type:`button`,...r,ref:t,onClick:p(e.onClick,()=>i.onOpenChange(!1))})});G.displayName=W;var K=`PopoverArrow`,q=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=D(n);return(0,w.jsx)(te,{...i,...r,ref:t})});q.displayName=K;function J(e){return e?`open`:`closed`}var Y=A,X=M,Z=P,ie=L,ae=z;function Q(e){let t=e.visualViewport;return t?Math.max(0,Math.round(e.innerHeight-t.height-t.offsetTop)):0}function oe(e,t,n,r=250){let i=e.visualViewport;if(!i)return t(0),()=>{};let a,o=()=>{let i=Q(e);t(i),n&&(clearTimeout(a),a=setTimeout(()=>n(i),r))};return t(Q(e)),i.addEventListener(`resize`,o),i.addEventListener(`scroll`,o),()=>{clearTimeout(a),i.removeEventListener(`resize`,o),i.removeEventListener(`scroll`,o),t(0)}}function se(e){let t=e.visualViewport;return t?{top:Math.max(0,Math.round(t.offsetTop)),bottom:Q(e)}:{top:0,bottom:0}}function ce(e,t=0){let n=typeof t==`number`?{top:t,right:t,bottom:t,left:t}:t;return{top:(n.top??0)+e.top,right:n.right??0,bottom:(n.bottom??0)+e.bottom,left:n.left??0}}function le(e,t){let n=e.visualViewport;if(!n)return t({top:0,bottom:0}),()=>{};let r=()=>t(se(e));return r(),n.addEventListener(`resize`,r),n.addEventListener(`scroll`,r),()=>{n.removeEventListener(`resize`,r),n.removeEventListener(`scroll`,r)}}function ue(){let[e,t]=(0,C.useState)({top:0,bottom:0});return(0,C.useEffect)(()=>le(window,e=>t(t=>t.top===e.top&&t.bottom===e.bottom?t:e)),[]),e}function de(e){let t=(0,C.useRef)(e);t.current=e,(0,C.useEffect)(()=>{let e=document.documentElement;return oe(window,t=>e.style.setProperty(`--kb`,`${t}px`),e=>t.current?.(e))},[])}var $=C.createContext(null);function fe({...e}){let t=C.useRef(!1);return(0,w.jsx)($,{value:t,children:(0,w.jsx)(Y,{"data-slot":`popover`,...e})})}function pe({onPointerDownCapture:e,onKeyDownCapture:t,...n}){let r=C.useContext($);return(0,w.jsx)(Z,{"data-slot":`popover-trigger`,...n,onPointerDownCapture:t=>{r&&(r.current=t.pointerType===`touch`),e?.(t)},onKeyDownCapture:e=>{r&&(r.current=!1),t?.(e)}})}function me({className:e,align:t=`center`,sideOffset:n=4,collisionPadding:r,onOpenAutoFocus:i,...a}){let o=ue(),s=C.useContext($);return(0,w.jsx)(ie,{children:(0,w.jsx)(ae,{"data-slot":`popover-content`,align:t,sideOffset:n,onOpenAutoFocus:e=>{let t=s?.current;if(s&&(s.current=!1),i?.(e),e.defaultPrevented||!t)return;let n=e.target;n instanceof HTMLElement&&n.querySelector(`[cmdk-input]`)&&(e.preventDefault(),n.focus({preventScroll:!0}))},collisionPadding:ce(o,r),className:y(`z-50 w-72 max-w-(--radix-popover-content-available-width) [overflow-wrap:anywhere] origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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`,e),...a})})}function he({...e}){return(0,w.jsx)(X,{"data-slot":`popover-anchor`,...e})}function ge(){let e=h(),t=o(),n=(e.data?.profiles??[]).map(e=>({provider:e.provider,id:e.id,label:e.label,configDir:e.configDir})),r=t.data?.info?.root;return{accounts:n,repoAccount:r?e.data?.selections?.[r]:void 0}}function _e(e,t){return e.filter(e=>e.provider===t).length>1}export{me as a,he as i,ge as n,pe as o,fe as r,de as s,_e as t};