@skrr-ai/cli 0.1.94 → 0.1.96
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 +175 -165
- package/bin/run.js +32 -0
- package/bin/update-npm-runner.cjs +33 -0
- package/bin/update-session.cjs +194 -0
- package/bin/update-worker.cjs +208 -0
- package/dist/base-command.js +0 -15
- package/dist/commands/agent/message/send.d.ts +2 -0
- package/dist/commands/agent/message/send.js +75 -5
- package/dist/commands/agents/chat.js +1 -1
- package/dist/commands/agents/create.d.ts +0 -3
- package/dist/commands/agents/create.js +0 -26
- package/dist/commands/agents/egress.d.ts +17 -0
- package/dist/commands/agents/egress.js +84 -0
- package/dist/commands/agents/mailbox/messages.d.ts +14 -0
- package/dist/commands/agents/mailbox/messages.js +52 -0
- package/dist/commands/agents/mailbox/show.d.ts +13 -0
- package/dist/commands/agents/mailbox/show.js +52 -0
- package/dist/commands/agents/operating-plan.js +8 -0
- package/dist/commands/agents/roles/capture.d.ts +16 -0
- package/dist/commands/agents/roles/capture.js +53 -0
- package/dist/commands/agents/roles/install.d.ts +1 -0
- package/dist/commands/agents/roles/install.js +8 -1
- package/dist/commands/agents/roles/preview.d.ts +2 -0
- package/dist/commands/agents/roles/preview.js +27 -3
- package/dist/commands/agents/update.d.ts +0 -3
- package/dist/commands/agents/update.js +1 -31
- package/dist/commands/agents/usage-cap.d.ts +4 -1
- package/dist/commands/agents/usage-cap.js +49 -10
- package/dist/commands/code/doctor.d.ts +1 -0
- package/dist/commands/code/doctor.js +6 -0
- package/dist/commands/code/index.js +4 -0
- package/dist/commands/code/install.d.ts +5 -62
- package/dist/commands/code/install.js +86 -279
- package/dist/commands/code/jobs/show.js +12 -0
- package/dist/commands/code/rollback.d.ts +8 -0
- package/dist/commands/code/rollback.js +20 -0
- package/dist/commands/code/update.d.ts +8 -37
- package/dist/commands/code/update.js +134 -213
- package/dist/commands/commitments/action-proposals/decide.d.ts +2 -0
- package/dist/commands/commitments/action-proposals/decide.js +12 -0
- package/dist/commands/commitments/action-proposals.js +7 -0
- package/dist/commands/commitments/create.d.ts +1 -0
- package/dist/commands/commitments/create.js +17 -1
- package/dist/commands/commitments/delivery.d.ts +1 -0
- package/dist/commands/commitments/delivery.js +11 -1
- package/dist/commands/commitments/doctor.js +4 -0
- package/dist/commands/commitments/effective-policy.js +26 -0
- package/dist/commands/commitments/endpoints/create.js +5 -2
- package/dist/commands/commitments/observations.d.ts +10 -0
- package/dist/commands/commitments/observations.js +88 -0
- package/dist/commands/commitments/pause.d.ts +1 -1
- package/dist/commands/commitments/pause.js +4 -3
- package/dist/commands/commitments/preflight.js +2 -1
- package/dist/commands/commitments/reports.d.ts +2 -0
- package/dist/commands/commitments/reports.js +22 -4
- package/dist/commands/commitments/update.d.ts +1 -0
- package/dist/commands/commitments/update.js +35 -5
- package/dist/commands/daemon/install.js +4 -0
- package/dist/commands/generate-recovery-code.d.ts +13 -1
- package/dist/commands/generate-recovery-code.js +146 -13
- package/dist/commands/grants/create.d.ts +1 -0
- package/dist/commands/grants/create.js +8 -0
- package/dist/commands/grants/rotate.d.ts +5 -0
- package/dist/commands/grants/rotate.js +13 -1
- package/dist/commands/grants/update.d.ts +12 -0
- package/dist/commands/grants/update.js +27 -0
- package/dist/commands/instructions/guide.d.ts +20 -0
- package/dist/commands/instructions/guide.js +84 -0
- package/dist/commands/instructions/install.js +57 -33
- package/dist/commands/instructions/setup.d.ts +14 -0
- package/dist/commands/instructions/setup.js +72 -0
- package/dist/commands/integrations/execute.js +1 -1
- package/dist/commands/login.d.ts +5 -6
- package/dist/commands/login.js +27 -11
- package/dist/commands/logout.js +7 -7
- package/dist/commands/machines/hosted/start.js +7 -1
- package/dist/commands/machines/list.js +4 -4
- package/dist/commands/measures/confirm.d.ts +1 -7
- package/dist/commands/measures/confirm.js +15 -14
- package/dist/commands/measures/list.d.ts +1 -7
- package/dist/commands/measures/list.js +1 -7
- package/dist/commands/measures/record.js +4 -4
- package/dist/commands/measures/show.d.ts +11 -0
- package/dist/commands/measures/show.js +26 -0
- package/dist/commands/northstars/governance/configure.d.ts +1 -0
- package/dist/commands/northstars/governance/configure.js +8 -1
- package/dist/commands/proposals/discard.d.ts +5 -0
- package/dist/commands/proposals/discard.js +8 -0
- package/dist/commands/proposals/keep.d.ts +5 -0
- package/dist/commands/proposals/keep.js +8 -0
- package/dist/commands/proposals/list.d.ts +15 -0
- package/dist/commands/proposals/list.js +74 -0
- package/dist/commands/proposals/show.d.ts +12 -0
- package/dist/commands/proposals/show.js +27 -0
- package/dist/commands/proposals/snooze.d.ts +5 -0
- package/dist/commands/proposals/snooze.js +8 -0
- package/dist/commands/security/overview.d.ts +11 -0
- package/dist/commands/security/overview.js +158 -0
- package/dist/commands/signals/monitors/create.d.ts +9 -0
- package/dist/commands/signals/monitors/create.js +29 -0
- package/dist/commands/signals/monitors/list.d.ts +8 -0
- package/dist/commands/signals/monitors/list.js +16 -0
- package/dist/commands/signals/monitors/pause.d.ts +11 -0
- package/dist/commands/signals/monitors/pause.js +17 -0
- package/dist/commands/signals/monitors/resume.d.ts +11 -0
- package/dist/commands/signals/monitors/resume.js +17 -0
- package/dist/commands/signals/monitors/revise.d.ts +12 -0
- package/dist/commands/signals/monitors/revise.js +25 -0
- package/dist/commands/signals/monitors/show.d.ts +11 -0
- package/dist/commands/signals/monitors/show.js +17 -0
- package/dist/commands/signals/monitors/test.d.ts +12 -0
- package/dist/commands/signals/monitors/test.js +25 -0
- package/dist/commands/signals/monitors/transitions.d.ts +11 -0
- package/dist/commands/signals/monitors/transitions.js +17 -0
- package/dist/commands/signals/monitors/validate.d.ts +9 -0
- package/dist/commands/signals/monitors/validate.js +24 -0
- package/dist/commands/signals/schemas/list.d.ts +8 -0
- package/dist/commands/signals/schemas/list.js +16 -0
- package/dist/commands/signals/schemas/show.d.ts +11 -0
- package/dist/commands/signals/schemas/show.js +17 -0
- package/dist/commands/signals/sources/list.d.ts +8 -0
- package/dist/commands/signals/sources/list.js +16 -0
- package/dist/commands/signals/sources/receipts.d.ts +11 -0
- package/dist/commands/signals/sources/receipts.js +17 -0
- package/dist/commands/signals/sources/show.d.ts +11 -0
- package/dist/commands/signals/sources/show.js +17 -0
- package/dist/commands/spaces/claim.d.ts +12 -0
- package/dist/commands/spaces/claim.js +42 -0
- package/dist/commands/spaces/ownership.d.ts +12 -0
- package/dist/commands/spaces/ownership.js +46 -0
- package/dist/commands/spaces/usage.d.ts +12 -0
- package/dist/commands/spaces/usage.js +28 -0
- package/dist/commands/spaces/workflows/run.js +3 -0
- package/dist/commands/spaces/workflows/runs.js +2 -1
- package/dist/commands/spaces/workflows/update.d.ts +6 -1
- package/dist/commands/spaces/workflows/update.js +34 -2
- package/dist/commands/tasks/cancel-cascade.js +1 -1
- package/dist/commands/tasks/cascade-status.d.ts +2 -0
- package/dist/commands/tasks/cascade-status.js +27 -1
- package/dist/commands/tasks/create-tree.d.ts +5 -1
- package/dist/commands/tasks/create-tree.js +35 -7
- package/dist/commands/tasks/create.d.ts +4 -0
- package/dist/commands/tasks/create.js +16 -0
- package/dist/commands/tasks/deliverable/add.js +12 -3
- package/dist/commands/tasks/economics-compare.d.ts +15 -0
- package/dist/commands/tasks/economics-compare.js +60 -0
- package/dist/commands/tasks/expectations/evaluate.d.ts +13 -0
- package/dist/commands/tasks/expectations/evaluate.js +43 -0
- package/dist/commands/tasks/interrupt.js +2 -2
- package/dist/commands/tasks/list.d.ts +1 -0
- package/dist/commands/tasks/list.js +44 -1
- package/dist/commands/tasks/pause-schedule.js +1 -1
- package/dist/commands/tasks/show.d.ts +5 -0
- package/dist/commands/tasks/show.js +17 -0
- package/dist/commands/tasks/update.d.ts +4 -0
- package/dist/commands/tasks/update.js +9 -0
- package/dist/commands/tasks/workflow-findings.js +2 -2
- package/dist/commands/token/list.d.ts +3 -5
- package/dist/commands/token/list.js +99 -57
- package/dist/commands/triggers/create.d.ts +2 -0
- package/dist/commands/triggers/create.js +15 -0
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +61 -0
- package/dist/commands/workspaces/grants.d.ts +12 -0
- package/dist/commands/workspaces/grants.js +31 -0
- package/dist/commands/workspaces/stewardship.d.ts +13 -0
- package/dist/commands/workspaces/stewardship.js +34 -0
- package/dist/generated/managed-instruction-guides.json +9 -0
- package/dist/hooks/init.d.ts +5 -0
- package/dist/hooks/init.js +27 -0
- package/dist/lib/agent-config.d.ts +0 -12
- package/dist/lib/agent-config.js +1 -27
- package/dist/lib/agent-mailbox.d.ts +35 -0
- package/dist/lib/agent-mailbox.js +55 -0
- package/dist/lib/commitment-endpoints.d.ts +2 -1
- package/dist/lib/commitment-endpoints.js +2 -5
- package/dist/lib/commitment-product.d.ts +3 -1
- package/dist/lib/commitment-product.js +10 -0
- package/dist/lib/commitment-terms.js +1 -1
- package/dist/lib/commitments.d.ts +17 -15
- package/dist/lib/commitments.js +173 -137
- package/dist/lib/compasses.d.ts +14 -0
- package/dist/lib/compasses.js +25 -11
- package/dist/lib/daemon-installer.d.ts +2 -0
- package/dist/lib/daemon-installer.js +100 -24
- package/dist/lib/first-party-harness-doctor.js +31 -1
- package/dist/lib/first-party-harness-remote-install.d.ts +71 -0
- package/dist/lib/first-party-harness-remote-install.js +310 -0
- package/dist/lib/first-party-harness-remote-update.d.ts +46 -0
- package/dist/lib/first-party-harness-remote-update.js +242 -0
- package/dist/lib/first-party-harness-startup-update.d.ts +26 -0
- package/dist/lib/first-party-harness-startup-update.js +166 -0
- package/dist/lib/first-party-harness-update.d.ts +46 -0
- package/dist/lib/first-party-harness-update.js +289 -0
- package/dist/lib/first-party-harness.js +7 -4
- package/dist/lib/grants.d.ts +5 -1
- package/dist/lib/grants.js +9 -1
- package/dist/lib/hosted-machines.d.ts +7 -0
- package/dist/lib/mailbox-human-command.d.ts +11 -0
- package/dist/lib/mailbox-human-command.js +41 -0
- package/dist/lib/managed-instruction-local.d.ts +12 -0
- package/dist/lib/managed-instruction-local.js +58 -0
- package/dist/lib/managed-instruction-setup.d.ts +35 -0
- package/dist/lib/managed-instruction-setup.js +306 -0
- package/dist/lib/measures.d.ts +28 -2
- package/dist/lib/measures.js +38 -14
- package/dist/lib/memory-sync.d.ts +1 -0
- package/dist/lib/memory-sync.js +279 -68
- package/dist/lib/message-intent.d.ts +6 -2
- package/dist/lib/message-intent.js +1 -0
- package/dist/lib/northstars.js +2 -1
- package/dist/lib/platform-update-local.d.ts +2 -0
- package/dist/lib/platform-update-local.js +113 -0
- package/dist/lib/platform-update.d.ts +39 -0
- package/dist/lib/platform-update.js +84 -0
- package/dist/lib/profile-machine-identity-guard.d.ts +2 -0
- package/dist/lib/profile-machine-identity-guard.js +13 -0
- package/dist/lib/prompt.d.ts +1 -0
- package/dist/lib/prompt.js +4 -3
- package/dist/lib/proposal-decision-command.d.ts +19 -0
- package/dist/lib/proposal-decision-command.js +64 -0
- package/dist/lib/proposals.d.ts +8 -0
- package/dist/lib/proposals.js +22 -0
- package/dist/lib/roles.d.ts +141 -6
- package/dist/lib/roles.js +8 -2
- package/dist/lib/signals.d.ts +20 -0
- package/dist/lib/signals.js +24 -0
- package/dist/lib/space-usage.d.ts +3 -0
- package/dist/lib/space-usage.js +17 -0
- package/dist/lib/stewardship-output.d.ts +4 -0
- package/dist/lib/stewardship-output.js +82 -0
- package/dist/lib/task-asset-upload.d.ts +3 -1
- package/dist/lib/task-asset-upload.js +1 -0
- package/dist/lib/task-batch.d.ts +9 -0
- package/dist/lib/task-batch.js +94 -17
- package/dist/lib/task-economics.d.ts +16 -0
- package/dist/lib/task-economics.js +22 -0
- package/dist/lib/task-instruction-offer.d.ts +1 -4
- package/dist/lib/task-instruction-offer.js +3 -129
- package/dist/lib/triggers.d.ts +11 -3
- package/dist/lib/triggers.js +58 -1
- package/dist/lib/update-check.d.ts +61 -53
- package/dist/lib/update-check.js +453 -314
- package/dist/lib/usage-discovery.d.ts +10 -0
- package/dist/lib/usage-discovery.js +24 -3
- package/dist/lib/workflow-run-budget.d.ts +12 -0
- package/dist/lib/workflow-run-budget.js +37 -0
- package/dist/lib/workspaces.d.ts +48 -0
- package/dist/lib/workspaces.js +8 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +14 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.d.ts +82 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.js +105 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.d.ts +110 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.js +945 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.d.ts +9 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.js +49 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +62 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +35 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.d.ts +63 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.js +169 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.js +44 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +12 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.d.ts +82 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.js +102 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.d.ts +110 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.js +923 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.d.ts +9 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.js +44 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +59 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +35 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.d.ts +63 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.js +163 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.js +41 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +41 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +8069 -7121
- package/docs/v1/README.md +28 -0
- package/docs/v1/api.md +55 -0
- package/docs/v1/changelog.md +16 -0
- package/docs/v1/check-examples.mjs +178 -0
- package/docs/v1/examples/api-readonly.mjs +144 -0
- package/docs/v1/operations.md +87 -0
- package/docs/v1/quickstart.md +71 -0
- package/oclif.manifest.json +49424 -46174
- package/package.json +34 -11
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** GitHub push diagnostics must never escape this parser as raw stderr. */
|
|
2
|
+
export declare const GITHUB_WORKFLOW_PERMISSION_REQUIRED = "github_workflow_permission_required";
|
|
3
|
+
export type GitHubPushRefusal = {
|
|
4
|
+
code: typeof GITHUB_WORKFLOW_PERMISSION_REQUIRED;
|
|
5
|
+
tokenKind: 'github_app' | 'oauth_app' | 'personal_access_token' | 'unknown';
|
|
6
|
+
paths: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare function isWorkflowPath(value: unknown): boolean;
|
|
9
|
+
export declare function classifyGitHubPushRefusal(text: unknown): GitHubPushRefusal | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GITHUB_WORKFLOW_PERMISSION_REQUIRED = void 0;
|
|
4
|
+
exports.isWorkflowPath = isWorkflowPath;
|
|
5
|
+
exports.classifyGitHubPushRefusal = classifyGitHubPushRefusal;
|
|
6
|
+
/** GitHub push diagnostics must never escape this parser as raw stderr. */
|
|
7
|
+
exports.GITHUB_WORKFLOW_PERMISSION_REQUIRED = 'github_workflow_permission_required';
|
|
8
|
+
function workflowPath(value) {
|
|
9
|
+
if (typeof value !== 'string' || value.length > 240)
|
|
10
|
+
return null;
|
|
11
|
+
const path = value.replaceAll('\\', '/');
|
|
12
|
+
if (!/^\.github\/workflows\/[A-Za-z0-9_. /-]+$/.test(path))
|
|
13
|
+
return null;
|
|
14
|
+
const parts = path.split('/');
|
|
15
|
+
if (parts.some((part) => !part.trim() || part !== part.trim() || part === '.' || part === '..'))
|
|
16
|
+
return null;
|
|
17
|
+
return path;
|
|
18
|
+
}
|
|
19
|
+
function isWorkflowPath(value) {
|
|
20
|
+
return workflowPath(value) !== null;
|
|
21
|
+
}
|
|
22
|
+
function classifyGitHubPushRefusal(text) {
|
|
23
|
+
if (typeof text !== 'string')
|
|
24
|
+
return null;
|
|
25
|
+
// Bound parsing work. Git prints the rejection LAST, after any amount of
|
|
26
|
+
// progress output, so keep the tail; truncated diagnostics keep the generic refusal.
|
|
27
|
+
const input = text.slice(-64 * 1024);
|
|
28
|
+
const pattern = /refusing to allow (?:a|an) (GitHub App|OAuth App|Personal Access Token) to create or update workflow [`'"]([^`'"\r\n]+)[`'"] without [`'"]workflows?[`'"] (?:permission|scope)/gi;
|
|
29
|
+
const paths = [];
|
|
30
|
+
let tokenKind = 'unknown';
|
|
31
|
+
for (const match of input.matchAll(pattern)) {
|
|
32
|
+
const path = workflowPath(match[2]);
|
|
33
|
+
if (!path)
|
|
34
|
+
continue;
|
|
35
|
+
if (tokenKind === 'unknown') {
|
|
36
|
+
tokenKind =
|
|
37
|
+
match[1].toLowerCase() === 'github app'
|
|
38
|
+
? 'github_app'
|
|
39
|
+
: match[1].toLowerCase() === 'oauth app'
|
|
40
|
+
? 'oauth_app'
|
|
41
|
+
: 'personal_access_token';
|
|
42
|
+
}
|
|
43
|
+
if (!paths.includes(path))
|
|
44
|
+
paths.push(path);
|
|
45
|
+
if (paths.length === 10)
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return paths.length ? { code: exports.GITHUB_WORKFLOW_PERMISSION_REQUIRED, tokenKind, paths } : null;
|
|
49
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only capability wire for ordinary agent turns. This is NOT a session
|
|
3
|
+
* authorization request or an effective permission profile. In particular,
|
|
4
|
+
* an ACP approval callback is not a process sandbox, and access to a loopback
|
|
5
|
+
* inference broker is not a grant to make arbitrary outbound connections.
|
|
6
|
+
*
|
|
7
|
+
* Daemons advertise what their adapters implement; execution still validates
|
|
8
|
+
* the requested policy independently. Consumers never infer controls from a
|
|
9
|
+
* provider name, a missing report, or the unified permission-mode vocabulary.
|
|
10
|
+
*/
|
|
11
|
+
export interface HarnessPermissionControls {
|
|
12
|
+
version: 1;
|
|
13
|
+
plan: 'harness' | 'reported-actions' | 'unsupported' | 'unknown';
|
|
14
|
+
approval: 'harness' | 'reported-actions' | 'none' | 'unknown';
|
|
15
|
+
filesystem: 'harness-sandbox' | 'tool-path-guards' | 'reported-actions' | 'not-restricted-by-daemon' | 'unknown';
|
|
16
|
+
strictToolPolicy: {
|
|
17
|
+
modes: readonly ('none' | 'allowlist')[];
|
|
18
|
+
enforcement: 'harness' | 'negotiated' | 'unsupported' | 'unknown';
|
|
19
|
+
};
|
|
20
|
+
organizationToolDenylist: 'unsupported' | 'unknown';
|
|
21
|
+
networkEgress: 'not-restricted-by-daemon' | 'unknown';
|
|
22
|
+
}
|
|
23
|
+
export interface HarnessPermissionControlReport {
|
|
24
|
+
controls: HarnessPermissionControls | null;
|
|
25
|
+
source: 'daemon' | 'daemon_record' | 'unreported';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Presence/registration metadata is untrusted input. Copy only the v1 enum
|
|
29
|
+
* vocabulary: unknown future claims and malformed profiles yield no report,
|
|
30
|
+
* rather than a green badge. Additive fields are stripped for compatibility.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseHarnessPermissionControls(value: unknown): HarnessPermissionControls | null;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseHarnessPermissionControls = parseHarnessPermissionControls;
|
|
4
|
+
const PLAN = ['harness', 'reported-actions', 'unsupported', 'unknown'];
|
|
5
|
+
const APPROVAL = ['harness', 'reported-actions', 'none', 'unknown'];
|
|
6
|
+
const FILESYSTEM = [
|
|
7
|
+
'harness-sandbox',
|
|
8
|
+
'tool-path-guards',
|
|
9
|
+
'reported-actions',
|
|
10
|
+
'not-restricted-by-daemon',
|
|
11
|
+
'unknown',
|
|
12
|
+
];
|
|
13
|
+
const TOOL_ENFORCEMENT = ['harness', 'negotiated', 'unsupported', 'unknown'];
|
|
14
|
+
/**
|
|
15
|
+
* Presence/registration metadata is untrusted input. Copy only the v1 enum
|
|
16
|
+
* vocabulary: unknown future claims and malformed profiles yield no report,
|
|
17
|
+
* rather than a green badge. Additive fields are stripped for compatibility.
|
|
18
|
+
*/
|
|
19
|
+
function parseHarnessPermissionControls(value) {
|
|
20
|
+
try {
|
|
21
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
22
|
+
return null;
|
|
23
|
+
const row = value;
|
|
24
|
+
const { version, plan, approval, filesystem, organizationToolDenylist, networkEgress, strictToolPolicy, } = row;
|
|
25
|
+
const strict = strictToolPolicy;
|
|
26
|
+
if (!strict || typeof strict !== 'object' || Array.isArray(strict))
|
|
27
|
+
return null;
|
|
28
|
+
const strictModes = strict.modes;
|
|
29
|
+
const enforcement = strict.enforcement;
|
|
30
|
+
if (version !== 1 ||
|
|
31
|
+
!PLAN.includes(plan) ||
|
|
32
|
+
!APPROVAL.includes(approval) ||
|
|
33
|
+
!FILESYSTEM.includes(filesystem) ||
|
|
34
|
+
!['unsupported', 'unknown'].includes(organizationToolDenylist) ||
|
|
35
|
+
!['not-restricted-by-daemon', 'unknown'].includes(networkEgress) ||
|
|
36
|
+
!TOOL_ENFORCEMENT.includes(enforcement) ||
|
|
37
|
+
!Array.isArray(strictModes) ||
|
|
38
|
+
strictModes.length > 2 ||
|
|
39
|
+
!strictModes.every((mode) => mode === 'none' || mode === 'allowlist'))
|
|
40
|
+
return null;
|
|
41
|
+
const modes = [...new Set(strictModes)];
|
|
42
|
+
if (((enforcement === 'unsupported' || enforcement === 'unknown') && modes.length > 0) ||
|
|
43
|
+
((enforcement === 'harness' || enforcement === 'negotiated') && modes.length === 0))
|
|
44
|
+
return null;
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
version: 1,
|
|
47
|
+
plan: plan,
|
|
48
|
+
approval: approval,
|
|
49
|
+
filesystem: filesystem,
|
|
50
|
+
strictToolPolicy: Object.freeze({
|
|
51
|
+
modes: Object.freeze(modes),
|
|
52
|
+
enforcement: enforcement,
|
|
53
|
+
}),
|
|
54
|
+
organizationToolDenylist: organizationToolDenylist,
|
|
55
|
+
networkEgress: networkEgress,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Malicious accessors/proxies must not break a read surface.
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -46,3 +46,5 @@ export { OVERSKY_RELEASE_PUBLIC_KEYS, type ReleaseKey } from './releaseKeys.js';
|
|
|
46
46
|
export { MAX_SIGS, TRUSTED_FEED_ORIGINS, buildArtifactMessage, buildManifestMessage, getDeltaFor, isTrustedArtifactOrigin, verifyArtifact, verifyManifest, type DaemonVersionManifest, type DeltaDescriptor, type ManifestSignature, type PlatformArtifact, type VerifyResult, } from './releaseManifest.js';
|
|
47
47
|
export { resolveConfigRoot, configRootPath, isConfigRootOverridden } from './configRoot.js';
|
|
48
48
|
export { PROFILES_SEGMENT, SUPERSEDED_MARKER_SUFFIX, SUPERSEDED_COPY_SUFFIX, profileStateDir, legacyProfileStateDir, resolveProfileStateDir, resolveProfileStateFile, profileStateFileLocations, profileStateFilePathsToClear, consolidateProfileState, type ResolvedProfileStateDir, type ConsolidationSpec, type ConsolidationStatus, type ConsolidationOutcome, } from './profileStateDir.js';
|
|
49
|
+
export * from './harnessPermissionControls.js';
|
|
50
|
+
export { compareReleaseVersions } from './releaseVersions.js';
|
|
@@ -26,7 +26,7 @@ exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__rese
|
|
|
26
26
|
exports.jwkThumbprint = exports.generateDeviceKeyPair = exports.buildDeviceProof = exports.zeroizeKekCaches = exports.WindowsKek = exports.registerZeroizeHook = exports.MacosKek = exports.LinuxKek = exports.KekUnavailableError = exports.UnavailableKek = exports.InMemoryKek = exports.getKekStrategy = exports.__setKekStrategyForTest = exports.credEnvelopeWrap = exports.credEnvelopeUnwrap = exports.credEnvelopeSerialize = exports.credSealBuffer = exports.credOpenBuffer = exports.CRED_KEY_LEN = exports.isCredEnvelopeString = exports.generateDek = exports.CRED_ENVELOPE_MAGIC = exports.credEnvelopeEncrypt = exports.credEnvelopeDeserialize = exports.credEnvelopeDecrypt = exports.buildCredentialAad = exports.isValidRecoveryCode = exports.normalizeRecoveryCode = exports.parseRecoveryCode = exports.formatRecoveryCode = exports.MAX_TTL_DAYS = exports.MIN_TTL_DAYS = exports.parseTtlDays = exports.LoginExchangeError = exports.LoginInitError = exports.loginWithLocalhost = exports.LocalCallbackMalformedError = exports.LocalCallbackDeniedError = exports.LocalCallbackStateMismatchError = exports.LocalCallbackTimeoutError = exports.LocalCallbackBindError = exports.LocalCallbackError = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.startLocalCallback = exports.verifyChallenge = exports.safeEqual = exports.generateState = exports.challenge = exports.generateVerifier = exports.invalidateHeadersHelperCache = void 0;
|
|
27
27
|
exports.qualifyMachineUuid = exports.getMachineUuidProfile = exports.setMachineUuidProfile = exports.getHardwareMachineUuid = exports.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = exports.describeDeviceKeySplit = exports.describeDeviceIdentityState = exports.getDevicePublicKey = exports.isDeviceIdentityActive = exports.clearEnrollmentMarker = exports.enrollWithServer = exports.signServerRequest = exports.resetDeviceIdentity = exports.initDeviceIdentity = exports.__resetShutdownGuardForTest = exports.__getCredEnvelopeStateForTest = exports.__setCredEnvelopeStateForTest = exports.__wrappedDekFilePathForTest = exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = exports.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.splitKeyConvergencePolicy = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDpopProof = exports.normalizeDpopHtu = exports.isJwkThumbprint = exports.isAth = exports.createDpopKey = exports.accessTokenHash = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_TYP = exports.DPOP_PROOF_REQUIRED = exports.DPOP_PROOF_INVALID = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.verifyDeviceProof = exports.tokenHash = exports.publicJwkFromX = exports.normalizeHtu = void 0;
|
|
28
28
|
exports.LOOPBACK_DEFAULT_MAX_BODY_BYTES = exports.LoopbackTargetRefusedError = exports.isLoopbackAddress = exports.isLoopbackHostname = exports.assertLoopbackHttpUrl = exports.loopbackFetch = exports.withLoopbackNoProxy = exports.envHasProxy = exports.LOOPBACK_NO_PROXY_HOSTS = exports.isDaemonChildEnv = exports.DAEMON_CHILD_ENV = exports.SENSITIVE_ENV_PREFIX_EXCEPTIONS = exports.SENSITIVE_ENV_NAME_TOKENS = exports.SENSITIVE_ENV_SUFFIXES = exports.SENSITIVE_ENV_PREFIXES = exports.SENSITIVE_ENV_VARS = exports.sanitizeSpawnEnv = exports.harnessLoadsClaudeSkillsLayout = exports.claudeSkillsLayoutEvidence = exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = exports.tierPermitsConfiguredAutoMode = exports.credentialPolicyForTier = exports.getHarnessTier = exports.HARNESS_TIERS = exports.pairUint8ArrayToBase64Url = exports.pairUint8ArrayToBase64 = exports.parsePairUrl = exports.parsePairPlaintext = exports.parsePairBundle = exports.openPairBundleBase64 = exports.generatePairKeyPair = exports.buildPairUrl = exports.buildPairAad = exports.pairBase64UrlToUint8Array = exports.pairBase64ToUint8Array = exports.assemblePairBundleBase64 = exports.PairBundleError = exports.PAIR_SECRET_LEN = exports.PAIR_PUBKEY_LEN = exports.PAIR_NONCE_LEN = exports.PAIR_BUNDLE_MAX_BASE64_LEN = exports.PAIR_AAD_MAX_LEN = exports.PAIR_AAD_PREFIX = exports.__resetCachedDaemonIdForTest = exports.legacyDaemonUuids = exports.ensureMachineDaemonId = exports.__resetMachineUuidCacheForTest = exports.HOSTED_RUNTIME_MACHINE_SOURCES = exports.hostedRuntimeIdOverride = exports.MACHINE_IDENTITY_FILENAME = void 0;
|
|
29
|
-
exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = void 0;
|
|
29
|
+
exports.compareReleaseVersions = exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = void 0;
|
|
30
30
|
// Daemon scope vocabulary — single source of truth for non-Mongoose tiers
|
|
31
31
|
// (daemon, desktop, cli). Schema-bound canonical lives in
|
|
32
32
|
// `@skrr-ai/data-schemas/common/daemonScopes`; the two are kept byte-equal
|
|
@@ -438,3 +438,6 @@ Object.defineProperty(exports, "resolveProfileStateFile", { enumerable: true, ge
|
|
|
438
438
|
Object.defineProperty(exports, "profileStateFileLocations", { enumerable: true, get: function () { return profileStateDir_js_1.profileStateFileLocations; } });
|
|
439
439
|
Object.defineProperty(exports, "profileStateFilePathsToClear", { enumerable: true, get: function () { return profileStateDir_js_1.profileStateFilePathsToClear; } });
|
|
440
440
|
Object.defineProperty(exports, "consolidateProfileState", { enumerable: true, get: function () { return profileStateDir_js_1.consolidateProfileState; } });
|
|
441
|
+
__exportStar(require("./harnessPermissionControls.js"), exports);
|
|
442
|
+
var releaseVersions_js_1 = require("./releaseVersions.js");
|
|
443
|
+
Object.defineProperty(exports, "compareReleaseVersions", { enumerable: true, get: function () { return releaseVersions_js_1.compareReleaseVersions; } });
|
|
@@ -211,11 +211,14 @@ function setMachineUuidProfile(profile) {
|
|
|
211
211
|
function getMachineUuidProfile() {
|
|
212
212
|
return activeMachineProfile;
|
|
213
213
|
}
|
|
214
|
-
function readPersistedChoice(file, hardware, profile) {
|
|
214
|
+
function readPersistedChoice(file, hardware, profile, requireProfile = false) {
|
|
215
215
|
try {
|
|
216
216
|
const parsed = JSON.parse(node_fs_1.default.readFileSync(file, 'utf-8'));
|
|
217
217
|
// A file from another machine (a restored backup, a synced home) is absent.
|
|
218
|
-
if (parsed?.version !== 1 ||
|
|
218
|
+
if (parsed?.version !== 1 ||
|
|
219
|
+
parsed.hardware !== hardware ||
|
|
220
|
+
(parsed.profile !== undefined && parsed.profile !== profile) ||
|
|
221
|
+
(requireProfile && parsed.profile !== profile))
|
|
219
222
|
return null;
|
|
220
223
|
// Only the two values this module can produce are accepted: a hand-edited or
|
|
221
224
|
// foreign string must never become an identity.
|
|
@@ -228,11 +231,18 @@ function readPersistedChoice(file, hardware, profile) {
|
|
|
228
231
|
return null;
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
|
-
function persistChoice(dir, file, hardware, machineUuid, reason) {
|
|
234
|
+
function persistChoice(dir, file, hardware, machineUuid, reason, profile) {
|
|
232
235
|
try {
|
|
233
236
|
node_fs_1.default.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
234
237
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
235
|
-
const body = {
|
|
238
|
+
const body = {
|
|
239
|
+
version: 1,
|
|
240
|
+
hardware,
|
|
241
|
+
machineUuid,
|
|
242
|
+
profile,
|
|
243
|
+
reason,
|
|
244
|
+
decidedAt: new Date().toISOString(),
|
|
245
|
+
};
|
|
236
246
|
node_fs_1.default.writeFileSync(tmp, `${JSON.stringify(body)}\n`, { mode: 0o600 });
|
|
237
247
|
node_fs_1.default.renameSync(tmp, file);
|
|
238
248
|
}
|
|
@@ -241,6 +251,20 @@ function persistChoice(dir, file, hardware, machineUuid, reason) {
|
|
|
241
251
|
// next process decides again from the same evidence.
|
|
242
252
|
}
|
|
243
253
|
}
|
|
254
|
+
/** A nonsecret recovery copy survives product uninstall. Each profile has its
|
|
255
|
+
* own file, so concurrent profiles never overwrite a shared identity map. A
|
|
256
|
+
* copied home cannot adopt another machine's value: the hardware is verified
|
|
257
|
+
* and only this module's hardware/qualified values are admitted. */
|
|
258
|
+
function recoveryChoicePath(profile) {
|
|
259
|
+
const name = node_crypto_1.default.createHash('sha256').update(profile).digest('hex');
|
|
260
|
+
return node_path_1.default.join(node_os_1.default.homedir(), '.local', 'state', 'skrr', 'machine-identities', `${name}.json`);
|
|
261
|
+
}
|
|
262
|
+
function preserveRecoveryChoice(profile, hardware, machineUuid) {
|
|
263
|
+
const file = recoveryChoicePath(profile);
|
|
264
|
+
if (readPersistedChoice(file, hardware, profile, true) === machineUuid)
|
|
265
|
+
return;
|
|
266
|
+
persistChoice(node_path_1.default.dirname(file), file, hardware, machineUuid, 'restored_profile', profile);
|
|
267
|
+
}
|
|
244
268
|
/** The identity a NON-default profile registers under. Never throws. */
|
|
245
269
|
function resolveProfileMachineUuid(profile, hardware) {
|
|
246
270
|
const cacheKey = `${hardware}\u0000${profile}`;
|
|
@@ -250,6 +274,11 @@ function resolveProfileMachineUuid(profile, hardware) {
|
|
|
250
274
|
const dir = node_path_1.default.join((0, configRoot_js_1.resolveConfigRoot)(), 'profiles', profile);
|
|
251
275
|
const file = node_path_1.default.join(dir, exports.MACHINE_IDENTITY_FILENAME);
|
|
252
276
|
let chosen = readPersistedChoice(file, hardware, profile);
|
|
277
|
+
if (!chosen) {
|
|
278
|
+
chosen = readPersistedChoice(recoveryChoicePath(profile), hardware, profile, true);
|
|
279
|
+
if (chosen)
|
|
280
|
+
persistChoice(dir, file, hardware, chosen, 'restored_profile', profile);
|
|
281
|
+
}
|
|
253
282
|
if (!chosen) {
|
|
254
283
|
const preExisting = PRE_EXISTING_PROFILE_EVIDENCE.some((name) => {
|
|
255
284
|
try {
|
|
@@ -260,8 +289,9 @@ function resolveProfileMachineUuid(profile, hardware) {
|
|
|
260
289
|
}
|
|
261
290
|
});
|
|
262
291
|
chosen = preExisting ? hardware : qualifyMachineUuid(hardware, profile);
|
|
263
|
-
persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile');
|
|
292
|
+
persistChoice(dir, file, hardware, chosen, preExisting ? 'pre_existing_profile' : 'new_profile', profile);
|
|
264
293
|
}
|
|
294
|
+
preserveRecoveryChoice(profile, hardware, chosen);
|
|
265
295
|
profileChoiceCache.set(cacheKey, chosen);
|
|
266
296
|
return chosen;
|
|
267
297
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ReleaseKey } from './releaseKeys.js';
|
|
2
|
+
export type ReleaseChannel = 'stable' | 'canary' | 'beta' | 'internal';
|
|
3
|
+
export interface ContractRange {
|
|
4
|
+
minimum: number;
|
|
5
|
+
maximum: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ComponentReleasePolicy {
|
|
8
|
+
version: string;
|
|
9
|
+
minimumVersion?: string;
|
|
10
|
+
contracts?: Record<string, ContractRange>;
|
|
11
|
+
}
|
|
12
|
+
export interface ReleasePolicySignature {
|
|
13
|
+
keyId: string;
|
|
14
|
+
sig: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SignedReleasePolicy {
|
|
17
|
+
schemaVersion: 1;
|
|
18
|
+
releaseId: string;
|
|
19
|
+
issuedAt: string;
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
channel: ReleaseChannel;
|
|
22
|
+
/** Percentage in basis points (0–10000), avoiding floating-point policy. */
|
|
23
|
+
rolloutBasisPoints: number;
|
|
24
|
+
cohortSeed: string;
|
|
25
|
+
components: Record<string, ComponentReleasePolicy>;
|
|
26
|
+
keyId: string;
|
|
27
|
+
sig: string;
|
|
28
|
+
sigs?: ReleasePolicySignature[];
|
|
29
|
+
}
|
|
30
|
+
export type PolicyVerifyResult = {
|
|
31
|
+
ok: true;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
reason: string;
|
|
35
|
+
} | {
|
|
36
|
+
disabled: true;
|
|
37
|
+
};
|
|
38
|
+
export interface ReleasePolicyAssessment {
|
|
39
|
+
valid: boolean;
|
|
40
|
+
eligible: boolean;
|
|
41
|
+
reason?: string;
|
|
42
|
+
}
|
|
43
|
+
/** Canonical, versioned signature message. Never sign JSON.stringify directly. */
|
|
44
|
+
export declare function buildReleasePolicyMessage(policy: Omit<SignedReleasePolicy, 'sig' | 'sigs'>): string;
|
|
45
|
+
export declare function verifyReleasePolicy(policy: SignedReleasePolicy, keys?: readonly ReleaseKey[]): PolicyVerifyResult;
|
|
46
|
+
export declare function isReleasePolicyCohortEligible(policy: SignedReleasePolicy, subject: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Verify expiry, daemon target binding, contract ranges, channel, and cohort.
|
|
49
|
+
* `autonomous=false` intentionally bypasses cohort gating: an operator who
|
|
50
|
+
* explicitly runs an update may recover a machine outside the staged cohort,
|
|
51
|
+
* but cannot bypass invalid signatures, an expired policy, or incompatibility.
|
|
52
|
+
*/
|
|
53
|
+
export declare function assessReleasePolicy(args: {
|
|
54
|
+
policy: SignedReleasePolicy;
|
|
55
|
+
targetDaemonVersion: string;
|
|
56
|
+
/** Defaults to daemon for backwards-compatible daemon policy assessment. */
|
|
57
|
+
targetComponent?: string;
|
|
58
|
+
channel: ReleaseChannel;
|
|
59
|
+
contracts: Record<string, number>;
|
|
60
|
+
cohortSubject: string;
|
|
61
|
+
autonomous: boolean;
|
|
62
|
+
now?: Date;
|
|
63
|
+
}): ReleasePolicyAssessment;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildReleasePolicyMessage = buildReleasePolicyMessage;
|
|
4
|
+
exports.verifyReleasePolicy = verifyReleasePolicy;
|
|
5
|
+
exports.isReleasePolicyCohortEligible = isReleasePolicyCohortEligible;
|
|
6
|
+
exports.assessReleasePolicy = assessReleasePolicy;
|
|
7
|
+
/**
|
|
8
|
+
* Signed multi-component release policy.
|
|
9
|
+
*
|
|
10
|
+
* The daemon artifact manifest authenticates a daemon binary. This envelope
|
|
11
|
+
* authenticates the *decision* to offer that binary to a cohort, and records
|
|
12
|
+
* the component contract that makes a CLI/daemon/desktop combination safe.
|
|
13
|
+
* It is separately signed so it can be added to a v1 artifact manifest without
|
|
14
|
+
* changing the legacy manifest signature bytes or blocking old daemons.
|
|
15
|
+
*/
|
|
16
|
+
const node_crypto_1 = require("node:crypto");
|
|
17
|
+
const releaseKeys_js_1 = require("./releaseKeys.js");
|
|
18
|
+
const MAX_SIGS = 8;
|
|
19
|
+
function stableJson(value) {
|
|
20
|
+
if (value === null ||
|
|
21
|
+
typeof value === 'boolean' ||
|
|
22
|
+
typeof value === 'number' ||
|
|
23
|
+
typeof value === 'string') {
|
|
24
|
+
return JSON.stringify(value);
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(value))
|
|
27
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
28
|
+
if (!value || typeof value !== 'object')
|
|
29
|
+
throw new Error('policy contains an unsupported value');
|
|
30
|
+
const record = value;
|
|
31
|
+
return `{${Object.keys(record)
|
|
32
|
+
.sort()
|
|
33
|
+
.map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`)
|
|
34
|
+
.join(',')}}`;
|
|
35
|
+
}
|
|
36
|
+
/** Canonical, versioned signature message. Never sign JSON.stringify directly. */
|
|
37
|
+
function buildReleasePolicyMessage(policy) {
|
|
38
|
+
return ['oversky-release-policy', 'v1', stableJson(policy)].join('\n');
|
|
39
|
+
}
|
|
40
|
+
function verifyOne(message, signature, keys) {
|
|
41
|
+
if (keys.length === 0)
|
|
42
|
+
return { disabled: true };
|
|
43
|
+
if (!signature)
|
|
44
|
+
return { ok: false, reason: 'missing signature' };
|
|
45
|
+
const messageBytes = Buffer.from(message, 'utf8');
|
|
46
|
+
const signatureBytes = Buffer.from(signature, 'base64');
|
|
47
|
+
for (const keySpec of keys) {
|
|
48
|
+
try {
|
|
49
|
+
const key = (0, node_crypto_1.createPublicKey)({
|
|
50
|
+
key: Buffer.from(keySpec.publicKeySpkiB64, 'base64'),
|
|
51
|
+
format: 'der',
|
|
52
|
+
type: 'spki',
|
|
53
|
+
});
|
|
54
|
+
if ((0, node_crypto_1.verify)(null, messageBytes, key, signatureBytes))
|
|
55
|
+
return { ok: true };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* malformed signatures and keys are a verification failure */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { ok: false, reason: 'signature did not verify against any pinned key' };
|
|
62
|
+
}
|
|
63
|
+
function policyShapeIsValid(policy) {
|
|
64
|
+
if (!policy ||
|
|
65
|
+
typeof policy !== 'object' ||
|
|
66
|
+
policy.schemaVersion !== 1 ||
|
|
67
|
+
!/^[0-9A-Za-z._:-]+$/.test(policy.releaseId) ||
|
|
68
|
+
!['stable', 'canary', 'beta', 'internal'].includes(policy.channel) ||
|
|
69
|
+
!Number.isInteger(policy.rolloutBasisPoints) ||
|
|
70
|
+
policy.rolloutBasisPoints < 0 ||
|
|
71
|
+
policy.rolloutBasisPoints > 10_000 ||
|
|
72
|
+
typeof policy.cohortSeed !== 'string' ||
|
|
73
|
+
policy.cohortSeed.length < 8 ||
|
|
74
|
+
!policy.components ||
|
|
75
|
+
typeof policy.components !== 'object' ||
|
|
76
|
+
Array.isArray(policy.components)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const issued = Date.parse(policy.issuedAt);
|
|
80
|
+
const expires = Date.parse(policy.expiresAt);
|
|
81
|
+
if (!Number.isFinite(issued) || !Number.isFinite(expires) || expires <= issued)
|
|
82
|
+
return false;
|
|
83
|
+
return Object.values(policy.components).every((component) => component &&
|
|
84
|
+
typeof component === 'object' &&
|
|
85
|
+
!Array.isArray(component) &&
|
|
86
|
+
(!component.contracts ||
|
|
87
|
+
(typeof component.contracts === 'object' && !Array.isArray(component.contracts))) &&
|
|
88
|
+
typeof component.version === 'string' &&
|
|
89
|
+
component.version.length > 0 &&
|
|
90
|
+
Object.values(component.contracts ?? {}).every((range) => range &&
|
|
91
|
+
typeof range === 'object' &&
|
|
92
|
+
!Array.isArray(range) &&
|
|
93
|
+
Number.isInteger(range.minimum) &&
|
|
94
|
+
Number.isInteger(range.maximum) &&
|
|
95
|
+
range.minimum >= 0 &&
|
|
96
|
+
range.maximum >= range.minimum));
|
|
97
|
+
}
|
|
98
|
+
function verifyReleasePolicy(policy, keys = releaseKeys_js_1.OVERSKY_RELEASE_PUBLIC_KEYS) {
|
|
99
|
+
if (!policyShapeIsValid(policy))
|
|
100
|
+
return { ok: false, reason: 'invalid release-policy shape' };
|
|
101
|
+
const { sig, sigs, ...unsigned } = policy;
|
|
102
|
+
const message = buildReleasePolicyMessage(unsigned);
|
|
103
|
+
if (Array.isArray(sigs) && sigs.length > 0) {
|
|
104
|
+
if (keys.length === 0)
|
|
105
|
+
return { disabled: true };
|
|
106
|
+
for (const entry of sigs.slice(0, MAX_SIGS)) {
|
|
107
|
+
if (!entry || typeof entry.sig !== 'string')
|
|
108
|
+
continue;
|
|
109
|
+
const verdict = verifyOne(message, entry.sig, keys);
|
|
110
|
+
if ('ok' in verdict && verdict.ok)
|
|
111
|
+
return verdict;
|
|
112
|
+
}
|
|
113
|
+
return { ok: false, reason: 'no policy multi-signature verified against a pinned key' };
|
|
114
|
+
}
|
|
115
|
+
return verifyOne(message, sig, keys);
|
|
116
|
+
}
|
|
117
|
+
function isReleasePolicyCohortEligible(policy, subject) {
|
|
118
|
+
if (!subject || policy.rolloutBasisPoints === 0)
|
|
119
|
+
return false;
|
|
120
|
+
if (policy.rolloutBasisPoints === 10_000)
|
|
121
|
+
return true;
|
|
122
|
+
const bucket = (0, node_crypto_1.createHash)('sha256')
|
|
123
|
+
.update(`${policy.releaseId}\n${policy.cohortSeed}\n${subject}`, 'utf8')
|
|
124
|
+
.digest()
|
|
125
|
+
.readUInt32BE(0) % 10_000;
|
|
126
|
+
return bucket < policy.rolloutBasisPoints;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Verify expiry, daemon target binding, contract ranges, channel, and cohort.
|
|
130
|
+
* `autonomous=false` intentionally bypasses cohort gating: an operator who
|
|
131
|
+
* explicitly runs an update may recover a machine outside the staged cohort,
|
|
132
|
+
* but cannot bypass invalid signatures, an expired policy, or incompatibility.
|
|
133
|
+
*/
|
|
134
|
+
function assessReleasePolicy(args) {
|
|
135
|
+
const now = args.now ?? new Date();
|
|
136
|
+
if (Date.parse(args.policy.expiresAt) <= now.getTime()) {
|
|
137
|
+
return { valid: false, eligible: false, reason: `policy expired at ${args.policy.expiresAt}` };
|
|
138
|
+
}
|
|
139
|
+
if (args.policy.channel !== args.channel) {
|
|
140
|
+
return {
|
|
141
|
+
valid: false,
|
|
142
|
+
eligible: false,
|
|
143
|
+
reason: `policy channel ${args.policy.channel} does not match ${args.channel}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const componentName = args.targetComponent ?? 'daemon';
|
|
147
|
+
const daemon = args.policy.components[componentName];
|
|
148
|
+
if (!daemon || daemon.version !== args.targetDaemonVersion) {
|
|
149
|
+
return {
|
|
150
|
+
valid: false,
|
|
151
|
+
eligible: false,
|
|
152
|
+
reason: `policy ${componentName} component does not bind the target artifact version`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
for (const [name, range] of Object.entries(daemon.contracts ?? {})) {
|
|
156
|
+
const actual = args.contracts[name];
|
|
157
|
+
if (!Number.isInteger(actual) || actual < range.minimum || actual > range.maximum) {
|
|
158
|
+
return {
|
|
159
|
+
valid: false,
|
|
160
|
+
eligible: false,
|
|
161
|
+
reason: `local contract ${name}=${String(actual)} is outside policy range ${range.minimum}..${range.maximum}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const eligible = !args.autonomous || isReleasePolicyCohortEligible(args.policy, args.cohortSubject);
|
|
166
|
+
return eligible
|
|
167
|
+
? { valid: true, eligible: true }
|
|
168
|
+
: { valid: true, eligible: false, reason: 'machine is outside rollout cohort' };
|
|
169
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compareReleaseVersions = compareReleaseVersions;
|
|
4
|
+
/** SemVer precedence shared by every local release consumer. Invalid inputs are never ordered. */
|
|
5
|
+
function compareReleaseVersions(a, b) {
|
|
6
|
+
const parse = (value) => {
|
|
7
|
+
if (typeof value !== 'string')
|
|
8
|
+
return null;
|
|
9
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(value.trim());
|
|
10
|
+
if (!match)
|
|
11
|
+
return null;
|
|
12
|
+
const prerelease = match[4]?.split('.') ?? [];
|
|
13
|
+
if (prerelease.some((part) => /^\d+$/.test(part) && part.length > 1 && part[0] === '0'))
|
|
14
|
+
return null;
|
|
15
|
+
return { core: match.slice(1, 4).map(BigInt), prerelease };
|
|
16
|
+
};
|
|
17
|
+
const left = parse(a);
|
|
18
|
+
const right = parse(b);
|
|
19
|
+
if (!left || !right)
|
|
20
|
+
return null;
|
|
21
|
+
for (let i = 0; i < 3; i++) {
|
|
22
|
+
if (left.core[i] !== right.core[i])
|
|
23
|
+
return left.core[i] < right.core[i] ? -1 : 1;
|
|
24
|
+
}
|
|
25
|
+
if (!left.prerelease.length || !right.prerelease.length) {
|
|
26
|
+
return left.prerelease.length === right.prerelease.length ? 0 : left.prerelease.length ? -1 : 1;
|
|
27
|
+
}
|
|
28
|
+
for (let i = 0; i < Math.max(left.prerelease.length, right.prerelease.length); i++) {
|
|
29
|
+
const l = left.prerelease[i];
|
|
30
|
+
const r = right.prerelease[i];
|
|
31
|
+
if (l === undefined || r === undefined)
|
|
32
|
+
return l === r ? 0 : l === undefined ? -1 : 1;
|
|
33
|
+
if (l === r)
|
|
34
|
+
continue;
|
|
35
|
+
const ln = /^\d+$/.test(l);
|
|
36
|
+
const rn = /^\d+$/.test(r);
|
|
37
|
+
if (ln && rn)
|
|
38
|
+
return BigInt(l) < BigInt(r) ? -1 : 1;
|
|
39
|
+
if (ln !== rn)
|
|
40
|
+
return ln ? -1 : 1;
|
|
41
|
+
return l < r ? -1 : 1;
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
@@ -129,3 +129,7 @@ export interface CliHandoffDpopProofResponse {
|
|
|
129
129
|
proof: string;
|
|
130
130
|
}
|
|
131
131
|
export declare function parseCliHandoffDpopProofResponse(value: unknown): CliHandoffDpopProofResponse | null;
|
|
132
|
+
/** Persisted credential families; deployed values must not change. */
|
|
133
|
+
export declare const DELEGATED_AGENT_CLI_ID_PREFIX = "oversky-daemon-agent-cli-";
|
|
134
|
+
export declare const WORKLOAD_CLI_ID_PREFIX = "oversky-daemon-workload-cli-";
|
|
135
|
+
export declare function classifyCliFamilyId(id: unknown): 'agent' | 'workload' | 'person';
|
|
@@ -133,3 +133,15 @@ export function parseCliHandoffDpopProofResponse(value) {
|
|
|
133
133
|
return null;
|
|
134
134
|
return { proof: v.proof };
|
|
135
135
|
}
|
|
136
|
+
/** Persisted credential families; deployed values must not change. */
|
|
137
|
+
export const DELEGATED_AGENT_CLI_ID_PREFIX = 'oversky-daemon-agent-cli-';
|
|
138
|
+
export const WORKLOAD_CLI_ID_PREFIX = 'oversky-daemon-workload-cli-';
|
|
139
|
+
export function classifyCliFamilyId(id) {
|
|
140
|
+
if (typeof id !== 'string')
|
|
141
|
+
return 'person';
|
|
142
|
+
if (id.startsWith(DELEGATED_AGENT_CLI_ID_PREFIX))
|
|
143
|
+
return 'agent';
|
|
144
|
+
if (id.startsWith(WORKLOAD_CLI_ID_PREFIX))
|
|
145
|
+
return 'workload';
|
|
146
|
+
return 'person';
|
|
147
|
+
}
|
|
@@ -18,10 +18,9 @@
|
|
|
18
18
|
* quietly redeclaring. This follows that road rather than reopening the one it
|
|
19
19
|
* replaced.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* performs them.
|
|
21
|
+
* Channel vocabulary stays here. The shared signed installer lives beside it
|
|
22
|
+
* in firstPartyHarnessUpdater.ts, used by CLI, Daemon and Desktop so updating
|
|
23
|
+
* a local engine needs neither a running daemon nor an account.
|
|
25
24
|
*
|
|
26
25
|
* Every product-named value comes from `firstPartyHarness.ts`; nothing in this
|
|
27
26
|
* file spells the product.
|
|
@@ -18,10 +18,9 @@
|
|
|
18
18
|
* quietly redeclaring. This follows that road rather than reopening the one it
|
|
19
19
|
* replaced.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* performs them.
|
|
21
|
+
* Channel vocabulary stays here. The shared signed installer lives beside it
|
|
22
|
+
* in firstPartyHarnessUpdater.ts, used by CLI, Daemon and Desktop so updating
|
|
23
|
+
* a local engine needs neither a running daemon nor an account.
|
|
25
24
|
*
|
|
26
25
|
* Every product-named value comes from `firstPartyHarness.ts`; nothing in this
|
|
27
26
|
* file spells the product.
|