@ran-sh/dsh-crew 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +16 -16
- package/.claude-plugin/plugin.json +14 -14
- package/LICENSE +21 -21
- package/README.de.md +359 -359
- package/README.es.md +359 -359
- package/README.fr.md +359 -359
- package/README.hi.md +359 -359
- package/README.id.md +359 -359
- package/README.ja.md +359 -359
- package/README.ko.md +359 -359
- package/README.md +126 -126
- package/README.pt.md +359 -359
- package/README.ru.md +359 -359
- package/README.th.md +359 -359
- package/README.tr.md +359 -359
- package/README.vi.md +359 -359
- package/README.zh-TW.md +359 -359
- package/README.zh.md +116 -116
- package/agents/ds-flash.md +24 -24
- package/agents/ds-pro.md +24 -24
- package/agents/ds-reviewer.md +23 -23
- package/agents/ds-worker.md +23 -23
- package/bin/dsh-crew.mjs +16 -16
- package/codex/agents/ds-flash.toml +34 -34
- package/codex/agents/ds-pro.toml +34 -34
- package/codex/agents/ds-reviewer.toml +32 -32
- package/codex/agents/ds-worker.toml +32 -32
- package/codex/prompts/dsh-config.md +19 -19
- package/codex/prompts/dsh-status.md +5 -5
- package/commands/config.md +23 -23
- package/commands/off.md +5 -5
- package/commands/on.md +5 -5
- package/commands/status.md +9 -9
- package/cordis.patch.yml +4 -4
- package/docs/gpt-relay-extension.md +103 -103
- package/docs/installation.md +138 -138
- package/docs/job-contracts.md +103 -103
- package/docs/readiness-matrix.md +85 -85
- package/docs/ui-surfaces.md +107 -107
- package/official-web-bridge/cordis.patch.yml +4 -4
- package/official-web-bridge/entry.mjs +1 -1
- package/official-web-bridge/overlay-entry.mjs +59 -59
- package/official-web-bridge/package.json +25 -25
- package/package.json +3 -2
- package/scripts/build-client.mjs +49 -49
- package/scripts/live-crew-smoke.mjs +39 -39
- package/scripts/live-policy-matrix.mjs +177 -177
- package/scripts/policy-probe.mjs +101 -101
- package/scripts/remove-legacy-official-bridge.ps1 +89 -89
- package/scripts/setup.mjs +393 -393
- package/scripts/smoke-real.mjs +110 -110
- package/scripts/smoke.mjs +78 -78
- package/scripts/verify-crew-ui-polish.mjs +145 -145
- package/scripts/verify-history-ui.mjs +97 -97
- package/scripts/verify-installer-fix.mjs +26 -26
- package/scripts/verify-npm-install.mjs +311 -311
- package/scripts/verify-official-bridge-e2e.mjs +192 -192
- package/src/adaptive-routing.mjs +260 -260
- package/src/client/activation-summary.tsx +64 -64
- package/src/client/collapsible-sections.mjs +55 -55
- package/src/client/history-panel.tsx +108 -108
- package/src/client/host-readiness.mjs +71 -71
- package/src/client/index.tsx +1711 -1711
- package/src/client/model-callability-view.mjs +17 -17
- package/src/client/panel-chrome.tsx +40 -40
- package/src/client/quick-entry.tsx +10 -10
- package/src/client/quick-panel.tsx +275 -275
- package/src/client/readiness-envelope.mjs +34 -34
- package/src/client/surface-detection.mjs +43 -43
- package/src/config-readiness.mjs +226 -226
- package/src/credential-reference.mjs +38 -38
- package/src/delivery.mjs +205 -205
- package/src/dsh-cli-runtime.mjs +1021 -1021
- package/src/dsh-cohort.mjs +20 -20
- package/src/extension-contract.mjs +104 -104
- package/src/failure-classification.mjs +201 -201
- package/src/history/admission-gate.mjs +67 -67
- package/src/history/archive-store.mjs +272 -272
- package/src/history/cleanup-plan.mjs +89 -89
- package/src/history/http.mjs +32 -32
- package/src/history/operation.mjs +86 -86
- package/src/history/runner-detach.mjs +34 -34
- package/src/history/runner.mjs +52 -52
- package/src/history/runtime.mjs +36 -36
- package/src/history/service.mjs +177 -177
- package/src/history/state.mjs +27 -27
- package/src/hub/entry.mjs +104 -104
- package/src/hub/index.mjs +2694 -2694
- package/src/hub-client.mjs +154 -154
- package/src/hub-compatibility.mjs +42 -42
- package/src/i18n.mjs +19 -19
- package/src/information-flow.mjs +67 -67
- package/src/install/cli.mjs +28 -28
- package/src/install/install-legacy.mjs +711 -711
- package/src/install/install.mjs +483 -483
- package/src/install/npx-lifecycle.mjs +3456 -3456
- package/src/install/official-frontend-assets.mjs +78 -78
- package/src/install/official-web.mjs +95 -95
- package/src/install/payload-content.mjs +88 -88
- package/src/install/windows-startup.mjs +236 -236
- package/src/install/windows-supervisor-adapter.mjs +443 -443
- package/src/install/windows-supervisor-lifecycle.mjs +782 -782
- package/src/install/zcode.mjs +397 -397
- package/src/job-contracts.mjs +255 -255
- package/src/local-request-guard.mjs +60 -60
- package/src/mcp-runtime.mjs +340 -340
- package/src/model-callability-contract.mjs +79 -79
- package/src/model-catalog.mjs +180 -180
- package/src/model-routing.mjs +586 -586
- package/src/model-schedule.mjs +207 -207
- package/src/official-web-bridge.mjs +447 -447
- package/src/policy.mjs +235 -235
- package/src/provider-delete-adapters.mjs +1934 -1934
- package/src/provider-health.mjs +130 -130
- package/src/provider-inventory.mjs +182 -182
- package/src/provider-layer-migration-adapters.mjs +759 -759
- package/src/provider-layer-migration.mjs +198 -198
- package/src/provider-lifecycle-state.mjs +103 -103
- package/src/provider-lifecycle.mjs +252 -252
- package/src/provider-profile-store.mjs +390 -390
- package/src/provider-settings-store.mjs +633 -633
- package/src/provider-store-lock.mjs +67 -67
- package/src/readiness-matrix.mjs +181 -181
- package/src/removable-waiter.mjs +29 -29
- package/src/role-profiles.mjs +107 -107
- package/src/runtime-controls.mjs +84 -84
- package/src/runtime-identity-contract.mjs +34 -34
- package/src/runtime-identity.mjs +235 -235
- package/src/runtime-readiness-snapshot.mjs +285 -285
- package/src/server.mjs +581 -581
- package/src/session-origins.mjs +60 -60
- package/src/standalone-sdk.mjs +23 -23
- package/src/status-shard.mjs +63 -63
- package/src/structured-error-code.mjs +38 -38
- package/src/supervisor/restart-request.mjs +256 -256
- package/src/workflow-runtime.mjs +739 -739
- package/src/workflow.mjs +155 -155
- package/src/workspace-audit.mjs +231 -231
- package/src/workspace-context.mjs +146 -146
- package/src/workspace-isolation.mjs +463 -455
- package/src/workspace-readiness.mjs +32 -32
- package/statusline/statusline.sh +14 -14
- package/statusline/worker-segment.sh +35 -35
- package/windows/start-dsh-crew.cmd +57 -57
- package/windows/start-dsh-crew.ps1 +1302 -1302
- package/windows/supervisor-control.ps1 +467 -467
- package/worker.cordis.yml +67 -67
- package/zcode/agents/ds-reviewer.md +31 -31
- package/zcode/agents/ds-worker.md +31 -31
- package/zcode/commands/dsh-config.md +17 -17
- package/zcode/commands/dsh-status.md +5 -5
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
// Persistent token-fenced lock shared by every lifecycle operation that can
|
|
2
|
-
// mutate the Harness profile/settings stores. A stale lock is reclaimed only
|
|
3
|
-
// while a serialized claim is held; malformed metadata fails closed.
|
|
4
|
-
|
|
5
|
-
import { randomUUID } from 'node:crypto';
|
|
6
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
|
-
import { dirname, resolve as resolvePath } from 'node:path';
|
|
8
|
-
|
|
9
|
-
function safeToken(value) { return typeof value === 'string' && /^[A-Za-z0-9-]{16,128}$/u.test(value); }
|
|
10
|
-
function readOwner(file) {
|
|
11
|
-
try {
|
|
12
|
-
const owner = JSON.parse(readFileSync(file, 'utf8'));
|
|
13
|
-
if (!owner || typeof owner !== 'object' || Array.isArray(owner) || !Number.isInteger(owner.pid) || owner.pid <= 0 || !safeToken(owner.token)) return { ok: false, malformed: true };
|
|
14
|
-
return { ok: true, owner };
|
|
15
|
-
} catch { return { ok: false, malformed: true }; }
|
|
16
|
-
}
|
|
17
|
-
function processAlive(pid) {
|
|
18
|
-
try { process.kill(pid, 0); return true; } catch (error) { return error?.code === 'EPERM'; }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function acquireProviderStoreLock(file) {
|
|
22
|
-
if (typeof file !== 'string' || !file.trim()) throw Object.assign(new Error('provider store lock path is required'), { code: 'PROVIDER_STORE_LOCK_UNAVAILABLE' });
|
|
23
|
-
const lockFile = resolvePath(file);
|
|
24
|
-
mkdirSync(dirname(lockFile), { recursive: true });
|
|
25
|
-
const claimFile = `${lockFile}.claim`;
|
|
26
|
-
const token = randomUUID();
|
|
27
|
-
let claimed = false;
|
|
28
|
-
try {
|
|
29
|
-
try { mkdirSync(claimFile); claimed = true; }
|
|
30
|
-
catch (error) { throw Object.assign(new Error('provider store lock is busy'), { code: error?.code === 'EEXIST' ? 'PROVIDER_STORE_LOCK_BUSY' : 'PROVIDER_STORE_LOCK_UNAVAILABLE' }); }
|
|
31
|
-
while (true) {
|
|
32
|
-
try {
|
|
33
|
-
writeFileSync(lockFile, JSON.stringify({ pid: process.pid, token, created_at: new Date().toISOString() }) + '\n', { flag: 'wx' });
|
|
34
|
-
return {
|
|
35
|
-
ok: true,
|
|
36
|
-
token,
|
|
37
|
-
release: async () => {
|
|
38
|
-
const current = readOwner(lockFile);
|
|
39
|
-
if (current.ok && current.owner.token === token) rmSync(lockFile, { force: true });
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
} catch (error) {
|
|
43
|
-
if (error?.code !== 'EEXIST') throw Object.assign(new Error('provider store lock is unavailable'), { code: 'PROVIDER_STORE_LOCK_UNAVAILABLE' });
|
|
44
|
-
const current = readOwner(lockFile);
|
|
45
|
-
if (!current.ok) throw Object.assign(new Error('provider store lock metadata is invalid'), { code: 'PROVIDER_STORE_LOCK_BUSY' });
|
|
46
|
-
if (processAlive(current.owner.pid)) throw Object.assign(new Error('provider store lock is busy'), { code: 'PROVIDER_STORE_LOCK_BUSY' });
|
|
47
|
-
rmSync(lockFile, { force: true });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
} finally { if (claimed) rmSync(claimFile, { recursive: true, force: true }); }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function recoverProviderStoreLock(file, { confirm = false } = {}) {
|
|
54
|
-
if (confirm !== true) return { ok: false, code: 'PROVIDER_STORE_LOCK_CONFIRM_REQUIRED' };
|
|
55
|
-
if (typeof file !== 'string' || !file.trim()) return { ok: false, code: 'PROVIDER_STORE_LOCK_UNAVAILABLE' };
|
|
56
|
-
const lockFile = resolvePath(file);
|
|
57
|
-
const claimFile = `${lockFile}.claim`;
|
|
58
|
-
try { mkdirSync(dirname(lockFile), { recursive: true }); mkdirSync(claimFile); }
|
|
59
|
-
catch (error) { return { ok: false, code: error?.code === 'EEXIST' ? 'PROVIDER_STORE_LOCK_BUSY' : 'PROVIDER_STORE_LOCK_UNAVAILABLE' }; }
|
|
60
|
-
try {
|
|
61
|
-
if (!existsSync(lockFile)) return { ok: true, recovered: false };
|
|
62
|
-
const current = readOwner(lockFile);
|
|
63
|
-
if (current.ok && processAlive(current.owner.pid)) return { ok: false, code: 'PROVIDER_STORE_LOCK_BUSY' };
|
|
64
|
-
rmSync(lockFile, { force: true });
|
|
65
|
-
return { ok: true, recovered: true };
|
|
66
|
-
} finally { rmSync(claimFile, { recursive: true, force: true }); }
|
|
67
|
-
}
|
|
1
|
+
// Persistent token-fenced lock shared by every lifecycle operation that can
|
|
2
|
+
// mutate the Harness profile/settings stores. A stale lock is reclaimed only
|
|
3
|
+
// while a serialized claim is held; malformed metadata fails closed.
|
|
4
|
+
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { dirname, resolve as resolvePath } from 'node:path';
|
|
8
|
+
|
|
9
|
+
function safeToken(value) { return typeof value === 'string' && /^[A-Za-z0-9-]{16,128}$/u.test(value); }
|
|
10
|
+
function readOwner(file) {
|
|
11
|
+
try {
|
|
12
|
+
const owner = JSON.parse(readFileSync(file, 'utf8'));
|
|
13
|
+
if (!owner || typeof owner !== 'object' || Array.isArray(owner) || !Number.isInteger(owner.pid) || owner.pid <= 0 || !safeToken(owner.token)) return { ok: false, malformed: true };
|
|
14
|
+
return { ok: true, owner };
|
|
15
|
+
} catch { return { ok: false, malformed: true }; }
|
|
16
|
+
}
|
|
17
|
+
function processAlive(pid) {
|
|
18
|
+
try { process.kill(pid, 0); return true; } catch (error) { return error?.code === 'EPERM'; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function acquireProviderStoreLock(file) {
|
|
22
|
+
if (typeof file !== 'string' || !file.trim()) throw Object.assign(new Error('provider store lock path is required'), { code: 'PROVIDER_STORE_LOCK_UNAVAILABLE' });
|
|
23
|
+
const lockFile = resolvePath(file);
|
|
24
|
+
mkdirSync(dirname(lockFile), { recursive: true });
|
|
25
|
+
const claimFile = `${lockFile}.claim`;
|
|
26
|
+
const token = randomUUID();
|
|
27
|
+
let claimed = false;
|
|
28
|
+
try {
|
|
29
|
+
try { mkdirSync(claimFile); claimed = true; }
|
|
30
|
+
catch (error) { throw Object.assign(new Error('provider store lock is busy'), { code: error?.code === 'EEXIST' ? 'PROVIDER_STORE_LOCK_BUSY' : 'PROVIDER_STORE_LOCK_UNAVAILABLE' }); }
|
|
31
|
+
while (true) {
|
|
32
|
+
try {
|
|
33
|
+
writeFileSync(lockFile, JSON.stringify({ pid: process.pid, token, created_at: new Date().toISOString() }) + '\n', { flag: 'wx' });
|
|
34
|
+
return {
|
|
35
|
+
ok: true,
|
|
36
|
+
token,
|
|
37
|
+
release: async () => {
|
|
38
|
+
const current = readOwner(lockFile);
|
|
39
|
+
if (current.ok && current.owner.token === token) rmSync(lockFile, { force: true });
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error?.code !== 'EEXIST') throw Object.assign(new Error('provider store lock is unavailable'), { code: 'PROVIDER_STORE_LOCK_UNAVAILABLE' });
|
|
44
|
+
const current = readOwner(lockFile);
|
|
45
|
+
if (!current.ok) throw Object.assign(new Error('provider store lock metadata is invalid'), { code: 'PROVIDER_STORE_LOCK_BUSY' });
|
|
46
|
+
if (processAlive(current.owner.pid)) throw Object.assign(new Error('provider store lock is busy'), { code: 'PROVIDER_STORE_LOCK_BUSY' });
|
|
47
|
+
rmSync(lockFile, { force: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} finally { if (claimed) rmSync(claimFile, { recursive: true, force: true }); }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function recoverProviderStoreLock(file, { confirm = false } = {}) {
|
|
54
|
+
if (confirm !== true) return { ok: false, code: 'PROVIDER_STORE_LOCK_CONFIRM_REQUIRED' };
|
|
55
|
+
if (typeof file !== 'string' || !file.trim()) return { ok: false, code: 'PROVIDER_STORE_LOCK_UNAVAILABLE' };
|
|
56
|
+
const lockFile = resolvePath(file);
|
|
57
|
+
const claimFile = `${lockFile}.claim`;
|
|
58
|
+
try { mkdirSync(dirname(lockFile), { recursive: true }); mkdirSync(claimFile); }
|
|
59
|
+
catch (error) { return { ok: false, code: error?.code === 'EEXIST' ? 'PROVIDER_STORE_LOCK_BUSY' : 'PROVIDER_STORE_LOCK_UNAVAILABLE' }; }
|
|
60
|
+
try {
|
|
61
|
+
if (!existsSync(lockFile)) return { ok: true, recovered: false };
|
|
62
|
+
const current = readOwner(lockFile);
|
|
63
|
+
if (current.ok && processAlive(current.owner.pid)) return { ok: false, code: 'PROVIDER_STORE_LOCK_BUSY' };
|
|
64
|
+
rmSync(lockFile, { force: true });
|
|
65
|
+
return { ok: true, recovered: true };
|
|
66
|
+
} finally { rmSync(claimFile, { recursive: true, force: true }); }
|
|
67
|
+
}
|
package/src/readiness-matrix.mjs
CHANGED
|
@@ -1,181 +1,181 @@
|
|
|
1
|
-
// Conservative release/environment readiness matrix.
|
|
2
|
-
//
|
|
3
|
-
// A row is PASS only when the caller supplies direct evidence for that row.
|
|
4
|
-
// Missing evidence is NOT_RUN, not an inferred success. Runtime observations
|
|
5
|
-
// (Hub handshake / catalog read) are intentionally separate from execution
|
|
6
|
-
// verification (real worker, reviewer, cancellation, timeout, etc.).
|
|
7
|
-
|
|
8
|
-
export const READINESS_STATUSES = Object.freeze(['PASS', 'FAIL', 'BLOCKED', 'SKIP', 'NOT_RUN']);
|
|
9
|
-
|
|
10
|
-
export const READINESS_REASON_CODES = Object.freeze({
|
|
11
|
-
LIVE_CHECK_PASSED: 'LIVE_CHECK_PASSED',
|
|
12
|
-
HUB_UNREACHABLE: 'HUB_UNREACHABLE',
|
|
13
|
-
HUB_INCOMPATIBLE: 'HUB_INCOMPATIBLE',
|
|
14
|
-
PROVIDER_CATALOG_RESOLVED: 'PROVIDER_CATALOG_RESOLVED',
|
|
15
|
-
PROVIDER_CATALOG_UNAVAILABLE: 'PROVIDER_CATALOG_UNAVAILABLE',
|
|
16
|
-
PROVIDER_CATALOG_HEALTH_WARNING: 'PROVIDER_CATALOG_HEALTH_WARNING',
|
|
17
|
-
PROVIDER_CATALOG_NOT_REQUIRED: 'PROVIDER_CATALOG_NOT_REQUIRED',
|
|
18
|
-
PROVIDER_LIFECYCLE_CONSISTENT: 'PROVIDER_LIFECYCLE_CONSISTENT',
|
|
19
|
-
PROVIDER_LIFECYCLE_INCONSISTENT: 'PROVIDER_LIFECYCLE_INCONSISTENT',
|
|
20
|
-
PROVIDER_INVENTORY_UNAVAILABLE: 'PROVIDER_INVENTORY_UNAVAILABLE',
|
|
21
|
-
PROVIDER_MODE_UNKNOWN: 'PROVIDER_MODE_UNKNOWN',
|
|
22
|
-
NO_CI_EVIDENCE: 'NO_CI_EVIDENCE',
|
|
23
|
-
NO_EXECUTION_EVIDENCE: 'NO_EXECUTION_EVIDENCE',
|
|
24
|
-
CREDENTIAL_STATUS_NOT_PROBED: 'CREDENTIAL_STATUS_NOT_PROBED',
|
|
25
|
-
EVIDENCE_REPORTED: 'EVIDENCE_REPORTED',
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const TARGET_ROWS = Object.freeze([
|
|
29
|
-
['linux_deterministic', 'ci'],
|
|
30
|
-
['windows_regressions', 'ci'],
|
|
31
|
-
['macos_smoke', 'ci'],
|
|
32
|
-
['hub_compatibility', 'live-runtime'],
|
|
33
|
-
['provider_catalog', 'live-runtime'],
|
|
34
|
-
['provider_health', 'live-runtime'],
|
|
35
|
-
['reviewer_health', 'live-runtime'],
|
|
36
|
-
['model_execution', 'real-execution'],
|
|
37
|
-
['worker_primary_callable', 'real-execution'],
|
|
38
|
-
['worker_escalation_callable', 'real-execution'],
|
|
39
|
-
['reviewer_primary_callable', 'real-execution'],
|
|
40
|
-
['provider_lifecycle_consistent', 'live-runtime'],
|
|
41
|
-
['deepseek_flash', 'real-execution'],
|
|
42
|
-
['deepseek_pro', 'real-execution'],
|
|
43
|
-
['reviewer_pipeline', 'real-execution'],
|
|
44
|
-
['cancellation_timeout_escalation', 'real-execution'],
|
|
45
|
-
['standalone_official', 'real-execution'],
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
function baseRow(id, category, status, reasonCode, evidenceSource = 'none', extra = {}) {
|
|
49
|
-
return {
|
|
50
|
-
id,
|
|
51
|
-
category,
|
|
52
|
-
status,
|
|
53
|
-
reason_code: reasonCode,
|
|
54
|
-
evidence_source: evidenceSource,
|
|
55
|
-
...extra,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function normalizeEvidence(row, evidence) {
|
|
60
|
-
if (!evidence || typeof evidence !== 'object') return row;
|
|
61
|
-
if (!READINESS_STATUSES.includes(evidence.status)) return row;
|
|
62
|
-
const reason = typeof evidence.reason_code === 'string' && evidence.reason_code.trim()
|
|
63
|
-
? evidence.reason_code.trim()
|
|
64
|
-
: READINESS_REASON_CODES.EVIDENCE_REPORTED;
|
|
65
|
-
const source = typeof evidence.evidence_source === 'string' && evidence.evidence_source.trim()
|
|
66
|
-
? evidence.evidence_source.trim()
|
|
67
|
-
: 'reported-evidence';
|
|
68
|
-
return {
|
|
69
|
-
...row,
|
|
70
|
-
status: evidence.status,
|
|
71
|
-
reason_code: reason,
|
|
72
|
-
evidence_source: source,
|
|
73
|
-
...(typeof evidence.evidence_ref === 'string' && evidence.evidence_ref.trim()
|
|
74
|
-
? { evidence_ref: evidence.evidence_ref.trim() }
|
|
75
|
-
: {}),
|
|
76
|
-
...(typeof evidence.detail_code === 'string' && /^[A-Z][A-Z0-9_]{0,127}$/u.test(evidence.detail_code)
|
|
77
|
-
? { detail_code: evidence.detail_code }
|
|
78
|
-
: {}),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function defaultRows() {
|
|
83
|
-
return Object.fromEntries(TARGET_ROWS.map(([id, category]) => {
|
|
84
|
-
const reason = category === 'ci'
|
|
85
|
-
? READINESS_REASON_CODES.NO_CI_EVIDENCE
|
|
86
|
-
: id === 'standalone_official'
|
|
87
|
-
? READINESS_REASON_CODES.CREDENTIAL_STATUS_NOT_PROBED
|
|
88
|
-
: READINESS_REASON_CODES.NO_EXECUTION_EVIDENCE;
|
|
89
|
-
return [id, baseRow(id, category, 'NOT_RUN', reason)];
|
|
90
|
-
}));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Build a secret-free readiness matrix from direct runtime observations plus
|
|
95
|
-
* optional trusted evidence supplied by a higher layer.
|
|
96
|
-
*
|
|
97
|
-
* `evidence` is deliberately inert metadata: this module never reads files,
|
|
98
|
-
* credentials, GitHub, provider config, or the network on its own.
|
|
99
|
-
*/
|
|
100
|
-
export function buildReadinessMatrix({
|
|
101
|
-
platform = process.platform,
|
|
102
|
-
hubCompatibility = null,
|
|
103
|
-
workerProviderMode = null,
|
|
104
|
-
providerCatalogChecked = false,
|
|
105
|
-
providerCatalogOk = false,
|
|
106
|
-
evidence = {},
|
|
107
|
-
} = {}) {
|
|
108
|
-
const rows = defaultRows();
|
|
109
|
-
|
|
110
|
-
if (hubCompatibility?.compatible === true) {
|
|
111
|
-
rows.hub_compatibility = baseRow(
|
|
112
|
-
'hub_compatibility', 'live-runtime', 'PASS',
|
|
113
|
-
READINESS_REASON_CODES.LIVE_CHECK_PASSED, 'hub-handshake',
|
|
114
|
-
{
|
|
115
|
-
runtime_version: hubCompatibility.runtime_version ?? null,
|
|
116
|
-
protocol_version: hubCompatibility.protocol_version ?? null,
|
|
117
|
-
},
|
|
118
|
-
);
|
|
119
|
-
} else if (hubCompatibility?.reachable === true) {
|
|
120
|
-
rows.hub_compatibility = baseRow(
|
|
121
|
-
'hub_compatibility', 'live-runtime', 'FAIL',
|
|
122
|
-
READINESS_REASON_CODES.HUB_INCOMPATIBLE, 'hub-handshake',
|
|
123
|
-
{ detail_code: hubCompatibility.code ?? null },
|
|
124
|
-
);
|
|
125
|
-
} else {
|
|
126
|
-
rows.hub_compatibility = baseRow(
|
|
127
|
-
'hub_compatibility', 'live-runtime', 'BLOCKED',
|
|
128
|
-
READINESS_REASON_CODES.HUB_UNREACHABLE, 'hub-handshake',
|
|
129
|
-
{ detail_code: hubCompatibility?.code ?? null },
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (workerProviderMode == null) {
|
|
134
|
-
rows.provider_catalog = baseRow(
|
|
135
|
-
'provider_catalog', 'live-runtime', 'NOT_RUN',
|
|
136
|
-
READINESS_REASON_CODES.PROVIDER_MODE_UNKNOWN, 'none',
|
|
137
|
-
);
|
|
138
|
-
} else if (workerProviderMode === 'deepseek-official') {
|
|
139
|
-
rows.provider_catalog = baseRow(
|
|
140
|
-
'provider_catalog', 'live-runtime', 'SKIP',
|
|
141
|
-
READINESS_REASON_CODES.PROVIDER_CATALOG_NOT_REQUIRED, 'runtime-policy',
|
|
142
|
-
);
|
|
143
|
-
} else if (!hubCompatibility?.compatible) {
|
|
144
|
-
rows.provider_catalog = baseRow(
|
|
145
|
-
'provider_catalog', 'live-runtime', 'BLOCKED',
|
|
146
|
-
hubCompatibility?.reachable
|
|
147
|
-
? READINESS_REASON_CODES.HUB_INCOMPATIBLE
|
|
148
|
-
: READINESS_REASON_CODES.HUB_UNREACHABLE,
|
|
149
|
-
'hub-handshake',
|
|
150
|
-
{ detail_code: hubCompatibility?.code ?? null },
|
|
151
|
-
);
|
|
152
|
-
} else if (providerCatalogChecked && providerCatalogOk) {
|
|
153
|
-
rows.provider_catalog = baseRow(
|
|
154
|
-
'provider_catalog', 'live-runtime', 'PASS',
|
|
155
|
-
READINESS_REASON_CODES.PROVIDER_CATALOG_RESOLVED, 'harness-catalog',
|
|
156
|
-
);
|
|
157
|
-
} else if (providerCatalogChecked) {
|
|
158
|
-
rows.provider_catalog = baseRow(
|
|
159
|
-
'provider_catalog', 'live-runtime', 'FAIL',
|
|
160
|
-
READINESS_REASON_CODES.PROVIDER_CATALOG_UNAVAILABLE, 'harness-catalog',
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
for (const [id] of TARGET_ROWS) {
|
|
165
|
-
rows[id] = normalizeEvidence(rows[id], evidence?.[id]);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const ordered = TARGET_ROWS.map(([id]) => rows[id]);
|
|
169
|
-
const counts = Object.fromEntries(READINESS_STATUSES.map((status) => [
|
|
170
|
-
status,
|
|
171
|
-
ordered.filter((row) => row.status === status).length,
|
|
172
|
-
]));
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
schema_version: 1,
|
|
176
|
-
platform,
|
|
177
|
-
conservative: true,
|
|
178
|
-
summary: counts,
|
|
179
|
-
rows: ordered,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
1
|
+
// Conservative release/environment readiness matrix.
|
|
2
|
+
//
|
|
3
|
+
// A row is PASS only when the caller supplies direct evidence for that row.
|
|
4
|
+
// Missing evidence is NOT_RUN, not an inferred success. Runtime observations
|
|
5
|
+
// (Hub handshake / catalog read) are intentionally separate from execution
|
|
6
|
+
// verification (real worker, reviewer, cancellation, timeout, etc.).
|
|
7
|
+
|
|
8
|
+
export const READINESS_STATUSES = Object.freeze(['PASS', 'FAIL', 'BLOCKED', 'SKIP', 'NOT_RUN']);
|
|
9
|
+
|
|
10
|
+
export const READINESS_REASON_CODES = Object.freeze({
|
|
11
|
+
LIVE_CHECK_PASSED: 'LIVE_CHECK_PASSED',
|
|
12
|
+
HUB_UNREACHABLE: 'HUB_UNREACHABLE',
|
|
13
|
+
HUB_INCOMPATIBLE: 'HUB_INCOMPATIBLE',
|
|
14
|
+
PROVIDER_CATALOG_RESOLVED: 'PROVIDER_CATALOG_RESOLVED',
|
|
15
|
+
PROVIDER_CATALOG_UNAVAILABLE: 'PROVIDER_CATALOG_UNAVAILABLE',
|
|
16
|
+
PROVIDER_CATALOG_HEALTH_WARNING: 'PROVIDER_CATALOG_HEALTH_WARNING',
|
|
17
|
+
PROVIDER_CATALOG_NOT_REQUIRED: 'PROVIDER_CATALOG_NOT_REQUIRED',
|
|
18
|
+
PROVIDER_LIFECYCLE_CONSISTENT: 'PROVIDER_LIFECYCLE_CONSISTENT',
|
|
19
|
+
PROVIDER_LIFECYCLE_INCONSISTENT: 'PROVIDER_LIFECYCLE_INCONSISTENT',
|
|
20
|
+
PROVIDER_INVENTORY_UNAVAILABLE: 'PROVIDER_INVENTORY_UNAVAILABLE',
|
|
21
|
+
PROVIDER_MODE_UNKNOWN: 'PROVIDER_MODE_UNKNOWN',
|
|
22
|
+
NO_CI_EVIDENCE: 'NO_CI_EVIDENCE',
|
|
23
|
+
NO_EXECUTION_EVIDENCE: 'NO_EXECUTION_EVIDENCE',
|
|
24
|
+
CREDENTIAL_STATUS_NOT_PROBED: 'CREDENTIAL_STATUS_NOT_PROBED',
|
|
25
|
+
EVIDENCE_REPORTED: 'EVIDENCE_REPORTED',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const TARGET_ROWS = Object.freeze([
|
|
29
|
+
['linux_deterministic', 'ci'],
|
|
30
|
+
['windows_regressions', 'ci'],
|
|
31
|
+
['macos_smoke', 'ci'],
|
|
32
|
+
['hub_compatibility', 'live-runtime'],
|
|
33
|
+
['provider_catalog', 'live-runtime'],
|
|
34
|
+
['provider_health', 'live-runtime'],
|
|
35
|
+
['reviewer_health', 'live-runtime'],
|
|
36
|
+
['model_execution', 'real-execution'],
|
|
37
|
+
['worker_primary_callable', 'real-execution'],
|
|
38
|
+
['worker_escalation_callable', 'real-execution'],
|
|
39
|
+
['reviewer_primary_callable', 'real-execution'],
|
|
40
|
+
['provider_lifecycle_consistent', 'live-runtime'],
|
|
41
|
+
['deepseek_flash', 'real-execution'],
|
|
42
|
+
['deepseek_pro', 'real-execution'],
|
|
43
|
+
['reviewer_pipeline', 'real-execution'],
|
|
44
|
+
['cancellation_timeout_escalation', 'real-execution'],
|
|
45
|
+
['standalone_official', 'real-execution'],
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
function baseRow(id, category, status, reasonCode, evidenceSource = 'none', extra = {}) {
|
|
49
|
+
return {
|
|
50
|
+
id,
|
|
51
|
+
category,
|
|
52
|
+
status,
|
|
53
|
+
reason_code: reasonCode,
|
|
54
|
+
evidence_source: evidenceSource,
|
|
55
|
+
...extra,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeEvidence(row, evidence) {
|
|
60
|
+
if (!evidence || typeof evidence !== 'object') return row;
|
|
61
|
+
if (!READINESS_STATUSES.includes(evidence.status)) return row;
|
|
62
|
+
const reason = typeof evidence.reason_code === 'string' && evidence.reason_code.trim()
|
|
63
|
+
? evidence.reason_code.trim()
|
|
64
|
+
: READINESS_REASON_CODES.EVIDENCE_REPORTED;
|
|
65
|
+
const source = typeof evidence.evidence_source === 'string' && evidence.evidence_source.trim()
|
|
66
|
+
? evidence.evidence_source.trim()
|
|
67
|
+
: 'reported-evidence';
|
|
68
|
+
return {
|
|
69
|
+
...row,
|
|
70
|
+
status: evidence.status,
|
|
71
|
+
reason_code: reason,
|
|
72
|
+
evidence_source: source,
|
|
73
|
+
...(typeof evidence.evidence_ref === 'string' && evidence.evidence_ref.trim()
|
|
74
|
+
? { evidence_ref: evidence.evidence_ref.trim() }
|
|
75
|
+
: {}),
|
|
76
|
+
...(typeof evidence.detail_code === 'string' && /^[A-Z][A-Z0-9_]{0,127}$/u.test(evidence.detail_code)
|
|
77
|
+
? { detail_code: evidence.detail_code }
|
|
78
|
+
: {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function defaultRows() {
|
|
83
|
+
return Object.fromEntries(TARGET_ROWS.map(([id, category]) => {
|
|
84
|
+
const reason = category === 'ci'
|
|
85
|
+
? READINESS_REASON_CODES.NO_CI_EVIDENCE
|
|
86
|
+
: id === 'standalone_official'
|
|
87
|
+
? READINESS_REASON_CODES.CREDENTIAL_STATUS_NOT_PROBED
|
|
88
|
+
: READINESS_REASON_CODES.NO_EXECUTION_EVIDENCE;
|
|
89
|
+
return [id, baseRow(id, category, 'NOT_RUN', reason)];
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build a secret-free readiness matrix from direct runtime observations plus
|
|
95
|
+
* optional trusted evidence supplied by a higher layer.
|
|
96
|
+
*
|
|
97
|
+
* `evidence` is deliberately inert metadata: this module never reads files,
|
|
98
|
+
* credentials, GitHub, provider config, or the network on its own.
|
|
99
|
+
*/
|
|
100
|
+
export function buildReadinessMatrix({
|
|
101
|
+
platform = process.platform,
|
|
102
|
+
hubCompatibility = null,
|
|
103
|
+
workerProviderMode = null,
|
|
104
|
+
providerCatalogChecked = false,
|
|
105
|
+
providerCatalogOk = false,
|
|
106
|
+
evidence = {},
|
|
107
|
+
} = {}) {
|
|
108
|
+
const rows = defaultRows();
|
|
109
|
+
|
|
110
|
+
if (hubCompatibility?.compatible === true) {
|
|
111
|
+
rows.hub_compatibility = baseRow(
|
|
112
|
+
'hub_compatibility', 'live-runtime', 'PASS',
|
|
113
|
+
READINESS_REASON_CODES.LIVE_CHECK_PASSED, 'hub-handshake',
|
|
114
|
+
{
|
|
115
|
+
runtime_version: hubCompatibility.runtime_version ?? null,
|
|
116
|
+
protocol_version: hubCompatibility.protocol_version ?? null,
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
} else if (hubCompatibility?.reachable === true) {
|
|
120
|
+
rows.hub_compatibility = baseRow(
|
|
121
|
+
'hub_compatibility', 'live-runtime', 'FAIL',
|
|
122
|
+
READINESS_REASON_CODES.HUB_INCOMPATIBLE, 'hub-handshake',
|
|
123
|
+
{ detail_code: hubCompatibility.code ?? null },
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
rows.hub_compatibility = baseRow(
|
|
127
|
+
'hub_compatibility', 'live-runtime', 'BLOCKED',
|
|
128
|
+
READINESS_REASON_CODES.HUB_UNREACHABLE, 'hub-handshake',
|
|
129
|
+
{ detail_code: hubCompatibility?.code ?? null },
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (workerProviderMode == null) {
|
|
134
|
+
rows.provider_catalog = baseRow(
|
|
135
|
+
'provider_catalog', 'live-runtime', 'NOT_RUN',
|
|
136
|
+
READINESS_REASON_CODES.PROVIDER_MODE_UNKNOWN, 'none',
|
|
137
|
+
);
|
|
138
|
+
} else if (workerProviderMode === 'deepseek-official') {
|
|
139
|
+
rows.provider_catalog = baseRow(
|
|
140
|
+
'provider_catalog', 'live-runtime', 'SKIP',
|
|
141
|
+
READINESS_REASON_CODES.PROVIDER_CATALOG_NOT_REQUIRED, 'runtime-policy',
|
|
142
|
+
);
|
|
143
|
+
} else if (!hubCompatibility?.compatible) {
|
|
144
|
+
rows.provider_catalog = baseRow(
|
|
145
|
+
'provider_catalog', 'live-runtime', 'BLOCKED',
|
|
146
|
+
hubCompatibility?.reachable
|
|
147
|
+
? READINESS_REASON_CODES.HUB_INCOMPATIBLE
|
|
148
|
+
: READINESS_REASON_CODES.HUB_UNREACHABLE,
|
|
149
|
+
'hub-handshake',
|
|
150
|
+
{ detail_code: hubCompatibility?.code ?? null },
|
|
151
|
+
);
|
|
152
|
+
} else if (providerCatalogChecked && providerCatalogOk) {
|
|
153
|
+
rows.provider_catalog = baseRow(
|
|
154
|
+
'provider_catalog', 'live-runtime', 'PASS',
|
|
155
|
+
READINESS_REASON_CODES.PROVIDER_CATALOG_RESOLVED, 'harness-catalog',
|
|
156
|
+
);
|
|
157
|
+
} else if (providerCatalogChecked) {
|
|
158
|
+
rows.provider_catalog = baseRow(
|
|
159
|
+
'provider_catalog', 'live-runtime', 'FAIL',
|
|
160
|
+
READINESS_REASON_CODES.PROVIDER_CATALOG_UNAVAILABLE, 'harness-catalog',
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const [id] of TARGET_ROWS) {
|
|
165
|
+
rows[id] = normalizeEvidence(rows[id], evidence?.[id]);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const ordered = TARGET_ROWS.map(([id]) => rows[id]);
|
|
169
|
+
const counts = Object.fromEntries(READINESS_STATUSES.map((status) => [
|
|
170
|
+
status,
|
|
171
|
+
ordered.filter((row) => row.status === status).length,
|
|
172
|
+
]));
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
schema_version: 1,
|
|
176
|
+
platform,
|
|
177
|
+
conservative: true,
|
|
178
|
+
summary: counts,
|
|
179
|
+
rows: ordered,
|
|
180
|
+
};
|
|
181
|
+
}
|
package/src/removable-waiter.mjs
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
// Long-poll wait registration that removes itself: the terminal transition
|
|
2
|
-
// bulk-clears job.waiters, so a timed-out waiter that stayed registered would
|
|
3
|
-
// linger (and accumulate across polls) for the remaining lifetime of a
|
|
4
|
-
// long-running job. All three wait surfaces (hub, standalone jobs, workflow
|
|
5
|
-
// runtime) share this one implementation so the cleanup invariant cannot
|
|
6
|
-
// drift again.
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Race the job's waiters against a timeout, always unregistering the waiter
|
|
10
|
-
* afterwards. `timeoutMs <= 0` means "no timeout": wait forever unless
|
|
11
|
-
* `immediateWithoutTimeout` is set, which resolves immediately (poll
|
|
12
|
-
* semantics, used by the hub route).
|
|
13
|
-
*/
|
|
14
|
-
export async function raceWaiters(waiters, { timeoutMs = 0, immediateWithoutTimeout = false } = {}) {
|
|
15
|
-
let waiter = null;
|
|
16
|
-
let timer = null;
|
|
17
|
-
try {
|
|
18
|
-
await Promise.race([
|
|
19
|
-
new Promise((resolve) => { waiter = resolve; waiters.push(resolve); }),
|
|
20
|
-
timeoutMs > 0
|
|
21
|
-
? new Promise((resolve) => { timer = setTimeout(resolve, timeoutMs); })
|
|
22
|
-
: (immediateWithoutTimeout ? Promise.resolve() : new Promise(() => {})),
|
|
23
|
-
]);
|
|
24
|
-
} finally {
|
|
25
|
-
const index = waiters.indexOf(waiter);
|
|
26
|
-
if (index !== -1) waiters.splice(index, 1);
|
|
27
|
-
if (timer !== null) clearTimeout(timer);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
// Long-poll wait registration that removes itself: the terminal transition
|
|
2
|
+
// bulk-clears job.waiters, so a timed-out waiter that stayed registered would
|
|
3
|
+
// linger (and accumulate across polls) for the remaining lifetime of a
|
|
4
|
+
// long-running job. All three wait surfaces (hub, standalone jobs, workflow
|
|
5
|
+
// runtime) share this one implementation so the cleanup invariant cannot
|
|
6
|
+
// drift again.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Race the job's waiters against a timeout, always unregistering the waiter
|
|
10
|
+
* afterwards. `timeoutMs <= 0` means "no timeout": wait forever unless
|
|
11
|
+
* `immediateWithoutTimeout` is set, which resolves immediately (poll
|
|
12
|
+
* semantics, used by the hub route).
|
|
13
|
+
*/
|
|
14
|
+
export async function raceWaiters(waiters, { timeoutMs = 0, immediateWithoutTimeout = false } = {}) {
|
|
15
|
+
let waiter = null;
|
|
16
|
+
let timer = null;
|
|
17
|
+
try {
|
|
18
|
+
await Promise.race([
|
|
19
|
+
new Promise((resolve) => { waiter = resolve; waiters.push(resolve); }),
|
|
20
|
+
timeoutMs > 0
|
|
21
|
+
? new Promise((resolve) => { timer = setTimeout(resolve, timeoutMs); })
|
|
22
|
+
: (immediateWithoutTimeout ? Promise.resolve() : new Promise(() => {})),
|
|
23
|
+
]);
|
|
24
|
+
} finally {
|
|
25
|
+
const index = waiters.indexOf(waiter);
|
|
26
|
+
if (index !== -1) waiters.splice(index, 1);
|
|
27
|
+
if (timer !== null) clearTimeout(timer);
|
|
28
|
+
}
|
|
29
|
+
}
|