@volter/supercode-orchestrator 0.5.126 → 0.5.128
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/bin/runtime.mjs +1 -1
- package/board/ci.mjs +11 -3
- package/gates.mjs +6 -1
- package/installed-apps.mjs +5 -6
- package/package.json +2 -2
package/bin/runtime.mjs
CHANGED
|
@@ -480,7 +480,7 @@ async function appsMain(argv) {
|
|
|
480
480
|
}
|
|
481
481
|
const program = rh2Program();
|
|
482
482
|
if (!program) {
|
|
483
|
-
process.stderr.write(
|
|
483
|
+
process.stderr.write(`apps mcp: rh2 is not on PATH (searched ${process.env.PATH || 'an empty PATH'})\n`);
|
|
484
484
|
process.exit(127);
|
|
485
485
|
}
|
|
486
486
|
const child = spawn(program, ['mcp'], { stdio: 'inherit', env: { ...process.env, ...rh2AgentEnv(record) } });
|
package/board/ci.mjs
CHANGED
|
@@ -411,12 +411,20 @@ export async function inspectCompletionEvidence(board, id, entry, payload = {},
|
|
|
411
411
|
// Merged source is immutable and needs no surviving workspace. Resolve an
|
|
412
412
|
// unmerged candidate's checkout as before; a broken legacy path never
|
|
413
413
|
// prevents a verdict on already merged commits.
|
|
414
|
-
const
|
|
414
|
+
const read = await Promise.all(urls.map(readPullRequest));
|
|
415
|
+
const mergedOnly = urls.length && read.every(pr => pr.state === 'MERGED');
|
|
415
416
|
const own = mergedOnly ? null : await repositoryWorkspace(board, id);
|
|
416
417
|
if (!own && !urls.length) {
|
|
417
418
|
return state;
|
|
418
419
|
}
|
|
419
|
-
|
|
420
|
+
// A card with no repository of its own (scratch) is reviewed through the branches it registered: each unmerged PR
|
|
421
|
+
// on one of them (or on its wt/<card> branch) is read at its pushed head, and needs no checkout to exist here.
|
|
422
|
+
if (!own && !mergedOnly) {
|
|
423
|
+
const registered = registeredBranches(board, id);
|
|
424
|
+
const declared = (pr) => namedWorkBranch(id, pr.headRefName) || registered.some((r) => r.branch === pr.headRefName && r.repository.toLowerCase() === repositoryOf(pr.url).toLowerCase());
|
|
425
|
+
const stray = read.find((pr) => pr.state !== 'MERGED' && !declared(pr));
|
|
426
|
+
if (stray) throw new Error(`An unmerged PR candidate needs a repository workspace: ${stray.url} (${stray.headRefName}) is on no branch the card or its tasks declare; register it with create "<what>" --subtask-of ${id} --workspace worktree:${repositoryOf(stray.url)} --branch ${stray.headRefName}`);
|
|
427
|
+
}
|
|
420
428
|
// A verdict with no requested PR (a scratch arc) keeps its own contract. An arc's review
|
|
421
429
|
// always names every opened PR it changed, independent of completion_ci.
|
|
422
430
|
if (stage === 'review_verdict' && !asked && !urls.length) return state;
|
|
@@ -452,7 +460,7 @@ async function verifyPullRequest(board, id, entry, own, url, supplied, stage) {
|
|
|
452
460
|
// card's own checkout, and this machine's checkout of the PR's branch when it keeps one, must also hold that head
|
|
453
461
|
// with nothing uncommitted. A card on another machine is reviewed at its pushed head: its checkout is not read from here.
|
|
454
462
|
const observed = await readPullRequest(url);
|
|
455
|
-
const path =
|
|
463
|
+
const path = observed.state === 'MERGED' || (own && !own.here) ? null : !own ? await branchCheckout(board, id, repository, observed.headRefName) : await originOf(own.path) === repository && await run('git', ['branch', '--show-current'], own.path) === observed.headRefName ? own.path : await branchCheckout(board, id, repository, observed.headRefName);
|
|
456
464
|
// A PR merged before its review (an outage fix landed first) is still reviewed: its head is fixed for good, so the
|
|
457
465
|
// verdict is recorded on that head, and nothing about a worktree or its base can move under it any more.
|
|
458
466
|
const merged = observed.state === 'MERGED';
|
package/gates.mjs
CHANGED
|
@@ -146,11 +146,16 @@ export async function runJobScript(profile, scriptPath, workdir = null, { env =
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
// A signal that aborts `ms` from now, for use inside AbortSignal.any(). Its timer holds its controller, so it fires
|
|
150
|
+
// whatever the garbage collector does. AbortSignal.timeout() inside AbortSignal.any() does not on Node 23.9: the timeout
|
|
151
|
+
// signal is collected, the combined signal never aborts, and a request to a server that went quiet never ends.
|
|
152
|
+
const timeoutSignal=ms=>{const controller=new AbortController(),timer=setTimeout(()=>controller.abort(new DOMException('The operation was aborted due to timeout','TimeoutError')),ms);timer.unref?.();return controller.signal;};
|
|
153
|
+
|
|
149
154
|
/** `cron/monitor.py` `_fetch_monitor_url`: a bounded GET, the body as text. */
|
|
150
155
|
async function fetchMonitorUrl(url, signal = null) {
|
|
151
156
|
if (!/^https?:\/\//i.test(url)) return { ok: false, output: `monitor_url must be http(s): ${JSON.stringify(url)}` };
|
|
152
157
|
try {
|
|
153
|
-
const res = await fetch(url, { headers: { 'user-agent': 'hermes-cron-monitor' }, signal: signal ? AbortSignal.any([signal,
|
|
158
|
+
const res = await fetch(url, { headers: { 'user-agent': 'hermes-cron-monitor' }, signal: signal ? AbortSignal.any([signal, timeoutSignal(URL_TIMEOUT_MS)]) : AbortSignal.timeout(URL_TIMEOUT_MS) });
|
|
154
159
|
if (!res.ok) return { ok: false, output: `monitor_url fetch failed: HTTP Error ${res.status}: ${res.statusText}` };
|
|
155
160
|
const reader = res.body?.getReader();
|
|
156
161
|
const chunks = [];
|
package/installed-apps.mjs
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
// token, and the channel stops; `remove` forgets the record here.
|
|
6
6
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { delimiter, isAbsolute, join } from 'node:path';
|
|
8
|
-
import { homedir } from 'node:os';
|
|
9
8
|
|
|
10
9
|
export const APPS_DIR = 'apps';
|
|
11
10
|
/** The apps this orchestrator can host, by name: what each installs and which adapter it attaches. */
|
|
@@ -65,12 +64,12 @@ function rh2Origin(origin) {
|
|
|
65
64
|
return /^https:\/\/|^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(String(origin ?? ''));
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
/** The `rh2` CLI, by absolute path: in an absolute folder on this process's PATH,
|
|
69
|
-
*
|
|
70
|
-
* may be a checkout that carries its own `rh2`, which would then run with the agent's
|
|
71
|
-
* name, which the OS would look up the same way. */
|
|
67
|
+
/** The `rh2` CLI, by absolute path: the first in an absolute folder on this process's PATH, as the person's shell
|
|
68
|
+
* finds it; null when PATH has none. No install location is assumed. A relative PATH entry is never searched (a
|
|
69
|
+
* session's working directory may be a checkout that carries its own `rh2`, which would then run with the agent's
|
|
70
|
+
* token), and neither is a bare name, which the OS would look up the same way. */
|
|
72
71
|
export function rh2Program() {
|
|
73
|
-
const folders =
|
|
72
|
+
const folders = String(process.env.PATH ?? '').split(delimiter).filter((folder) => folder && isAbsolute(folder));
|
|
74
73
|
return folders.map((folder) => join(folder, 'rh2')).find((file) => existsSync(file)) ?? null;
|
|
75
74
|
}
|
|
76
75
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.128",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@volter/supercode-harness-sdk": "^0.3.60",
|
|
28
|
-
"@volter/teams": "0.2.
|
|
28
|
+
"@volter/teams": "0.2.51",
|
|
29
29
|
"@volter/ztrack": "^2.4.23",
|
|
30
30
|
"yaml": "^2.9.0"
|
|
31
31
|
},
|