@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,43 +1,43 @@
|
|
|
1
|
-
export const CREW_UI_SURFACES = Object.freeze({
|
|
2
|
-
OFFICIAL: 'official-bridge',
|
|
3
|
-
NATIVE: 'native-crew-harness',
|
|
4
|
-
UNKNOWN: 'unknown',
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Classify the current same-origin UI from explicit backend contracts. The
|
|
9
|
-
* official bridge signal wins because its proxied runtime response correctly
|
|
10
|
-
* describes the 3210 backend, not the browser-facing 3080 surface.
|
|
11
|
-
*/
|
|
12
|
-
export function classifyCrewSurface({ bridgeStatus, runtime } = {}) {
|
|
13
|
-
if (bridgeStatus?.ok === true
|
|
14
|
-
&& (bridgeStatus.surface === CREW_UI_SURFACES.OFFICIAL
|
|
15
|
-
|| bridgeStatus.mode === 'official-3080-isolated-3210')) {
|
|
16
|
-
return CREW_UI_SURFACES.OFFICIAL;
|
|
17
|
-
}
|
|
18
|
-
if (runtime?.ok === true
|
|
19
|
-
&& runtime.service === 'dsh-crew-hub'
|
|
20
|
-
&& (runtime.surface === CREW_UI_SURFACES.NATIVE || runtime.surface === undefined)) {
|
|
21
|
-
return CREW_UI_SURFACES.NATIVE;
|
|
22
|
-
}
|
|
23
|
-
return CREW_UI_SURFACES.UNKNOWN;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Surface capability model. The NATIVE 3210 Crew harness is the single full
|
|
28
|
-
* control plane; the OFFICIAL 3080 surface is a narrow quick-controls panel
|
|
29
|
-
* (total switch and the flash/pro model priority lists) plus a
|
|
30
|
-
* deep link back to 3210. Unknown surfaces get diagnostics only — never write
|
|
31
|
-
* authority. This replaces the old binary full-vs-minimal assumption.
|
|
32
|
-
*/
|
|
33
|
-
export function surfaceResponsibilities(surface) {
|
|
34
|
-
switch (surface) {
|
|
35
|
-
case CREW_UI_SURFACES.NATIVE:
|
|
36
|
-
return { fullControlPlane: true, quickControlPlane: true, diagnostics: true };
|
|
37
|
-
case CREW_UI_SURFACES.OFFICIAL:
|
|
38
|
-
return { fullControlPlane: false, quickControlPlane: true, diagnostics: true };
|
|
39
|
-
default:
|
|
40
|
-
return { fullControlPlane: false, quickControlPlane: false, diagnostics: true };
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
1
|
+
export const CREW_UI_SURFACES = Object.freeze({
|
|
2
|
+
OFFICIAL: 'official-bridge',
|
|
3
|
+
NATIVE: 'native-crew-harness',
|
|
4
|
+
UNKNOWN: 'unknown',
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Classify the current same-origin UI from explicit backend contracts. The
|
|
9
|
+
* official bridge signal wins because its proxied runtime response correctly
|
|
10
|
+
* describes the 3210 backend, not the browser-facing 3080 surface.
|
|
11
|
+
*/
|
|
12
|
+
export function classifyCrewSurface({ bridgeStatus, runtime } = {}) {
|
|
13
|
+
if (bridgeStatus?.ok === true
|
|
14
|
+
&& (bridgeStatus.surface === CREW_UI_SURFACES.OFFICIAL
|
|
15
|
+
|| bridgeStatus.mode === 'official-3080-isolated-3210')) {
|
|
16
|
+
return CREW_UI_SURFACES.OFFICIAL;
|
|
17
|
+
}
|
|
18
|
+
if (runtime?.ok === true
|
|
19
|
+
&& runtime.service === 'dsh-crew-hub'
|
|
20
|
+
&& (runtime.surface === CREW_UI_SURFACES.NATIVE || runtime.surface === undefined)) {
|
|
21
|
+
return CREW_UI_SURFACES.NATIVE;
|
|
22
|
+
}
|
|
23
|
+
return CREW_UI_SURFACES.UNKNOWN;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Surface capability model. The NATIVE 3210 Crew harness is the single full
|
|
28
|
+
* control plane; the OFFICIAL 3080 surface is a narrow quick-controls panel
|
|
29
|
+
* (total switch and the flash/pro model priority lists) plus a
|
|
30
|
+
* deep link back to 3210. Unknown surfaces get diagnostics only — never write
|
|
31
|
+
* authority. This replaces the old binary full-vs-minimal assumption.
|
|
32
|
+
*/
|
|
33
|
+
export function surfaceResponsibilities(surface) {
|
|
34
|
+
switch (surface) {
|
|
35
|
+
case CREW_UI_SURFACES.NATIVE:
|
|
36
|
+
return { fullControlPlane: true, quickControlPlane: true, diagnostics: true };
|
|
37
|
+
case CREW_UI_SURFACES.OFFICIAL:
|
|
38
|
+
return { fullControlPlane: false, quickControlPlane: true, diagnostics: true };
|
|
39
|
+
default:
|
|
40
|
+
return { fullControlPlane: false, quickControlPlane: false, diagnostics: true };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
package/src/config-readiness.mjs
CHANGED
|
@@ -1,226 +1,226 @@
|
|
|
1
|
-
import { buildReadinessMatrix, READINESS_REASON_CODES } from './readiness-matrix.mjs';
|
|
2
|
-
import { PRODUCTION_EXECUTION_PLANE, PRODUCTION_PROFILE, PRODUCTION_LISTEN_PORT } from './runtime-identity.mjs';
|
|
3
|
-
|
|
4
|
-
function warningCodes(catalogBody) {
|
|
5
|
-
const hints = Array.isArray(catalogBody?.health?.hints) ? catalogBody.health.hints : [];
|
|
6
|
-
return [...new Set(hints
|
|
7
|
-
.filter((hint) => hint?.level === 'warning' && typeof hint?.code === 'string' && hint.code.trim())
|
|
8
|
-
.map((hint) => hint.code.trim()))];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function verifiedHubJob(job) {
|
|
12
|
-
const context = job?.execution_context;
|
|
13
|
-
return job?.status === 'done'
|
|
14
|
-
&& job?.task_status === 'success'
|
|
15
|
-
&& job?.delivery_complete === true
|
|
16
|
-
&& job?.workspace_evidence_ok === true
|
|
17
|
-
&& context?.execution_plane === PRODUCTION_EXECUTION_PLANE
|
|
18
|
-
&& context?.profile === PRODUCTION_PROFILE
|
|
19
|
-
&& Number(context?.listen_port) === PRODUCTION_LISTEN_PORT
|
|
20
|
-
&& typeof context?.runtime_id === 'string'
|
|
21
|
-
&& context.runtime_id.trim().length > 0;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function hasBlockingProviderRecovery(body, currentSelections) {
|
|
25
|
-
if (!Object.hasOwn(body, 'recovery_transactions')) return false;
|
|
26
|
-
if (!Array.isArray(body.recovery_transactions)) return true;
|
|
27
|
-
const selectedProviders = new Set(Object.values(currentSelections ?? {})
|
|
28
|
-
.filter((selection) => selection?.ok === true && typeof selection.provider === 'string')
|
|
29
|
-
.map((selection) => selection.provider));
|
|
30
|
-
return body.recovery_transactions.some((transaction) => transaction?.unresolved === true
|
|
31
|
-
|| typeof transaction?.provider_id !== 'string'
|
|
32
|
-
|| selectedProviders.has(transaction.provider_id));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function structurallyConsistentProviderInventory(body, currentSelections) {
|
|
36
|
-
if (!body || typeof body !== 'object' || body.ok === false || !Array.isArray(body.records)) return false;
|
|
37
|
-
if ('lifecycle_evidence' in body && body.lifecycle_evidence?.ok !== true) return false;
|
|
38
|
-
if ('declaration_evidence' in body && body.declaration_evidence?.ok !== true) return false;
|
|
39
|
-
if ('default_evidence' in body && body.default_evidence?.ok !== true) return false;
|
|
40
|
-
if (hasBlockingProviderRecovery(body, currentSelections)) return false;
|
|
41
|
-
return body.records.every((record) => {
|
|
42
|
-
if (!record || typeof record.id !== 'string' || record.id.trim() === '') return false;
|
|
43
|
-
if (!['present', 'absent'].includes(record.desired_state)) return false;
|
|
44
|
-
if (!record.lifecycle || typeof record.lifecycle !== 'object' || Array.isArray(record.lifecycle)) return false;
|
|
45
|
-
for (const field of ['installed', 'configured', 'enabled', 'catalogued']) {
|
|
46
|
-
if (typeof record.lifecycle[field] !== 'boolean') return false;
|
|
47
|
-
}
|
|
48
|
-
if (record.desired_state === 'absent' && record.lifecycle.enabled === true) return false;
|
|
49
|
-
return true;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function matchesCurrentRoute(job, currentSelections, role) {
|
|
54
|
-
if (!currentSelections) return true;
|
|
55
|
-
const selectionKey = role === 'worker'
|
|
56
|
-
&& Number(job?.attempt ?? job?.selection_trace?.logical_attempt ?? 0) > 0
|
|
57
|
-
&& Object.hasOwn(currentSelections, 'worker_escalation')
|
|
58
|
-
? 'worker_escalation'
|
|
59
|
-
: role;
|
|
60
|
-
if (!Object.hasOwn(currentSelections, selectionKey)) return true;
|
|
61
|
-
const selected = currentSelections[selectionKey];
|
|
62
|
-
return selected?.ok === true
|
|
63
|
-
&& job?.provider === selected.provider
|
|
64
|
-
&& job?.model === selected.model;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function safeDetailCode(value, fallback) {
|
|
68
|
-
return typeof value === 'string' && /^[A-Z][A-Z0-9_]{0,127}$/u.test(value) ? value : fallback;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function currentRouteHealthEvidence({ role, currentSelections, providerHealthChecked, providerHealthBody } = {}) {
|
|
72
|
-
if (!currentSelections || !Object.hasOwn(currentSelections, role)) return null;
|
|
73
|
-
const selected = currentSelections[role];
|
|
74
|
-
if (selected?.ok === false) {
|
|
75
|
-
return {
|
|
76
|
-
status: 'FAIL',
|
|
77
|
-
reason_code: 'PROVIDER_ROUTE_UNCALLABLE',
|
|
78
|
-
evidence_source: 'model-routing',
|
|
79
|
-
detail_code: safeDetailCode(selected.code, 'MODEL_ROUTE_UNAVAILABLE'),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
if (!selected?.provider || !selected?.model || providerHealthChecked !== true) return null;
|
|
83
|
-
if (providerHealthBody?.ok === false) {
|
|
84
|
-
return { status: 'FAIL', reason_code: 'PROVIDER_HEALTH_UNAVAILABLE', evidence_source: 'provider-health' };
|
|
85
|
-
}
|
|
86
|
-
const health = Array.isArray(providerHealthBody?.health) ? providerHealthBody.health : [];
|
|
87
|
-
const observation = health.find((entry) => entry?.provider === selected.provider
|
|
88
|
-
&& entry?.model === selected.model
|
|
89
|
-
&& entry?.fresh === true);
|
|
90
|
-
if (!observation || observation.state === 'unprobed') return null;
|
|
91
|
-
if (observation.state === 'callable') {
|
|
92
|
-
return { status: 'PASS', reason_code: 'PROVIDER_ROUTE_CALLABLE', evidence_source: 'provider-health' };
|
|
93
|
-
}
|
|
94
|
-
const stateCode = String(observation.state ?? '').toUpperCase().replaceAll('-', '_');
|
|
95
|
-
return {
|
|
96
|
-
status: 'FAIL',
|
|
97
|
-
reason_code: 'PROVIDER_ROUTE_UNCALLABLE',
|
|
98
|
-
evidence_source: 'provider-health',
|
|
99
|
-
detail_code: safeDetailCode(observation.reason_code, safeDetailCode(stateCode, 'PROVIDER_UNCALLABLE')),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function buildHubExecutionRows(hubJobs = [], { currentSelections = null } = {}) {
|
|
104
|
-
const jobs = Array.isArray(hubJobs) ? hubJobs : [];
|
|
105
|
-
const workerPassed = jobs.some((job) => job?.role === 'worker' && matchesCurrentRoute(job, currentSelections, 'worker') && verifiedHubJob(job));
|
|
106
|
-
const workerPrimaryPassed = jobs.some((job) => job?.role === 'worker'
|
|
107
|
-
&& matchesCurrentRoute(job, currentSelections, 'worker')
|
|
108
|
-
&& verifiedHubJob(job)
|
|
109
|
-
&& Number(job?.attempt ?? job?.selection_trace?.logical_attempt ?? 0) === 0);
|
|
110
|
-
const workerEscalationPassed = jobs.some((job) => job?.role === 'worker'
|
|
111
|
-
&& matchesCurrentRoute(job, currentSelections, 'worker')
|
|
112
|
-
&& verifiedHubJob(job)
|
|
113
|
-
&& (Number(job?.attempt ?? job?.selection_trace?.logical_attempt ?? 0) > 0));
|
|
114
|
-
const reviewerPassed = jobs.some((job) => job?.role === 'reviewer'
|
|
115
|
-
&& matchesCurrentRoute(job, currentSelections, 'reviewer')
|
|
116
|
-
&& verifiedHubJob(job)
|
|
117
|
-
&& job?.review_verdict === 'approve');
|
|
118
|
-
return [
|
|
119
|
-
workerPassed
|
|
120
|
-
? { id: 'model_execution', status: 'PASS', reason_code: 'REAL_EXECUTION_PASSED', evidence_source: 'hub-jobs' }
|
|
121
|
-
: { id: 'model_execution', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
122
|
-
workerPrimaryPassed
|
|
123
|
-
? { id: 'worker_primary_callable', status: 'PASS', reason_code: 'WORKER_PRIMARY_CALLABLE', evidence_source: 'hub-jobs' }
|
|
124
|
-
: { id: 'worker_primary_callable', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
125
|
-
workerEscalationPassed
|
|
126
|
-
? { id: 'worker_escalation_callable', status: 'PASS', reason_code: 'WORKER_ESCALATION_CALLABLE', evidence_source: 'hub-jobs' }
|
|
127
|
-
: { id: 'worker_escalation_callable', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
128
|
-
reviewerPassed
|
|
129
|
-
? { id: 'reviewer_primary_callable', status: 'PASS', reason_code: 'REVIEWER_PRIMARY_CALLABLE', evidence_source: 'hub-jobs' }
|
|
130
|
-
: { id: 'reviewer_primary_callable', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
131
|
-
{ id: 'provider_lifecycle_consistent', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
132
|
-
reviewerPassed
|
|
133
|
-
? { id: 'reviewer_pipeline', status: 'PASS', reason_code: 'REAL_REVIEW_PASSED', evidence_source: 'hub-jobs' }
|
|
134
|
-
: { id: 'reviewer_pipeline', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
135
|
-
];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Enrich the conservative runtime matrix with evidence the config report has
|
|
140
|
-
* already collected. This function performs no I/O and never reads provider
|
|
141
|
-
* configuration, credentials, quotas, pricing, or hidden catalog expectations.
|
|
142
|
-
*/
|
|
143
|
-
export function buildConfigReadinessMatrix({
|
|
144
|
-
platform = process.platform,
|
|
145
|
-
hubCompatibility = null,
|
|
146
|
-
workerProviderMode = null,
|
|
147
|
-
providerCatalogChecked = false,
|
|
148
|
-
providerCatalogBody = null,
|
|
149
|
-
providerInventoryChecked = false,
|
|
150
|
-
providerInventoryBody = null,
|
|
151
|
-
providerHealthChecked = false,
|
|
152
|
-
providerHealthBody = null,
|
|
153
|
-
hubJobsChecked = false,
|
|
154
|
-
hubJobsBody = null,
|
|
155
|
-
currentSelections = null,
|
|
156
|
-
} = {}) {
|
|
157
|
-
const warnings = warningCodes(providerCatalogBody);
|
|
158
|
-
const catalogResponseOk = !!providerCatalogBody
|
|
159
|
-
&& typeof providerCatalogBody === 'object'
|
|
160
|
-
&& providerCatalogBody.ok !== false;
|
|
161
|
-
const catalogOk = providerCatalogChecked && catalogResponseOk && warnings.length === 0;
|
|
162
|
-
|
|
163
|
-
const evidence = {};
|
|
164
|
-
const hubJobs = hubCompatibility?.compatible === true
|
|
165
|
-
&& hubJobsChecked
|
|
166
|
-
&& hubJobsBody?.ok !== false
|
|
167
|
-
&& Array.isArray(hubJobsBody?.jobs)
|
|
168
|
-
? hubJobsBody.jobs
|
|
169
|
-
: [];
|
|
170
|
-
for (const row of buildHubExecutionRows(hubJobs, { currentSelections })) {
|
|
171
|
-
if (row.status === 'PASS') {
|
|
172
|
-
evidence[row.id] = {
|
|
173
|
-
status: row.status,
|
|
174
|
-
reason_code: row.reason_code,
|
|
175
|
-
evidence_source: row.evidence_source,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if (
|
|
180
|
-
workerProviderMode !== 'deepseek-official'
|
|
181
|
-
&& hubCompatibility?.compatible === true
|
|
182
|
-
&& providerCatalogChecked
|
|
183
|
-
&& catalogResponseOk
|
|
184
|
-
&& warnings.length > 0
|
|
185
|
-
) {
|
|
186
|
-
evidence.provider_catalog = {
|
|
187
|
-
status: 'FAIL',
|
|
188
|
-
reason_code: READINESS_REASON_CODES.PROVIDER_CATALOG_HEALTH_WARNING,
|
|
189
|
-
evidence_source: 'harness-catalog',
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (providerInventoryChecked) {
|
|
194
|
-
evidence.provider_lifecycle_consistent = structurallyConsistentProviderInventory(providerInventoryBody, currentSelections)
|
|
195
|
-
? { status: 'PASS', reason_code: READINESS_REASON_CODES.PROVIDER_LIFECYCLE_CONSISTENT, evidence_source: 'provider-inventory' }
|
|
196
|
-
: {
|
|
197
|
-
status: 'FAIL',
|
|
198
|
-
reason_code: providerInventoryBody?.ok === false
|
|
199
|
-
? READINESS_REASON_CODES.PROVIDER_INVENTORY_UNAVAILABLE
|
|
200
|
-
: READINESS_REASON_CODES.PROVIDER_LIFECYCLE_INCONSISTENT,
|
|
201
|
-
evidence_source: 'provider-inventory',
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
for (const [role, rowId] of [['worker', 'provider_health'], ['reviewer', 'reviewer_health']]) {
|
|
206
|
-
const healthEvidence = currentRouteHealthEvidence({ role, currentSelections, providerHealthChecked, providerHealthBody });
|
|
207
|
-
if (healthEvidence) evidence[rowId] = healthEvidence;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const matrix = buildReadinessMatrix({
|
|
211
|
-
platform,
|
|
212
|
-
hubCompatibility,
|
|
213
|
-
workerProviderMode,
|
|
214
|
-
providerCatalogChecked,
|
|
215
|
-
providerCatalogOk: catalogOk,
|
|
216
|
-
evidence,
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
if (warnings.length === 0) return matrix;
|
|
220
|
-
return {
|
|
221
|
-
...matrix,
|
|
222
|
-
rows: matrix.rows.map((row) => row.id === 'provider_catalog'
|
|
223
|
-
? { ...row, detail_codes: warnings }
|
|
224
|
-
: row),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
1
|
+
import { buildReadinessMatrix, READINESS_REASON_CODES } from './readiness-matrix.mjs';
|
|
2
|
+
import { PRODUCTION_EXECUTION_PLANE, PRODUCTION_PROFILE, PRODUCTION_LISTEN_PORT } from './runtime-identity.mjs';
|
|
3
|
+
|
|
4
|
+
function warningCodes(catalogBody) {
|
|
5
|
+
const hints = Array.isArray(catalogBody?.health?.hints) ? catalogBody.health.hints : [];
|
|
6
|
+
return [...new Set(hints
|
|
7
|
+
.filter((hint) => hint?.level === 'warning' && typeof hint?.code === 'string' && hint.code.trim())
|
|
8
|
+
.map((hint) => hint.code.trim()))];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function verifiedHubJob(job) {
|
|
12
|
+
const context = job?.execution_context;
|
|
13
|
+
return job?.status === 'done'
|
|
14
|
+
&& job?.task_status === 'success'
|
|
15
|
+
&& job?.delivery_complete === true
|
|
16
|
+
&& job?.workspace_evidence_ok === true
|
|
17
|
+
&& context?.execution_plane === PRODUCTION_EXECUTION_PLANE
|
|
18
|
+
&& context?.profile === PRODUCTION_PROFILE
|
|
19
|
+
&& Number(context?.listen_port) === PRODUCTION_LISTEN_PORT
|
|
20
|
+
&& typeof context?.runtime_id === 'string'
|
|
21
|
+
&& context.runtime_id.trim().length > 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasBlockingProviderRecovery(body, currentSelections) {
|
|
25
|
+
if (!Object.hasOwn(body, 'recovery_transactions')) return false;
|
|
26
|
+
if (!Array.isArray(body.recovery_transactions)) return true;
|
|
27
|
+
const selectedProviders = new Set(Object.values(currentSelections ?? {})
|
|
28
|
+
.filter((selection) => selection?.ok === true && typeof selection.provider === 'string')
|
|
29
|
+
.map((selection) => selection.provider));
|
|
30
|
+
return body.recovery_transactions.some((transaction) => transaction?.unresolved === true
|
|
31
|
+
|| typeof transaction?.provider_id !== 'string'
|
|
32
|
+
|| selectedProviders.has(transaction.provider_id));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function structurallyConsistentProviderInventory(body, currentSelections) {
|
|
36
|
+
if (!body || typeof body !== 'object' || body.ok === false || !Array.isArray(body.records)) return false;
|
|
37
|
+
if ('lifecycle_evidence' in body && body.lifecycle_evidence?.ok !== true) return false;
|
|
38
|
+
if ('declaration_evidence' in body && body.declaration_evidence?.ok !== true) return false;
|
|
39
|
+
if ('default_evidence' in body && body.default_evidence?.ok !== true) return false;
|
|
40
|
+
if (hasBlockingProviderRecovery(body, currentSelections)) return false;
|
|
41
|
+
return body.records.every((record) => {
|
|
42
|
+
if (!record || typeof record.id !== 'string' || record.id.trim() === '') return false;
|
|
43
|
+
if (!['present', 'absent'].includes(record.desired_state)) return false;
|
|
44
|
+
if (!record.lifecycle || typeof record.lifecycle !== 'object' || Array.isArray(record.lifecycle)) return false;
|
|
45
|
+
for (const field of ['installed', 'configured', 'enabled', 'catalogued']) {
|
|
46
|
+
if (typeof record.lifecycle[field] !== 'boolean') return false;
|
|
47
|
+
}
|
|
48
|
+
if (record.desired_state === 'absent' && record.lifecycle.enabled === true) return false;
|
|
49
|
+
return true;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function matchesCurrentRoute(job, currentSelections, role) {
|
|
54
|
+
if (!currentSelections) return true;
|
|
55
|
+
const selectionKey = role === 'worker'
|
|
56
|
+
&& Number(job?.attempt ?? job?.selection_trace?.logical_attempt ?? 0) > 0
|
|
57
|
+
&& Object.hasOwn(currentSelections, 'worker_escalation')
|
|
58
|
+
? 'worker_escalation'
|
|
59
|
+
: role;
|
|
60
|
+
if (!Object.hasOwn(currentSelections, selectionKey)) return true;
|
|
61
|
+
const selected = currentSelections[selectionKey];
|
|
62
|
+
return selected?.ok === true
|
|
63
|
+
&& job?.provider === selected.provider
|
|
64
|
+
&& job?.model === selected.model;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function safeDetailCode(value, fallback) {
|
|
68
|
+
return typeof value === 'string' && /^[A-Z][A-Z0-9_]{0,127}$/u.test(value) ? value : fallback;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function currentRouteHealthEvidence({ role, currentSelections, providerHealthChecked, providerHealthBody } = {}) {
|
|
72
|
+
if (!currentSelections || !Object.hasOwn(currentSelections, role)) return null;
|
|
73
|
+
const selected = currentSelections[role];
|
|
74
|
+
if (selected?.ok === false) {
|
|
75
|
+
return {
|
|
76
|
+
status: 'FAIL',
|
|
77
|
+
reason_code: 'PROVIDER_ROUTE_UNCALLABLE',
|
|
78
|
+
evidence_source: 'model-routing',
|
|
79
|
+
detail_code: safeDetailCode(selected.code, 'MODEL_ROUTE_UNAVAILABLE'),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!selected?.provider || !selected?.model || providerHealthChecked !== true) return null;
|
|
83
|
+
if (providerHealthBody?.ok === false) {
|
|
84
|
+
return { status: 'FAIL', reason_code: 'PROVIDER_HEALTH_UNAVAILABLE', evidence_source: 'provider-health' };
|
|
85
|
+
}
|
|
86
|
+
const health = Array.isArray(providerHealthBody?.health) ? providerHealthBody.health : [];
|
|
87
|
+
const observation = health.find((entry) => entry?.provider === selected.provider
|
|
88
|
+
&& entry?.model === selected.model
|
|
89
|
+
&& entry?.fresh === true);
|
|
90
|
+
if (!observation || observation.state === 'unprobed') return null;
|
|
91
|
+
if (observation.state === 'callable') {
|
|
92
|
+
return { status: 'PASS', reason_code: 'PROVIDER_ROUTE_CALLABLE', evidence_source: 'provider-health' };
|
|
93
|
+
}
|
|
94
|
+
const stateCode = String(observation.state ?? '').toUpperCase().replaceAll('-', '_');
|
|
95
|
+
return {
|
|
96
|
+
status: 'FAIL',
|
|
97
|
+
reason_code: 'PROVIDER_ROUTE_UNCALLABLE',
|
|
98
|
+
evidence_source: 'provider-health',
|
|
99
|
+
detail_code: safeDetailCode(observation.reason_code, safeDetailCode(stateCode, 'PROVIDER_UNCALLABLE')),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function buildHubExecutionRows(hubJobs = [], { currentSelections = null } = {}) {
|
|
104
|
+
const jobs = Array.isArray(hubJobs) ? hubJobs : [];
|
|
105
|
+
const workerPassed = jobs.some((job) => job?.role === 'worker' && matchesCurrentRoute(job, currentSelections, 'worker') && verifiedHubJob(job));
|
|
106
|
+
const workerPrimaryPassed = jobs.some((job) => job?.role === 'worker'
|
|
107
|
+
&& matchesCurrentRoute(job, currentSelections, 'worker')
|
|
108
|
+
&& verifiedHubJob(job)
|
|
109
|
+
&& Number(job?.attempt ?? job?.selection_trace?.logical_attempt ?? 0) === 0);
|
|
110
|
+
const workerEscalationPassed = jobs.some((job) => job?.role === 'worker'
|
|
111
|
+
&& matchesCurrentRoute(job, currentSelections, 'worker')
|
|
112
|
+
&& verifiedHubJob(job)
|
|
113
|
+
&& (Number(job?.attempt ?? job?.selection_trace?.logical_attempt ?? 0) > 0));
|
|
114
|
+
const reviewerPassed = jobs.some((job) => job?.role === 'reviewer'
|
|
115
|
+
&& matchesCurrentRoute(job, currentSelections, 'reviewer')
|
|
116
|
+
&& verifiedHubJob(job)
|
|
117
|
+
&& job?.review_verdict === 'approve');
|
|
118
|
+
return [
|
|
119
|
+
workerPassed
|
|
120
|
+
? { id: 'model_execution', status: 'PASS', reason_code: 'REAL_EXECUTION_PASSED', evidence_source: 'hub-jobs' }
|
|
121
|
+
: { id: 'model_execution', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
122
|
+
workerPrimaryPassed
|
|
123
|
+
? { id: 'worker_primary_callable', status: 'PASS', reason_code: 'WORKER_PRIMARY_CALLABLE', evidence_source: 'hub-jobs' }
|
|
124
|
+
: { id: 'worker_primary_callable', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
125
|
+
workerEscalationPassed
|
|
126
|
+
? { id: 'worker_escalation_callable', status: 'PASS', reason_code: 'WORKER_ESCALATION_CALLABLE', evidence_source: 'hub-jobs' }
|
|
127
|
+
: { id: 'worker_escalation_callable', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
128
|
+
reviewerPassed
|
|
129
|
+
? { id: 'reviewer_primary_callable', status: 'PASS', reason_code: 'REVIEWER_PRIMARY_CALLABLE', evidence_source: 'hub-jobs' }
|
|
130
|
+
: { id: 'reviewer_primary_callable', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
131
|
+
{ id: 'provider_lifecycle_consistent', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
132
|
+
reviewerPassed
|
|
133
|
+
? { id: 'reviewer_pipeline', status: 'PASS', reason_code: 'REAL_REVIEW_PASSED', evidence_source: 'hub-jobs' }
|
|
134
|
+
: { id: 'reviewer_pipeline', status: 'NOT_RUN', reason_code: 'NO_EXECUTION_EVIDENCE', evidence_source: 'none' },
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Enrich the conservative runtime matrix with evidence the config report has
|
|
140
|
+
* already collected. This function performs no I/O and never reads provider
|
|
141
|
+
* configuration, credentials, quotas, pricing, or hidden catalog expectations.
|
|
142
|
+
*/
|
|
143
|
+
export function buildConfigReadinessMatrix({
|
|
144
|
+
platform = process.platform,
|
|
145
|
+
hubCompatibility = null,
|
|
146
|
+
workerProviderMode = null,
|
|
147
|
+
providerCatalogChecked = false,
|
|
148
|
+
providerCatalogBody = null,
|
|
149
|
+
providerInventoryChecked = false,
|
|
150
|
+
providerInventoryBody = null,
|
|
151
|
+
providerHealthChecked = false,
|
|
152
|
+
providerHealthBody = null,
|
|
153
|
+
hubJobsChecked = false,
|
|
154
|
+
hubJobsBody = null,
|
|
155
|
+
currentSelections = null,
|
|
156
|
+
} = {}) {
|
|
157
|
+
const warnings = warningCodes(providerCatalogBody);
|
|
158
|
+
const catalogResponseOk = !!providerCatalogBody
|
|
159
|
+
&& typeof providerCatalogBody === 'object'
|
|
160
|
+
&& providerCatalogBody.ok !== false;
|
|
161
|
+
const catalogOk = providerCatalogChecked && catalogResponseOk && warnings.length === 0;
|
|
162
|
+
|
|
163
|
+
const evidence = {};
|
|
164
|
+
const hubJobs = hubCompatibility?.compatible === true
|
|
165
|
+
&& hubJobsChecked
|
|
166
|
+
&& hubJobsBody?.ok !== false
|
|
167
|
+
&& Array.isArray(hubJobsBody?.jobs)
|
|
168
|
+
? hubJobsBody.jobs
|
|
169
|
+
: [];
|
|
170
|
+
for (const row of buildHubExecutionRows(hubJobs, { currentSelections })) {
|
|
171
|
+
if (row.status === 'PASS') {
|
|
172
|
+
evidence[row.id] = {
|
|
173
|
+
status: row.status,
|
|
174
|
+
reason_code: row.reason_code,
|
|
175
|
+
evidence_source: row.evidence_source,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (
|
|
180
|
+
workerProviderMode !== 'deepseek-official'
|
|
181
|
+
&& hubCompatibility?.compatible === true
|
|
182
|
+
&& providerCatalogChecked
|
|
183
|
+
&& catalogResponseOk
|
|
184
|
+
&& warnings.length > 0
|
|
185
|
+
) {
|
|
186
|
+
evidence.provider_catalog = {
|
|
187
|
+
status: 'FAIL',
|
|
188
|
+
reason_code: READINESS_REASON_CODES.PROVIDER_CATALOG_HEALTH_WARNING,
|
|
189
|
+
evidence_source: 'harness-catalog',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (providerInventoryChecked) {
|
|
194
|
+
evidence.provider_lifecycle_consistent = structurallyConsistentProviderInventory(providerInventoryBody, currentSelections)
|
|
195
|
+
? { status: 'PASS', reason_code: READINESS_REASON_CODES.PROVIDER_LIFECYCLE_CONSISTENT, evidence_source: 'provider-inventory' }
|
|
196
|
+
: {
|
|
197
|
+
status: 'FAIL',
|
|
198
|
+
reason_code: providerInventoryBody?.ok === false
|
|
199
|
+
? READINESS_REASON_CODES.PROVIDER_INVENTORY_UNAVAILABLE
|
|
200
|
+
: READINESS_REASON_CODES.PROVIDER_LIFECYCLE_INCONSISTENT,
|
|
201
|
+
evidence_source: 'provider-inventory',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (const [role, rowId] of [['worker', 'provider_health'], ['reviewer', 'reviewer_health']]) {
|
|
206
|
+
const healthEvidence = currentRouteHealthEvidence({ role, currentSelections, providerHealthChecked, providerHealthBody });
|
|
207
|
+
if (healthEvidence) evidence[rowId] = healthEvidence;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const matrix = buildReadinessMatrix({
|
|
211
|
+
platform,
|
|
212
|
+
hubCompatibility,
|
|
213
|
+
workerProviderMode,
|
|
214
|
+
providerCatalogChecked,
|
|
215
|
+
providerCatalogOk: catalogOk,
|
|
216
|
+
evidence,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (warnings.length === 0) return matrix;
|
|
220
|
+
return {
|
|
221
|
+
...matrix,
|
|
222
|
+
rows: matrix.rows.map((row) => row.id === 'provider_catalog'
|
|
223
|
+
? { ...row, detail_codes: warnings }
|
|
224
|
+
: row),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
// Credential references are identifiers, never credential values. Keep this
|
|
2
|
-
// boundary strict because provider metadata is returned to the browser/UI.
|
|
3
|
-
|
|
4
|
-
const ENV_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/u;
|
|
5
|
-
const HANDLE_REFERENCE = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/u;
|
|
6
|
-
const SECRET_LIKE = /^(?:sk|pk|rk|sess|tok|token|key|secret)[_-]/iu;
|
|
7
|
-
const EMBEDDED_SECRET = /(?:^|\s)(?:bearer|basic)\s+|(?:https?|data):|=/iu;
|
|
8
|
-
|
|
9
|
-
function rawText(value) {
|
|
10
|
-
if (typeof value === 'string') return value.trim() || null;
|
|
11
|
-
if (value && typeof value === 'object') {
|
|
12
|
-
const candidate = value.name_or_handle ?? value.name ?? value.reference;
|
|
13
|
-
return typeof candidate === 'string' ? candidate.trim() || null : null;
|
|
14
|
-
}
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Classify a reference without returning an unsafe value. `kind: env` is the
|
|
20
|
-
* common apiKeyEnv form; non-env stores may use a bounded opaque handle.
|
|
21
|
-
*/
|
|
22
|
-
export function classifyCredentialReference(value, { kind = 'env' } = {}) {
|
|
23
|
-
const raw = rawText(value);
|
|
24
|
-
if (!raw) return { present: false, value: null, redacted: false };
|
|
25
|
-
const normalizedKind = typeof kind === 'string' ? kind.trim().toLowerCase() : 'env';
|
|
26
|
-
const valid = normalizedKind === 'env'
|
|
27
|
-
? ENV_REFERENCE.test(raw) && !SECRET_LIKE.test(raw) && !EMBEDDED_SECRET.test(raw)
|
|
28
|
-
: HANDLE_REFERENCE.test(raw) && !SECRET_LIKE.test(raw) && !EMBEDDED_SECRET.test(raw);
|
|
29
|
-
return { present: true, value: valid ? raw : null, redacted: !valid };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function sanitizeCredentialReference(value, options = {}) {
|
|
33
|
-
return classifyCredentialReference(value, options).value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function hasCredentialReference(value, options = {}) {
|
|
37
|
-
return classifyCredentialReference(value, options).present;
|
|
38
|
-
}
|
|
1
|
+
// Credential references are identifiers, never credential values. Keep this
|
|
2
|
+
// boundary strict because provider metadata is returned to the browser/UI.
|
|
3
|
+
|
|
4
|
+
const ENV_REFERENCE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/u;
|
|
5
|
+
const HANDLE_REFERENCE = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/u;
|
|
6
|
+
const SECRET_LIKE = /^(?:sk|pk|rk|sess|tok|token|key|secret)[_-]/iu;
|
|
7
|
+
const EMBEDDED_SECRET = /(?:^|\s)(?:bearer|basic)\s+|(?:https?|data):|=/iu;
|
|
8
|
+
|
|
9
|
+
function rawText(value) {
|
|
10
|
+
if (typeof value === 'string') return value.trim() || null;
|
|
11
|
+
if (value && typeof value === 'object') {
|
|
12
|
+
const candidate = value.name_or_handle ?? value.name ?? value.reference;
|
|
13
|
+
return typeof candidate === 'string' ? candidate.trim() || null : null;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Classify a reference without returning an unsafe value. `kind: env` is the
|
|
20
|
+
* common apiKeyEnv form; non-env stores may use a bounded opaque handle.
|
|
21
|
+
*/
|
|
22
|
+
export function classifyCredentialReference(value, { kind = 'env' } = {}) {
|
|
23
|
+
const raw = rawText(value);
|
|
24
|
+
if (!raw) return { present: false, value: null, redacted: false };
|
|
25
|
+
const normalizedKind = typeof kind === 'string' ? kind.trim().toLowerCase() : 'env';
|
|
26
|
+
const valid = normalizedKind === 'env'
|
|
27
|
+
? ENV_REFERENCE.test(raw) && !SECRET_LIKE.test(raw) && !EMBEDDED_SECRET.test(raw)
|
|
28
|
+
: HANDLE_REFERENCE.test(raw) && !SECRET_LIKE.test(raw) && !EMBEDDED_SECRET.test(raw);
|
|
29
|
+
return { present: true, value: valid ? raw : null, redacted: !valid };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function sanitizeCredentialReference(value, options = {}) {
|
|
33
|
+
return classifyCredentialReference(value, options).value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function hasCredentialReference(value, options = {}) {
|
|
37
|
+
return classifyCredentialReference(value, options).present;
|
|
38
|
+
}
|