@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.
- package/README.md +37 -2
- package/dist/agent-config/model-settings/shared.d.ts +2 -1
- package/dist/agent-config/model-settings/shared.js +1 -1
- package/dist/agent-config/model-settings/shared.js.map +1 -1
- package/dist/ci-wait/client.d.ts +4 -0
- package/dist/ci-wait/client.js +67 -0
- package/dist/ci-wait/client.js.map +1 -0
- package/dist/ci-wait/controller.d.ts +78 -0
- package/dist/ci-wait/controller.js +148 -0
- package/dist/ci-wait/controller.js.map +1 -0
- package/dist/ci-wait/github.d.ts +61 -0
- package/dist/ci-wait/github.js +209 -0
- package/dist/ci-wait/github.js.map +1 -0
- package/dist/ci-wait/injection.d.ts +3 -0
- package/dist/ci-wait/injection.js +26 -0
- package/dist/ci-wait/injection.js.map +1 -0
- package/dist/ci-wait/mcp.d.ts +97 -0
- package/dist/ci-wait/mcp.js +35 -0
- package/dist/ci-wait/mcp.js.map +1 -0
- package/dist/ci-wait/resources.d.ts +7 -0
- package/dist/ci-wait/resources.js +32 -0
- package/dist/ci-wait/resources.js.map +1 -0
- package/dist/ci-wait/supervisor.d.ts +21 -0
- package/dist/ci-wait/supervisor.js +163 -0
- package/dist/ci-wait/supervisor.js.map +1 -0
- package/dist/contract/ci-wait.d.ts +151 -0
- package/dist/contract/conversations.d.ts +7 -0
- package/dist/contract/delegation.d.ts +2 -0
- package/dist/contract/events.d.ts +3 -0
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.js +1016 -935
- package/dist/contract/runs.d.ts +461 -0
- package/dist/contract/skills.d.ts +92 -0
- package/dist/core/agent-env.js +2 -0
- package/dist/core/agent-env.js.map +1 -1
- package/dist/core/agent-runner.d.ts +6 -0
- package/dist/core/agent-runner.js +1 -0
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/claude-cli-runner.js +10 -0
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/codex-app-server-runner.js +5 -1
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/codex-app-server-transport.js +3 -1
- package/dist/core/codex-app-server-transport.js.map +1 -1
- package/dist/core/cursor-acp-runner.js +9 -1
- package/dist/core/cursor-acp-runner.js.map +1 -1
- package/dist/core/opencode-server-runner.js +6 -2
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/pi-runner.js +7 -1
- package/dist/core/pi-runner.js.map +1 -1
- package/dist/delegation/cli.js +8 -4
- package/dist/delegation/cli.js.map +1 -1
- package/dist/delegation/conversations.js +2 -1
- package/dist/delegation/conversations.js.map +1 -1
- package/dist/delegation/input.d.ts +6 -0
- package/dist/delegation/input.js +22 -0
- package/dist/delegation/input.js.map +1 -1
- package/dist/delegation/provision.js +3 -1
- package/dist/delegation/provision.js.map +1 -1
- package/dist/delegation/routes.d.ts +11 -0
- package/dist/delegation/service.js +31 -7
- package/dist/delegation/service.js.map +1 -1
- package/dist/delegation/transport.d.ts +11 -0
- package/dist/runs/delegation-state.d.ts +2 -0
- package/dist/runs/run-index.js +2 -3
- package/dist/runs/run-index.js.map +1 -1
- package/dist/runs/store.d.ts +514 -3
- package/dist/runs/store.js +85 -6
- package/dist/runs/store.js.map +1 -1
- package/dist/server/server.d.ts +1376 -12
- package/dist/workflows/run.d.ts +25 -1
- package/dist/workflows/run.js +470 -43
- package/dist/workflows/run.js.map +1 -1
- package/package.json +4 -3
- package/scripts/mock-ci-tool.mjs +53 -0
- package/scripts/mock-claude.mjs +8 -0
- package/scripts/mock-codex-app-server.mjs +212 -0
- package/scripts/mock-cursor-acp.mjs +6 -2
- package/scripts/mock-opencode-serve.mjs +392 -0
- package/scripts/mock-pi-rpc.mjs +7 -0
- package/scripts/pi-ci-wait.mjs +14 -0
- package/web/dist/assets/{agent-accounts-lP_xK83o.js → agent-accounts-BZm_2WAv.js} +1 -1
- package/web/dist/assets/{alert-dialog-CmDuefYh.js → alert-dialog-B422RI-k.js} +1 -1
- package/web/dist/assets/{collapsible-BqX8REmY.js → collapsible-Cy_Fmkrd.js} +1 -1
- package/web/dist/assets/{commit-list-BWvv1H7A.js → commit-list-CLxXMZvP.js} +1 -1
- package/web/dist/assets/{compare-variants-Bg7eLW3z.js → compare-variants-D2dRj6H_.js} +1 -1
- package/web/dist/assets/{diff-controls-HwWHHait.js → diff-controls-BGrXGi7O.js} +1 -1
- package/web/dist/assets/{diff-stat-mNkhAc5m.js → diff-stat-Cgnl_hYF.js} +1 -1
- package/web/dist/assets/{diff-0GBum40Q2.js → diff-tRE59UNb2.js} +2 -2
- package/web/dist/assets/{diff-view-DW64doDY.js → diff-view-DN1ort7Q.js} +1 -1
- package/web/dist/assets/{dropdown-menu-sZjYPcLh.js → dropdown-menu-dwDGT8MM.js} +1 -1
- package/web/dist/assets/{git-toolbar-DBW5EIcY.js → git-toolbar-UiHnGvE0.js} +1 -1
- package/web/dist/assets/{github-CkZ4hWI5.js → github-CaGjGQnW.js} +1 -1
- package/web/dist/assets/{image-preview-DZicGNkU.js → image-preview-C2JGcBlC.js} +1 -1
- package/web/dist/assets/index-CXZukB1O.css +1 -0
- package/web/dist/assets/{index-DWWI-B9n.js → index-D0RwD9-c.js} +3 -3
- package/web/dist/assets/{markdown-AY2K3kIV.js → markdown-C_IcKVQd.js} +1 -1
- package/web/dist/assets/{pill-CcJqA1cL.js → pill-BsbO6G7y.js} +1 -1
- package/web/dist/assets/{project-router-SwsXRKXs.js → project-router-DbLkll25.js} +1 -1
- package/web/dist/assets/{prompt-templates-iV2WD2jy.js → prompt-templates-BlTGJB5W.js} +1 -1
- package/web/dist/assets/{repo-git-BW3TOs3U.js → repo-git-CWD2dO8S.js} +1 -1
- package/web/dist/assets/{run-diff-DPUNj56q.js → run-diff-CyG0Nciv.js} +1 -1
- package/web/dist/assets/{run-header-CSaJIGXl.css → run-header-BJQfFL2l.css} +1 -1
- package/web/dist/assets/run-header-IFyIGCey.js +1 -0
- package/web/dist/assets/{skills-Dv8Vwv5D.js → skills-e7wHnuoc.js} +1 -1
- package/web/dist/assets/{tab-link-DSftvkZS.js → tab-link-BWNp3mLY.js} +1 -1
- package/web/dist/assets/{task-changes-CTCuVls1.js → task-changes-CpJImtq8.js} +1 -1
- package/web/dist/assets/{task-commits-weJQ5GSl.js → task-commits-hujrykPI.js} +1 -1
- package/web/dist/assets/{task-files-B4bUimY8.js → task-files-CjTiGtjt.js} +1 -1
- package/web/dist/assets/{task-thread-BuXrVB9K.js → task-thread-DDPFDS8C.js} +4 -4
- package/web/dist/assets/task-thread-Dc3wplva.css +1 -0
- package/web/dist/assets/{textarea-CxRdTWlq.js → textarea-FjaU370i.js} +1 -1
- package/web/dist/assets/thread-loading-77sJ8Yu_.js +1 -0
- package/web/dist/assets/{token-metrics-Z3UFYbuD.js → token-metrics-Clxh03bE.js} +1 -1
- package/web/dist/assets/utils-PYYcpCFZ.js +64 -0
- package/web/dist/assets/{workflows-vA27Yi8W.js → workflows-DGUQmWy7.js} +1 -1
- package/web/dist/assets/{zoomable-image-DUE1KFJ0.js → zoomable-image-Z3JDnm0b.js} +1 -1
- package/web/dist/index.html +14 -14
- package/web/dist/assets/index-DK0Mkv2m.css +0 -1
- package/web/dist/assets/run-header-DbDluWoV.js +0 -1
- package/web/dist/assets/task-thread-DMRD0IVt.css +0 -1
- package/web/dist/assets/thread-loading-JlP6dUpX.js +0 -1
- package/web/dist/assets/utils-CUnaAifW.js +0 -64
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ciPrIdentitySchema, ciWaitRequestSchema } from '../contract/index.js';
|
|
4
|
+
export const CI_QUERY_TIMEOUT_MS = 10_000;
|
|
5
|
+
export const CI_QUERY_LIMIT = 4;
|
|
6
|
+
export const CI_OUTPUT_LIMIT = 64 * 1024;
|
|
7
|
+
export const CI_TERMINATE_MS = 2_000;
|
|
8
|
+
export class CiGithubError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
transient;
|
|
11
|
+
constructor(code, message, transient = false) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.transient = transient;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** FIFO permits; aborted queued requests never consume a slot. */
|
|
18
|
+
export class CiSemaphore {
|
|
19
|
+
limit;
|
|
20
|
+
active = 0;
|
|
21
|
+
queue = [];
|
|
22
|
+
constructor(limit) {
|
|
23
|
+
this.limit = limit;
|
|
24
|
+
}
|
|
25
|
+
async acquire(signal) {
|
|
26
|
+
signal.throwIfAborted();
|
|
27
|
+
if (this.active >= this.limit)
|
|
28
|
+
await new Promise((resolve, reject) => {
|
|
29
|
+
const ready = () => { signal.removeEventListener('abort', abort); resolve(); };
|
|
30
|
+
const abort = () => { const i = this.queue.indexOf(ready); if (i >= 0)
|
|
31
|
+
this.queue.splice(i, 1); reject(signal.reason); };
|
|
32
|
+
this.queue.push(ready);
|
|
33
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
34
|
+
});
|
|
35
|
+
else
|
|
36
|
+
this.active++;
|
|
37
|
+
// A slot is transferred to the oldest waiter, not released and raced for.
|
|
38
|
+
if (signal.aborted) {
|
|
39
|
+
this.release();
|
|
40
|
+
signal.throwIfAborted();
|
|
41
|
+
}
|
|
42
|
+
let released = false;
|
|
43
|
+
return () => { if (!released) {
|
|
44
|
+
released = true;
|
|
45
|
+
this.release();
|
|
46
|
+
} };
|
|
47
|
+
}
|
|
48
|
+
release() { const next = this.queue.shift(); if (next)
|
|
49
|
+
next();
|
|
50
|
+
else
|
|
51
|
+
this.active--; }
|
|
52
|
+
}
|
|
53
|
+
// The helper owns gh's process group and observes the controller-owned stdin pipe.
|
|
54
|
+
// EOF (including controller SIGKILL) initiates termination without trusting persisted PIDs.
|
|
55
|
+
// Keep this inline so the installed JS artifact carries the helper without another asset.
|
|
56
|
+
export const CI_PROCESS_WRAPPER = String.raw `
|
|
57
|
+
const { spawn } = require('node:child_process');
|
|
58
|
+
const command = JSON.parse(process.argv[1]);
|
|
59
|
+
const child = spawn(command.file, command.args, { detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
60
|
+
let stopping = false, escalation, spawnFailed = false;
|
|
61
|
+
function kill(force) {
|
|
62
|
+
if (!child.pid) return;
|
|
63
|
+
if (process.platform === 'win32') {
|
|
64
|
+
const args = ['/PID', String(child.pid), '/T']; if (force) args.push('/F');
|
|
65
|
+
spawn('taskkill', args, {stdio:'ignore',windowsHide:true}).on('error',()=>{});
|
|
66
|
+
} else { try { process.kill(-child.pid, force ? 'SIGKILL' : 'SIGTERM'); } catch {} }
|
|
67
|
+
}
|
|
68
|
+
function stop() {
|
|
69
|
+
if (stopping) return; stopping = true; kill(false);
|
|
70
|
+
escalation = setTimeout(() => kill(true), ${CI_TERMINATE_MS});
|
|
71
|
+
}
|
|
72
|
+
process.stdin.resume(); process.stdin.on('end', stop); process.on('SIGTERM', stop); process.on('SIGINT', stop);
|
|
73
|
+
process.stdout.on('error', stop); process.stderr.on('error', stop);
|
|
74
|
+
child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
|
|
75
|
+
child.on('error', () => { spawnFailed = true; });
|
|
76
|
+
// A grandchild can retain the leader's stdout after exit; close alone would wait forever.
|
|
77
|
+
child.on('exit', () => kill(true));
|
|
78
|
+
child.on('close', code => { clearTimeout(escalation); process.exit(spawnFailed ? 127 : code === null ? 143 : code); });
|
|
79
|
+
`;
|
|
80
|
+
const dryRunCommand = String.raw `
|
|
81
|
+
const args = process.argv.slice(1);
|
|
82
|
+
if (args[1] === 'view') {
|
|
83
|
+
const url = args[2].startsWith('https:') ? args[2] : 'https://' + args[args.indexOf('--repo')+1] + '/pull/' + args[2];
|
|
84
|
+
console.log(JSON.stringify({url, number:Number(url.split('/').pop()), headRefOid:'a'.repeat(40)}));
|
|
85
|
+
} else if (!args.includes('--watch')) console.log(JSON.stringify([{name:'Dry-run checks',state:'SUCCESS',bucket:'pass',link:''}]));
|
|
86
|
+
`;
|
|
87
|
+
/** Environment stays inherited, matching the existing forge gh resolver (including GH_TOKEN/GITHUB_TOKEN). */
|
|
88
|
+
export async function runCiCommand(command, signal, discardOutput = false, env = process.env, cwd) {
|
|
89
|
+
signal.throwIfAborted();
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const child = spawn(process.execPath, ['-e', CI_PROCESS_WRAPPER, JSON.stringify(command)], { env, cwd, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
92
|
+
let stdout = '', stderr = '', bytes = 0;
|
|
93
|
+
let failure;
|
|
94
|
+
const abort = () => { failure ??= signal.reason; child.stdin.end(); };
|
|
95
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
96
|
+
// Closing stdin asks the wrapper to terminate and await the owned process tree.
|
|
97
|
+
const capture = (chunk, error) => {
|
|
98
|
+
if (!error && discardOutput)
|
|
99
|
+
return;
|
|
100
|
+
bytes += chunk.length;
|
|
101
|
+
if (bytes > CI_OUTPUT_LIMIT) {
|
|
102
|
+
failure ??= new CiGithubError('output_limit', 'GitHub output exceeded 64 KiB; narrow the PR check set and retry.');
|
|
103
|
+
child.stdin.end();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (error)
|
|
107
|
+
stderr += chunk.toString();
|
|
108
|
+
else
|
|
109
|
+
stdout += chunk.toString();
|
|
110
|
+
};
|
|
111
|
+
child.stdout.on('data', (chunk) => capture(chunk, false));
|
|
112
|
+
child.stderr.on('data', (chunk) => capture(chunk, true));
|
|
113
|
+
child.stdin.on('error', () => { });
|
|
114
|
+
child.on('error', () => { failure = new CiGithubError('gh_missing', 'Unable to launch GitHub CLI; install gh and retry.'); });
|
|
115
|
+
child.on('close', code => {
|
|
116
|
+
signal.removeEventListener('abort', abort);
|
|
117
|
+
if (failure)
|
|
118
|
+
reject(failure);
|
|
119
|
+
else
|
|
120
|
+
resolve({ code: code ?? 143, stdout, stderr });
|
|
121
|
+
});
|
|
122
|
+
if (signal.aborted)
|
|
123
|
+
abort();
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const metadataSchema = z.object({ url: z.string(), number: z.number().int().positive(), headRefOid: z.string().regex(/^[0-9a-f]{40}$/i) });
|
|
127
|
+
export const githubChecksSchema = z.array(z.object({ name: z.string(), state: z.string(), link: z.string(), bucket: z.enum(['pass', 'fail', 'pending', 'skipping', 'cancel']) }));
|
|
128
|
+
function parseJson(text, schema) {
|
|
129
|
+
try {
|
|
130
|
+
return schema.parse(JSON.parse(text));
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new CiGithubError('malformed_data', 'GitHub returned malformed structured data; update gh and retry.');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function commandError(result) {
|
|
137
|
+
// Only classify stderr: never forward potentially credential-bearing external diagnostics.
|
|
138
|
+
if (result.code === 127)
|
|
139
|
+
return new CiGithubError('gh_missing', 'GitHub CLI is unavailable; install gh and retry.');
|
|
140
|
+
if (/401|403|auth|log.?in|token/i.test(result.stderr))
|
|
141
|
+
return new CiGithubError('authentication', 'GitHub authentication failed; run gh auth login with access to this repository.');
|
|
142
|
+
if (/404|not found|could not resolve|no pull requests/i.test(result.stderr))
|
|
143
|
+
return new CiGithubError('inaccessible_pr', 'PR is inaccessible; verify its URL and repository access.');
|
|
144
|
+
return new CiGithubError('command_failed', 'GitHub query failed; check connectivity and GitHub availability, then retry.', /5\d\d|timeout|timed out|network|connection|rate limit|429|temporar/i.test(result.stderr));
|
|
145
|
+
}
|
|
146
|
+
export class GithubCiClient {
|
|
147
|
+
queries = new CiSemaphore(CI_QUERY_LIMIT);
|
|
148
|
+
command;
|
|
149
|
+
env;
|
|
150
|
+
cwd;
|
|
151
|
+
constructor(options = {}) {
|
|
152
|
+
this.env = options.env ?? process.env;
|
|
153
|
+
this.command = options.command ?? (this.env.CEZ_DRY_RUN === '1' ? { file: process.execPath, args: ['-e', dryRunCommand] } : { file: 'gh', args: [] });
|
|
154
|
+
this.cwd = options.cwd;
|
|
155
|
+
}
|
|
156
|
+
async query(args, signal) {
|
|
157
|
+
const bounded = AbortSignal.any([signal, AbortSignal.timeout(CI_QUERY_TIMEOUT_MS)]);
|
|
158
|
+
let release;
|
|
159
|
+
try {
|
|
160
|
+
release = await this.queries.acquire(bounded);
|
|
161
|
+
return await runCiCommand({ file: this.command.file, args: [...this.command.args, ...args] }, bounded, false, this.env, this.cwd);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (!signal.aborted && bounded.aborted)
|
|
165
|
+
throw new CiGithubError('query_timeout', 'GitHub query exceeded ten seconds; check connectivity and retry.', true);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
release?.();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async resolve(pr, signal) {
|
|
173
|
+
if (!ciWaitRequestSchema.safeParse({ pr }).success)
|
|
174
|
+
throw new CiGithubError('invalid_request', 'Provide an HTTPS GitHub pull request URL.');
|
|
175
|
+
const supplied = new URL(pr);
|
|
176
|
+
if (supplied.hostname !== 'github.com')
|
|
177
|
+
throw new CiGithubError('unsupported_host', 'This GitHub Enterprise host is not recognized by the forge configuration; use github.com or configure supported forge authentication.');
|
|
178
|
+
const response = await this.query(['pr', 'view', pr, '--json', 'url,number,headRefOid'], signal);
|
|
179
|
+
if (response.code !== 0)
|
|
180
|
+
throw commandError(response);
|
|
181
|
+
const metadata = parseJson(response.stdout, metadataSchema);
|
|
182
|
+
const validUrl = ciWaitRequestSchema.safeParse({ pr: metadata.url });
|
|
183
|
+
if (!validUrl.success)
|
|
184
|
+
throw new CiGithubError('malformed_data', 'GitHub returned an invalid canonical PR URL.');
|
|
185
|
+
const url = new URL(metadata.url);
|
|
186
|
+
const parts = url.pathname.split('/');
|
|
187
|
+
if (url.hostname !== supplied.hostname || Number(parts[4]) !== metadata.number || metadata.number !== Number(supplied.pathname.split('/')[4]))
|
|
188
|
+
throw new CiGithubError('malformed_data', 'GitHub returned inconsistent PR identity.');
|
|
189
|
+
return ciPrIdentitySchema.parse({ prUrl: metadata.url, repository: `${parts[1]}/${parts[2]}`, prNumber: metadata.number, headSha: metadata.headRefOid });
|
|
190
|
+
}
|
|
191
|
+
async head(pr, signal) { return (await this.resolve(pr.prUrl, signal)).headSha; }
|
|
192
|
+
async checks(pr, signal) {
|
|
193
|
+
const result = await this.query(['pr', 'checks', String(pr.prNumber), '--repo', `github.com/${pr.repository}`, '--json', 'name,state,link,bucket'], signal);
|
|
194
|
+
// gh documents 1 for failed checks and 8 for pending. Only validated JSON decides CI outcome.
|
|
195
|
+
if (![0, 1, 8].includes(result.code) || !result.stdout.trim()) {
|
|
196
|
+
// gh's empty-check path emits no JSON; pin the exact CLI diagnostic, never a generic exit 1.
|
|
197
|
+
if (result.code === 1 && /^no checks reported on the ['"]?.+['"]? branch\s*$/i.test(result.stderr.trim()))
|
|
198
|
+
return [];
|
|
199
|
+
throw commandError(result);
|
|
200
|
+
}
|
|
201
|
+
return parseJson(result.stdout, githubChecksSchema);
|
|
202
|
+
}
|
|
203
|
+
async watch(pr, signal) {
|
|
204
|
+
const result = await runCiCommand({ file: this.command.file, args: [...this.command.args, 'pr', 'checks', String(pr.prNumber), '--repo', `github.com/${pr.repository}`, '--watch', '--interval', '10'] }, signal, true, this.env, this.cwd);
|
|
205
|
+
if (![0, 1, 8].includes(result.code) || result.stderr.trim())
|
|
206
|
+
throw commandError(result);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/ci-wait/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAA2C,MAAM,8BAA8B,CAAC;AAEhI,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAChC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAErC,MAAM,OAAO,aAAc,SAAQ,KAAK;IACjB,IAAI;IAA6C,SAAS;IAA/E,YAAqB,IAAqB,EAAE,OAAe,EAAW,SAAS,GAAG,KAAK;QAAI,KAAK,CAAC,OAAO,CAAC,CAAC;oBAArF,IAAI;yBAA6C,SAAS;IAA4B,CAAC;CAC7G;AAED,kEAAkE;AAClE,MAAM,OAAO,WAAW;IAGO,KAAK;IAF1B,MAAM,GAAG,CAAC,CAAC;IACF,KAAK,GAAsB,EAAE,CAAC;IAC/C,YAA6B,KAAa;qBAAb,KAAK;IAAW,CAAC;IAC9C,KAAK,CAAC,OAAO,CAAC,MAAmB;QAC/B,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACzE,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC/E,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACzH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAClF,CAAC,CAAC,CAAC;;YACE,IAAI,CAAC,MAAM,EAAE,CAAC;QACnB,0EAA0E;QAC1E,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAAC,CAAC;QAChE,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAAC,QAAQ,GAAG,IAAI,CAAC;YAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IACO,OAAO,KAAW,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,IAAI;QAAE,IAAI,EAAE,CAAC;;QAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CACnG;AAED,mFAAmF;AACnF,4FAA4F;AAC5F,0FAA0F;AAC1F,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;6CAcC,eAAe;;;;;;;;;CAS3D,CAAC;AACF,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;CAM/B,CAAC;AAIF,8GAA8G;AAC9G,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAyB,EAAE,MAAmB,EAAE,aAAa,GAAG,KAAK,EAAE,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAE,GAAY;IAC1J,MAAM,CAAC,cAAc,EAAE,CAAC;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7J,IAAI,MAAM,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,CAAC,CAAC;QACxC,IAAI,OAAgB,CAAC;QACrB,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,gFAAgF;QAChF,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,KAAc,EAAE,EAAE;YAChD,IAAI,CAAC,KAAK,IAAI,aAAa;gBAAE,OAAO;YACpC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,KAAK,GAAG,eAAe,EAAE,CAAC;gBAAC,OAAO,KAAK,IAAI,aAAa,CAAC,cAAc,EAAE,mEAAmE,CAAC,CAAC;gBAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YAC/K,IAAI,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;;gBAAM,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACzE,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAClE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACjE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,GAAG,IAAI,aAAa,CAAC,YAAY,EAAE,oDAAoD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9H,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE;YACvB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC3C,IAAI,OAAO;gBAAE,MAAM,CAAC,OAAO,CAAC,CAAC;;gBACxB,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,OAAO;YAAE,KAAK,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;AAC3I,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAElL,SAAS,SAAS,CAAI,IAAY,EAAE,MAAoB;IACtD,IAAI,CAAC;QAAC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,MAAM,IAAI,aAAa,CAAC,gBAAgB,EAAE,iEAAiE,CAAC,CAAC;IAAC,CAAC;AACxK,CAAC;AACD,SAAS,YAAY,CAAC,MAAuB;IAC3C,2FAA2F;IAC3F,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG;QAAE,OAAO,IAAI,aAAa,CAAC,YAAY,EAAE,kDAAkD,CAAC,CAAC;IACpH,IAAI,6BAA6B,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,aAAa,CAAC,gBAAgB,EAAE,iFAAiF,CAAC,CAAC;IACrL,IAAI,mDAAmD,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,aAAa,CAAC,iBAAiB,EAAE,2DAA2D,CAAC,CAAC;IACtL,OAAO,IAAI,aAAa,CAAC,gBAAgB,EAAE,8EAA8E,EAAE,qEAAqE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AACxN,CAAC;AACD,MAAM,OAAO,cAAc;IACR,OAAO,GAAG,IAAI,WAAW,CAAC,cAAc,CAAC,CAAC;IAC1C,OAAO,CAAmB;IAC1B,GAAG,CAAoB;IACvB,GAAG,CAAU;IAC9B,YAAY,OAAO,GAA0E,EAAE;QAC7F,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACtJ,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACzB,CAAC;IACO,KAAK,CAAC,KAAK,CAAC,IAAc,EAAE,MAAmB;QACrD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACpF,IAAI,OAAiC,CAAC;QACtC,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC9C,OAAO,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QACtI,OAAO,KAAK,EAAE,CAAC;YAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO;gBAAE,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,kEAAkE,EAAE,IAAI,CAAC,CAAC;YAAC,MAAM,KAAK,CAAC;QAAC,CAAC;gBAClL,CAAC;YAAC,OAAO,EAAE,EAAE,CAAC;QAAC,CAAC;IAC1B,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,MAAmB;QAC3C,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAC,EAAE,EAAC,CAAC,CAAC,OAAO;YAAE,MAAM,IAAI,aAAa,CAAC,iBAAiB,EAAE,2CAA2C,CAAC,CAAC;QAC1I,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7B,IAAI,QAAQ,CAAC,QAAQ,KAAK,YAAY;YAAE,MAAM,IAAI,aAAa,CAAC,kBAAkB,EAAE,uIAAuI,CAAC,CAAC;QAC7N,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,uBAAuB,CAAC,EAAE,MAAM,CAAC,CAAC;QACjG,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC;YAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,EAAC,EAAE,EAAC,QAAQ,CAAC,GAAG,EAAC,CAAC,CAAC;QAClE,IAAI,CAAC,QAAQ,CAAC,OAAO;YAAE,MAAM,IAAI,aAAa,CAAC,gBAAgB,EAAE,8CAA8C,CAAC,CAAC;QACjH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,aAAa,CAAC,gBAAgB,EAAE,2CAA2C,CAAC,CAAC;QACtO,OAAO,kBAAkB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3J,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAgB,EAAE,MAAmB,IAAqB,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7H,KAAK,CAAC,MAAM,CAAC,EAAgB,EAAE,MAAmB;QAChD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,wBAAwB,CAAC,EAAE,MAAM,CAAC,CAAC;QAC5J,8FAA8F;QAC9F,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9D,6FAA6F;YAC7F,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,qDAAqD,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAAE,OAAO,EAAE,CAAC;YACrH,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACtD,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,EAAgB,EAAE,MAAmB;QAC/C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAC,IAAI,EAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAC,IAAI,EAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,EAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACvO,IAAI,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YAAE,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACzF,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { parseConfigContent } from '../agent-config/model-settings/shared.js';
|
|
2
|
+
/** Runtime config has highest precedence; merge only our collision-resistant server. */
|
|
3
|
+
export function ciOpenCodeEnv(spec) {
|
|
4
|
+
if (!spec.cezarTools)
|
|
5
|
+
return spec.env;
|
|
6
|
+
const supplied = spec.env?.OPENCODE_CONFIG_CONTENT ?? process.env.OPENCODE_CONFIG_CONTENT;
|
|
7
|
+
let config = {};
|
|
8
|
+
if (supplied) {
|
|
9
|
+
try {
|
|
10
|
+
const parsed = parseConfigContent(supplied, 'jsonc');
|
|
11
|
+
if (!isObject(parsed) || (parsed.mcp !== undefined && !isObject(parsed.mcp)))
|
|
12
|
+
throw new Error();
|
|
13
|
+
config = parsed;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error('CI tool unavailable: invalid supplied OpenCode runtime configuration');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const { name, command, args } = spec.cezarTools;
|
|
20
|
+
const existing = isObject(config.mcp) ? config.mcp : {};
|
|
21
|
+
if (Object.hasOwn(existing, name))
|
|
22
|
+
throw new Error('CI tool unavailable: OpenCode server name collision');
|
|
23
|
+
return { ...spec.env, OPENCODE_CONFIG_CONTENT: JSON.stringify({ ...config, mcp: { ...existing, [name]: { type: 'local', command: [command, ...args], enabled: true } } }) };
|
|
24
|
+
}
|
|
25
|
+
function isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); }
|
|
26
|
+
//# sourceMappingURL=injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection.js","sourceRoot":"","sources":["../../src/ci-wait/injection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAG9E,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,IAAkB;IAC9C,IAAI,CAAC,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,uBAAuB,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IAC1F,IAAI,MAAM,GAA4B,EAAE,CAAC;IACzC,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,MAAM,GAAY,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC9D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAAE,MAAM,IAAI,KAAK,EAAE,CAAC;YAChG,MAAM,GAAG,MAAM,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YAAC,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAAC,CAAC;IACtG,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;IAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC1G,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,uBAAuB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AAC9K,CAAC;AACD,SAAS,QAAQ,CAAC,KAAc,IAAsC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ciToolDefinition: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
$schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#" | "http://json-schema.org/draft-04/schema#";
|
|
7
|
+
$id?: string;
|
|
8
|
+
$anchor?: string;
|
|
9
|
+
$ref?: string;
|
|
10
|
+
$dynamicRef?: string;
|
|
11
|
+
$dynamicAnchor?: string;
|
|
12
|
+
$vocabulary?: Record<string, boolean>;
|
|
13
|
+
$comment?: string;
|
|
14
|
+
$defs?: Record<string, z.core.JSONSchema.JSONSchema>;
|
|
15
|
+
additionalItems?: z.core.JSONSchema._JSONSchema;
|
|
16
|
+
unevaluatedItems?: z.core.JSONSchema._JSONSchema;
|
|
17
|
+
prefixItems?: z.core.JSONSchema._JSONSchema[];
|
|
18
|
+
items?: z.core.JSONSchema._JSONSchema | z.core.JSONSchema._JSONSchema[];
|
|
19
|
+
contains?: z.core.JSONSchema._JSONSchema;
|
|
20
|
+
additionalProperties?: z.core.JSONSchema._JSONSchema;
|
|
21
|
+
unevaluatedProperties?: z.core.JSONSchema._JSONSchema;
|
|
22
|
+
properties?: Record<string, z.core.JSONSchema._JSONSchema>;
|
|
23
|
+
patternProperties?: Record<string, z.core.JSONSchema._JSONSchema>;
|
|
24
|
+
dependentSchemas?: Record<string, z.core.JSONSchema._JSONSchema>;
|
|
25
|
+
propertyNames?: z.core.JSONSchema._JSONSchema;
|
|
26
|
+
if?: z.core.JSONSchema._JSONSchema;
|
|
27
|
+
then?: z.core.JSONSchema._JSONSchema;
|
|
28
|
+
else?: z.core.JSONSchema._JSONSchema;
|
|
29
|
+
allOf?: z.core.JSONSchema.JSONSchema[];
|
|
30
|
+
anyOf?: z.core.JSONSchema.JSONSchema[];
|
|
31
|
+
oneOf?: z.core.JSONSchema.JSONSchema[];
|
|
32
|
+
not?: z.core.JSONSchema._JSONSchema;
|
|
33
|
+
multipleOf?: number;
|
|
34
|
+
maximum?: number;
|
|
35
|
+
exclusiveMaximum?: number | boolean;
|
|
36
|
+
minimum?: number;
|
|
37
|
+
exclusiveMinimum?: number | boolean;
|
|
38
|
+
maxLength?: number;
|
|
39
|
+
minLength?: number;
|
|
40
|
+
pattern?: string;
|
|
41
|
+
maxItems?: number;
|
|
42
|
+
minItems?: number;
|
|
43
|
+
uniqueItems?: boolean;
|
|
44
|
+
maxContains?: number;
|
|
45
|
+
minContains?: number;
|
|
46
|
+
maxProperties?: number;
|
|
47
|
+
minProperties?: number;
|
|
48
|
+
required?: string[];
|
|
49
|
+
dependentRequired?: Record<string, string[]>;
|
|
50
|
+
enum?: Array<string | number | boolean | null>;
|
|
51
|
+
const?: string | number | boolean | null;
|
|
52
|
+
id?: string;
|
|
53
|
+
title?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
default?: unknown;
|
|
56
|
+
deprecated?: boolean;
|
|
57
|
+
readOnly?: boolean;
|
|
58
|
+
writeOnly?: boolean;
|
|
59
|
+
nullable?: boolean;
|
|
60
|
+
examples?: unknown[];
|
|
61
|
+
format?: string;
|
|
62
|
+
contentMediaType?: string;
|
|
63
|
+
contentEncoding?: string;
|
|
64
|
+
contentSchema?: z.core.JSONSchema.JSONSchema;
|
|
65
|
+
_prefault?: unknown;
|
|
66
|
+
"~standard": z.core.ZodStandardSchemaWithJSON<z.ZodObject<{
|
|
67
|
+
pr: z.ZodString;
|
|
68
|
+
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
69
|
+
}, z.core.$strict>>;
|
|
70
|
+
type: 'object';
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
export declare function invokeCiTool(input: unknown): Promise<{
|
|
74
|
+
content: {
|
|
75
|
+
type: 'text';
|
|
76
|
+
text: string;
|
|
77
|
+
}[];
|
|
78
|
+
details: {
|
|
79
|
+
prUrl: string;
|
|
80
|
+
repository: string;
|
|
81
|
+
prNumber: number;
|
|
82
|
+
headSha: string;
|
|
83
|
+
waitId: string;
|
|
84
|
+
registeredAt: string;
|
|
85
|
+
deadline: string;
|
|
86
|
+
phase: "delivered" | "parked" | "registered" | "wake-pending" | "withdrawn";
|
|
87
|
+
};
|
|
88
|
+
isError?: undefined;
|
|
89
|
+
} | {
|
|
90
|
+
isError: boolean;
|
|
91
|
+
content: {
|
|
92
|
+
type: 'text';
|
|
93
|
+
text: string;
|
|
94
|
+
}[];
|
|
95
|
+
details: {};
|
|
96
|
+
}>;
|
|
97
|
+
export declare function serveCiMcp(): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { ciWaitRequestSchema } from '../contract/index.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { callCiWait, monitorCiOwner } from './client.js';
|
|
8
|
+
export const ciToolDefinition = {
|
|
9
|
+
name: 'cezar_wait_for_ci',
|
|
10
|
+
description: 'Register a bounded CI wait for a GitHub pull request. End your turn after registration; Cezar resumes you with an observation. No marker is needed. Passing reported checks does not prove all expected workflows appeared and is not merge approval.',
|
|
11
|
+
inputSchema: { ...z.toJSONSchema(ciWaitRequestSchema, { io: 'input' }), type: 'object' },
|
|
12
|
+
};
|
|
13
|
+
export async function invokeCiTool(input) {
|
|
14
|
+
try {
|
|
15
|
+
const receipt = await callCiWait(input);
|
|
16
|
+
return { content: [{ type: 'text', text: `${JSON.stringify(receipt)}\nRegistered. Please end your turn to wait; no marker is needed. This observation is not merge approval.` }], details: receipt };
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return { isError: true, content: [{ type: 'text', text: error instanceof Error ? error.message : 'CI tool unavailable' }], details: {} };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function serveCiMcp() {
|
|
23
|
+
const server = new Server({ name: 'cezar-ci', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
24
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ciToolDefinition] }));
|
|
25
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => request.params.name === ciToolDefinition.name ? invokeCiTool(request.params.arguments) : { isError: true, content: [{ type: 'text', text: 'Unknown tool' }] });
|
|
26
|
+
const finish = () => { void server.close().finally(() => process.exit(0)); };
|
|
27
|
+
const stop = monitorCiOwner(finish);
|
|
28
|
+
process.stdin.once('end', finish);
|
|
29
|
+
server.onclose = () => { stop(); process.stdin.removeListener('end', finish); };
|
|
30
|
+
await server.connect(new StdioServerTransport());
|
|
31
|
+
}
|
|
32
|
+
// The module also supplies Pi with the same contract/renderer; importing it must not start stdio.
|
|
33
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
34
|
+
await serveCiMcp();
|
|
35
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/ci-wait/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CiToolController } from './controller.ts';
|
|
2
|
+
import { CiWatcherSupervisor } from './supervisor.ts';
|
|
3
|
+
export declare function acquireCiResources(workspace: object): {
|
|
4
|
+
supervisor: CiWatcherSupervisor;
|
|
5
|
+
controller: () => Promise<CiToolController | undefined>;
|
|
6
|
+
release(): void;
|
|
7
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CiToolController } from './controller.js';
|
|
2
|
+
import { CiWatcherSupervisor } from './supervisor.js';
|
|
3
|
+
/** The workspace semaphore is shared by cockpit projects; headless callers have one too. */
|
|
4
|
+
const workspaces = new WeakMap();
|
|
5
|
+
export function acquireCiResources(workspace) {
|
|
6
|
+
let resource = workspaces.get(workspace);
|
|
7
|
+
if (!resource) {
|
|
8
|
+
resource = { refs: 0, supervisor: new CiWatcherSupervisor() };
|
|
9
|
+
workspaces.set(workspace, resource);
|
|
10
|
+
}
|
|
11
|
+
const shared = resource;
|
|
12
|
+
shared.refs++;
|
|
13
|
+
let released = false;
|
|
14
|
+
return {
|
|
15
|
+
supervisor: shared.supervisor,
|
|
16
|
+
controller: () => shared.controller ??= CiToolController.start().catch(() => {
|
|
17
|
+
console.warn('[cez] CI-wait tool unavailable; ordinary runs remain enabled.');
|
|
18
|
+
return undefined;
|
|
19
|
+
}),
|
|
20
|
+
release() {
|
|
21
|
+
if (released)
|
|
22
|
+
return;
|
|
23
|
+
released = true;
|
|
24
|
+
if (--shared.refs !== 0)
|
|
25
|
+
return;
|
|
26
|
+
workspaces.delete(workspace);
|
|
27
|
+
shared.supervisor.close();
|
|
28
|
+
void shared.controller?.then(controller => controller?.close()).catch(() => { });
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/ci-wait/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,4FAA4F;AAC5F,MAAM,UAAU,GAAG,IAAI,OAAO,EAI1B,CAAC;AAEL,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,IAAI,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,mBAAmB,EAAE,EAAE,CAAC;QAC9D,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC;IACxB,MAAM,CAAC,IAAI,EAAE,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,KAAK,gBAAgB,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YAC1E,OAAO,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;YAC9E,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC;QACF,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO;YAChC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7B,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClF,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CiPrIdentity, type CiWait, type CiWaitResult } from '../contract/index.js';
|
|
2
|
+
import { GithubCiClient } from './github.ts';
|
|
3
|
+
export declare const CI_WATCHER_LIMIT = 4;
|
|
4
|
+
export declare const CI_PROBE_MS = 10000;
|
|
5
|
+
export declare const CI_DISCOVERY_MS = 60000;
|
|
6
|
+
export declare const CI_RETRY_MS: readonly [1000, 5000, 15000];
|
|
7
|
+
export declare const CI_RESULT_LIMIT: number;
|
|
8
|
+
/** One shared instance per workspace; queues watchers FIFO without extending their deadlines. */
|
|
9
|
+
export declare class CiWatcherSupervisor {
|
|
10
|
+
private readonly lifetime;
|
|
11
|
+
private readonly slots;
|
|
12
|
+
private readonly github;
|
|
13
|
+
private readonly current;
|
|
14
|
+
constructor(options?: {
|
|
15
|
+
github?: GithubCiClient;
|
|
16
|
+
});
|
|
17
|
+
resolve(pr: string, signal?: AbortSignal): Promise<CiPrIdentity>;
|
|
18
|
+
watch(wait: CiWait, signal: AbortSignal): Promise<CiWaitResult>;
|
|
19
|
+
close(): void;
|
|
20
|
+
private run;
|
|
21
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
+
import { ciWaitSchema, ciWaitResultSchema } from '../contract/index.js';
|
|
3
|
+
import { CiGithubError, CiSemaphore, GithubCiClient } from './github.js';
|
|
4
|
+
export const CI_WATCHER_LIMIT = 4;
|
|
5
|
+
export const CI_PROBE_MS = 10_000;
|
|
6
|
+
export const CI_DISCOVERY_MS = 60_000;
|
|
7
|
+
export const CI_RETRY_MS = [1_000, 5_000, 15_000];
|
|
8
|
+
export const CI_RESULT_LIMIT = 32 * 1024;
|
|
9
|
+
function clean(value, max) {
|
|
10
|
+
return value.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '').replace(/[\x00-\x1f\x7f-\x9f]/g, '').slice(0, max);
|
|
11
|
+
}
|
|
12
|
+
function safeLink(value) {
|
|
13
|
+
if (value.length > 2048 || /[\x00-\x20\x7f]/.test(value))
|
|
14
|
+
return '';
|
|
15
|
+
try {
|
|
16
|
+
const url = new URL(value);
|
|
17
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash)
|
|
18
|
+
return '';
|
|
19
|
+
return url.href.length <= 2048 ? url.href : '';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function outcome(checks) {
|
|
26
|
+
if (!checks.length || checks.some(c => c.bucket === 'pending'))
|
|
27
|
+
return undefined;
|
|
28
|
+
if (checks.some(c => c.bucket === 'fail'))
|
|
29
|
+
return 'failed';
|
|
30
|
+
if (checks.some(c => c.bucket === 'cancel'))
|
|
31
|
+
return 'cancelled';
|
|
32
|
+
if (checks.some(c => c.bucket === 'skipping'))
|
|
33
|
+
return 'skipped';
|
|
34
|
+
return 'passed';
|
|
35
|
+
}
|
|
36
|
+
function observation(wait, result, rows, extra = {}) {
|
|
37
|
+
const checks = rows.slice(0, 100).map(row => ({ name: clean(row.name, 256), state: clean(row.state, 256), link: safeLink(row.link) }));
|
|
38
|
+
const value = { outcome: result, headSha: wait.headSha, observedAt: new Date().toISOString(), checks, totalChecks: rows.length, truncated: rows.length > checks.length || rows.some((row, i) => i < checks.length && (row.name !== checks[i].name || row.state !== checks[i].state || row.link !== checks[i].link)), ...extra };
|
|
39
|
+
while (Buffer.byteLength(JSON.stringify(value)) > CI_RESULT_LIMIT && checks.length) {
|
|
40
|
+
checks.pop();
|
|
41
|
+
value.truncated = true;
|
|
42
|
+
}
|
|
43
|
+
return ciWaitResultSchema.parse(value);
|
|
44
|
+
}
|
|
45
|
+
/** One shared instance per workspace; queues watchers FIFO without extending their deadlines. */
|
|
46
|
+
export class CiWatcherSupervisor {
|
|
47
|
+
lifetime = new AbortController();
|
|
48
|
+
slots = new CiSemaphore(CI_WATCHER_LIMIT);
|
|
49
|
+
github;
|
|
50
|
+
current = new Map();
|
|
51
|
+
constructor(options = {}) { this.github = options.github ?? new GithubCiClient(); }
|
|
52
|
+
async resolve(pr, signal) {
|
|
53
|
+
const bounded = AbortSignal.any([this.lifetime.signal, ...(signal ? [signal] : []), AbortSignal.timeout(10_000)]);
|
|
54
|
+
try {
|
|
55
|
+
return await this.github.resolve(pr, bounded);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (bounded.aborted && !this.lifetime.signal.aborted && !signal?.aborted)
|
|
59
|
+
throw new CiGithubError('query_timeout', 'PR registration exceeded ten seconds; retry when GitHub is available.');
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
watch(wait, signal) {
|
|
64
|
+
const existing = this.current.get(wait.id);
|
|
65
|
+
if (existing)
|
|
66
|
+
return existing;
|
|
67
|
+
const task = this.run(wait, signal).finally(() => { this.current.delete(wait.id); });
|
|
68
|
+
this.current.set(wait.id, task);
|
|
69
|
+
return task;
|
|
70
|
+
}
|
|
71
|
+
close() { this.lifetime.abort(); }
|
|
72
|
+
async run(wait, caller) {
|
|
73
|
+
const remaining = Date.parse(wait.deadline) - Date.now();
|
|
74
|
+
if (!ciWaitSchema.safeParse(wait).success)
|
|
75
|
+
return observation({ ...wait, headSha: /^[0-9a-f]{40}$/i.test(wait.headSha ?? '') ? wait.headSha : '0'.repeat(40) }, 'error', [], { diagnostic: 'malformed_data: Persisted CI wait is invalid; an all-zero head denotes an unavailable original identity. Register a new wait.' });
|
|
76
|
+
const deadline = AbortSignal.timeout(Math.max(0, Math.min(remaining, 7_200_000)));
|
|
77
|
+
const signal = AbortSignal.any([caller, this.lifetime.signal, deadline]);
|
|
78
|
+
let rows = [];
|
|
79
|
+
let release;
|
|
80
|
+
let retry = 0;
|
|
81
|
+
const discoveryDeadline = Math.min(Date.parse(wait.deadline), Date.now() + CI_DISCOVERY_MS);
|
|
82
|
+
const stopped = () => observation(wait, caller.aborted || this.lifetime.signal.aborted ? 'cancelled' : 'deadline', rows);
|
|
83
|
+
if (remaining <= 0 || signal.aborted)
|
|
84
|
+
return stopped();
|
|
85
|
+
const snapshot = async () => {
|
|
86
|
+
const before = await this.github.head(wait, signal);
|
|
87
|
+
if (before !== wait.headSha)
|
|
88
|
+
return observation(wait, 'head_changed', rows, { observedHeadSha: before });
|
|
89
|
+
const observed = await this.github.checks(wait, signal);
|
|
90
|
+
const after = await this.github.head(wait, signal);
|
|
91
|
+
if (after !== wait.headSha)
|
|
92
|
+
return observation(wait, 'head_changed', rows, { observedHeadSha: after });
|
|
93
|
+
rows = observed;
|
|
94
|
+
const settled = outcome(rows);
|
|
95
|
+
return settled ? observation(wait, settled, rows) : undefined;
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
release = await this.slots.acquire(signal);
|
|
99
|
+
while (!signal.aborted) {
|
|
100
|
+
try {
|
|
101
|
+
const settled = await snapshot();
|
|
102
|
+
if (settled)
|
|
103
|
+
return settled;
|
|
104
|
+
if (!rows.length) {
|
|
105
|
+
if (Date.now() >= discoveryDeadline)
|
|
106
|
+
return observation(wait, 'no_checks', rows);
|
|
107
|
+
await delay(Math.min(CI_PROBE_MS, discoveryDeadline - Date.now()), undefined, { signal });
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const watchAbort = new AbortController();
|
|
111
|
+
const watchSignal = AbortSignal.any([signal, watchAbort.signal]);
|
|
112
|
+
// Attach a rejection handler immediately; the watcher may finish during a head probe.
|
|
113
|
+
const watcher = this.github.watch(wait, watchSignal).then(() => ({ done: true }), error => ({ done: true, error }));
|
|
114
|
+
try {
|
|
115
|
+
while (!signal.aborted) {
|
|
116
|
+
const tick = new AbortController();
|
|
117
|
+
const next = await Promise.race([watcher, delay(CI_PROBE_MS, { done: false }, { signal: AbortSignal.any([signal, tick.signal]) }).catch(() => ({ done: false }))]);
|
|
118
|
+
tick.abort();
|
|
119
|
+
signal.throwIfAborted();
|
|
120
|
+
if (next.done) {
|
|
121
|
+
if ('error' in next)
|
|
122
|
+
throw next.error;
|
|
123
|
+
const final = await snapshot();
|
|
124
|
+
if (final)
|
|
125
|
+
return final;
|
|
126
|
+
// A stale CLI watch completion never turns a pending snapshot into success.
|
|
127
|
+
await delay(CI_PROBE_MS, undefined, { signal });
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
const head = await this.github.head(wait, signal);
|
|
131
|
+
if (head !== wait.headSha)
|
|
132
|
+
return observation(wait, 'head_changed', rows, { observedHeadSha: head });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
watchAbort.abort();
|
|
137
|
+
// Capacity is retained until the owned child (including forced termination) exits.
|
|
138
|
+
await watcher;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (signal.aborted)
|
|
143
|
+
return stopped();
|
|
144
|
+
if (error instanceof CiGithubError && error.transient && retry < CI_RETRY_MS.length) {
|
|
145
|
+
await delay(CI_RETRY_MS[retry++], undefined, { signal });
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
return observation(wait, 'error', rows, { diagnostic: error instanceof CiGithubError ? `${error.code}: ${error.message}` : 'unavailable: CI watcher failed; register a new wait.' });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return stopped();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (signal.aborted)
|
|
155
|
+
return stopped();
|
|
156
|
+
return observation(wait, 'error', rows, { diagnostic: error instanceof CiGithubError ? `${error.code}: ${error.message}` : 'unavailable: CI watcher failed; register a new wait.' });
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
release?.();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=supervisor.js.map
|