@skrr-ai/cli 0.1.119 → 0.1.121
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/dist/base-command.d.ts +31 -0
- package/dist/base-command.js +117 -6
- package/dist/commands/agent/message/context.d.ts +1 -0
- package/dist/commands/agent/message/context.js +3 -2
- package/dist/commands/commands.js +11 -0
- package/dist/commands/commitments/action-proposal.d.ts +13 -0
- package/dist/commands/commitments/action-proposal.js +39 -0
- package/dist/commands/commitments/action-proposals/decide.js +8 -1
- package/dist/commands/commitments/action-proposals/execute.js +11 -1
- package/dist/commands/commitments/create.js +2 -2
- package/dist/commands/commitments/doctor.d.ts +8 -0
- package/dist/commands/commitments/doctor.js +22 -0
- package/dist/commands/commitments/execute-remediation.js +12 -1
- package/dist/commands/commitments/observations.js +2 -2
- package/dist/commands/commitments/pack/bind.d.ts +18 -1
- package/dist/commands/commitments/pack/bind.js +66 -8
- package/dist/commands/commitments/pack/plan.d.ts +4 -1
- package/dist/commands/commitments/pack/plan.js +30 -2
- package/dist/commands/commitments/update.js +8 -2
- package/dist/commands/completion/index.js +1 -1
- package/dist/commands/completion/values.js +1 -1
- package/dist/commands/computer-use/audio.d.ts +1 -0
- package/dist/commands/computer-use/audio.js +1 -0
- package/dist/commands/computer-use/browser.d.ts +1 -0
- package/dist/commands/computer-use/browser.js +1 -0
- package/dist/commands/computer-use/disable.d.ts +1 -0
- package/dist/commands/computer-use/disable.js +1 -0
- package/dist/commands/computer-use/enable.d.ts +1 -0
- package/dist/commands/computer-use/enable.js +1 -0
- package/dist/commands/computer-use/index.d.ts +1 -0
- package/dist/commands/computer-use/index.js +1 -0
- package/dist/commands/computer-use/install.d.ts +1 -0
- package/dist/commands/computer-use/install.js +1 -0
- package/dist/commands/computer-use/recover.d.ts +1 -0
- package/dist/commands/computer-use/recover.js +1 -0
- package/dist/commands/computer-use/status.d.ts +1 -0
- package/dist/commands/computer-use/status.js +1 -0
- package/dist/commands/computer-use/stop.d.ts +1 -0
- package/dist/commands/computer-use/stop.js +1 -0
- package/dist/commands/context.js +16 -11
- package/dist/commands/convos/recovery.js +1 -1
- package/dist/commands/daemon/diagnostics.d.ts +9 -0
- package/dist/commands/daemon/diagnostics.js +53 -0
- package/dist/commands/daemon/index.js +2 -0
- package/dist/commands/daemon/maintenance/attest.d.ts +15 -0
- package/dist/commands/daemon/maintenance/attest.js +52 -0
- package/dist/commands/daemon/maintenance/hold.d.ts +17 -0
- package/dist/commands/daemon/maintenance/hold.js +70 -0
- package/dist/commands/daemon/maintenance/index.d.ts +12 -0
- package/dist/commands/daemon/maintenance/index.js +30 -0
- package/dist/commands/daemon/maintenance/resume.d.ts +13 -0
- package/dist/commands/daemon/maintenance/resume.js +36 -0
- package/dist/commands/daemon/maintenance/status.d.ts +13 -0
- package/dist/commands/daemon/maintenance/status.js +36 -0
- package/dist/commands/doctor.d.ts +4 -1
- package/dist/commands/doctor.js +50 -6
- package/dist/commands/followups/plans/cancel-batch.js +6 -3
- package/dist/commands/followups/plans/capacity.js +7 -3
- package/dist/commands/followups/plans/observability.js +11 -3
- package/dist/commands/followups/plans/preferences/set.js +26 -8
- package/dist/commands/followups/plans/preferences.js +1 -1
- package/dist/commands/followups/plans/show.js +1 -1
- package/dist/commands/goals/key-results/assess.d.ts +36 -0
- package/dist/commands/goals/key-results/assess.js +110 -0
- package/dist/commands/goals/key-results/history.d.ts +30 -0
- package/dist/commands/goals/key-results/history.js +104 -0
- package/dist/commands/goals/key-results/list.d.ts +5 -4
- package/dist/commands/goals/key-results/list.js +12 -6
- package/dist/commands/goals/key-results/show.d.ts +4 -2
- package/dist/commands/goals/key-results/show.js +25 -4
- package/dist/commands/goals/key-results/update.d.ts +4 -0
- package/dist/commands/goals/key-results/update.js +18 -24
- package/dist/commands/goals/plan-now.js +12 -1
- package/dist/commands/goals/planner-compare.d.ts +30 -0
- package/dist/commands/goals/planner-compare.js +136 -0
- package/dist/commands/machines/dedicated/service/exec.d.ts +3 -0
- package/dist/commands/machines/dedicated/service/exec.js +70 -11
- package/dist/commands/machines/hosted/destroy.d.ts +1 -0
- package/dist/commands/machines/hosted/destroy.js +11 -1
- package/dist/commands/machines/hosted/exec.d.ts +1 -0
- package/dist/commands/machines/hosted/exec.js +12 -1
- package/dist/commands/machines/hosted/index.js +1 -0
- package/dist/commands/machines/hosted/operation.d.ts +14 -0
- package/dist/commands/machines/hosted/operation.js +51 -0
- package/dist/commands/machines/hosted/pull.js +1 -1
- package/dist/commands/machines/hosted/start.js +6 -2
- package/dist/commands/search/agents.js +5 -1
- package/dist/commands/search/all.js +74 -22
- package/dist/commands/search/skills.d.ts +1 -0
- package/dist/commands/search/skills.js +12 -1
- package/dist/commands/search/spaces.d.ts +8 -1
- package/dist/commands/search/spaces.js +50 -12
- package/dist/commands/search/tasks.d.ts +13 -2
- package/dist/commands/search/tasks.js +78 -14
- package/dist/commands/search/teams.js +5 -1
- package/dist/commands/search/workspace.d.ts +19 -0
- package/dist/commands/search/workspace.js +126 -0
- package/dist/commands/skills/browse.d.ts +1 -0
- package/dist/commands/skills/browse.js +8 -1
- package/dist/commands/skills/search.d.ts +1 -0
- package/dist/commands/skills/search.js +12 -1
- package/dist/commands/spaces/auto-executor/compare.d.ts +18 -0
- package/dist/commands/spaces/auto-executor/compare.js +139 -0
- package/dist/commands/spaces/auto-executor/runs.d.ts +3 -0
- package/dist/commands/spaces/auto-executor/runs.js +13 -1
- package/dist/commands/spaces/charter/create-from-description.d.ts +21 -0
- package/dist/commands/spaces/charter/create-from-description.js +77 -0
- package/dist/commands/spaces/charter/show.d.ts +22 -0
- package/dist/commands/spaces/charter/show.js +61 -0
- package/dist/commands/spaces/heartbeats/list.d.ts +1 -16
- package/dist/commands/spaces/heartbeats/list.js +13 -0
- package/dist/commands/spaces/install-template.js +3 -0
- package/dist/commands/spaces/list.d.ts +1 -0
- package/dist/commands/spaces/list.js +12 -3
- package/dist/commands/spaces/loops/migrate.d.ts +32 -0
- package/dist/commands/spaces/loops/migrate.js +103 -0
- package/dist/commands/spaces/loops/release.d.ts +14 -0
- package/dist/commands/spaces/loops/release.js +59 -0
- package/dist/commands/spaces/notebook.d.ts +25 -0
- package/dist/commands/spaces/notebook.js +75 -0
- package/dist/commands/spaces/proposals/discard.d.ts +17 -0
- package/dist/commands/spaces/proposals/discard.js +17 -0
- package/dist/commands/spaces/proposals/keep.d.ts +17 -0
- package/dist/commands/spaces/proposals/keep.js +17 -0
- package/dist/commands/spaces/proposals/list.d.ts +24 -0
- package/dist/commands/spaces/proposals/list.js +89 -0
- package/dist/commands/spaces/search.d.ts +1 -0
- package/dist/commands/spaces/search.js +9 -0
- package/dist/commands/spaces/skills/propose.d.ts +29 -0
- package/dist/commands/spaces/skills/propose.js +82 -0
- package/dist/commands/spaces/templates/fork.js +3 -0
- package/dist/commands/spaces/triggers/show.js +6 -0
- package/dist/commands/spaces/workflows/retry.d.ts +1 -0
- package/dist/commands/spaces/workflows/retry.js +8 -1
- package/dist/commands/spaces/workflows/triggers/create.js +3 -2
- package/dist/commands/spaces/workflows/triggers/fire.js +4 -0
- package/dist/commands/spaces/workflows/triggers/update.js +2 -1
- package/dist/commands/spaces/write-policy/set.d.ts +22 -0
- package/dist/commands/spaces/write-policy/set.js +82 -0
- package/dist/commands/spaces/write-policy/show.d.ts +19 -0
- package/dist/commands/spaces/write-policy/show.js +49 -0
- package/dist/commands/store/artifacts/receipt.d.ts +14 -0
- package/dist/commands/store/artifacts/receipt.js +34 -0
- package/dist/commands/store/artifacts/release.d.ts +2 -0
- package/dist/commands/store/artifacts/release.js +11 -0
- package/dist/commands/store/governance/publish-release.d.ts +2 -0
- package/dist/commands/store/governance/publish-release.js +30 -1
- package/dist/commands/store/governance/release-receipt.d.ts +12 -0
- package/dist/commands/store/governance/release-receipt.js +32 -0
- package/dist/commands/tasks/approvals/list.d.ts +11 -5
- package/dist/commands/tasks/approvals/list.js +13 -6
- package/dist/commands/tasks/attachments/facts.d.ts +12 -0
- package/dist/commands/tasks/attachments/facts.js +29 -0
- package/dist/commands/tasks/attachments/upload.d.ts +1 -0
- package/dist/commands/tasks/attachments/upload.js +16 -0
- package/dist/commands/tasks/bulk-update.js +35 -1
- package/dist/commands/tasks/context.d.ts +15 -0
- package/dist/commands/tasks/context.js +69 -0
- package/dist/commands/tasks/copy.d.ts +18 -0
- package/dist/commands/tasks/copy.js +85 -0
- package/dist/commands/tasks/create-tree.js +97 -23
- package/dist/commands/tasks/create.js +9 -1
- package/dist/commands/tasks/deliverable/add.js +20 -10
- package/dist/commands/tasks/move.js +8 -1
- package/dist/commands/tasks/operations/show.d.ts +16 -0
- package/dist/commands/tasks/operations/show.js +98 -0
- package/dist/commands/tasks/operations/wait.d.ts +22 -0
- package/dist/commands/tasks/operations/wait.js +114 -0
- package/dist/commands/tasks/preflight.d.ts +1 -0
- package/dist/commands/tasks/preflight.js +11 -1
- package/dist/commands/tasks/rearm-recurrence.js +4 -0
- package/dist/commands/tasks/result/show.js +22 -0
- package/dist/commands/tasks/result/submit.d.ts +1 -0
- package/dist/commands/tasks/result/submit.js +3 -0
- package/dist/commands/tasks/retry-receipt.d.ts +15 -0
- package/dist/commands/tasks/retry-receipt.js +89 -0
- package/dist/commands/tasks/retry-review.d.ts +16 -0
- package/dist/commands/tasks/retry-review.js +66 -0
- package/dist/commands/tasks/retry.d.ts +4 -8
- package/dist/commands/tasks/retry.js +34 -68
- package/dist/commands/tasks/runs.d.ts +21 -0
- package/dist/commands/tasks/runs.js +95 -0
- package/dist/commands/tasks/search.d.ts +1 -0
- package/dist/commands/tasks/search.js +9 -0
- package/dist/commands/tasks/show.d.ts +2 -0
- package/dist/commands/tasks/show.js +15 -1
- package/dist/commands/tasks/start-receipt.d.ts +15 -0
- package/dist/commands/tasks/start-receipt.js +95 -0
- package/dist/commands/tasks/start.d.ts +4 -0
- package/dist/commands/tasks/start.js +49 -3
- package/dist/commands/tasks/update.js +3 -0
- package/dist/commands/tasks/upsert.js +17 -3
- package/dist/commands/tasks/watch.d.ts +3 -0
- package/dist/commands/tasks/watch.js +85 -20
- package/dist/commands/wiki/history.d.ts +33 -0
- package/dist/commands/wiki/history.js +134 -0
- package/dist/commands/wiki/ls.d.ts +2 -0
- package/dist/commands/wiki/ls.js +8 -0
- package/dist/commands/wiki/update.d.ts +3 -1
- package/dist/commands/wiki/update.js +24 -2
- package/dist/commands/wiki/write.d.ts +22 -0
- package/dist/commands/wiki/write.js +144 -6
- package/dist/generated/agent-guides.json +81 -81
- package/dist/generated/managed-instruction-guides.json +1 -1
- package/dist/lib/agent-guides.d.ts +9 -0
- package/dist/lib/agent-guides.js +23 -1
- package/dist/lib/agentic-stream.d.ts +5 -0
- package/dist/lib/agentic-stream.js +122 -3
- package/dist/lib/api-error-reason.d.ts +2 -0
- package/dist/lib/api-error-reason.js +14 -0
- package/dist/lib/api-fetch.js +3 -0
- package/dist/lib/auth-diagnostics.d.ts +39 -0
- package/dist/lib/auth-diagnostics.js +71 -0
- package/dist/lib/cli-client-identity.d.ts +6 -0
- package/dist/lib/cli-client-identity.js +16 -0
- package/dist/lib/cli-installation-diagnostics.d.ts +52 -0
- package/dist/lib/cli-installation-diagnostics.js +254 -0
- package/dist/lib/command-hygiene.d.ts +45 -0
- package/dist/lib/command-hygiene.js +413 -0
- package/dist/lib/commitments.d.ts +64 -3
- package/dist/lib/commitments.js +34 -6
- package/dist/lib/completion-index.d.ts +16 -4
- package/dist/lib/completion-index.js +50 -14
- package/dist/lib/config.js +7 -1
- package/dist/lib/daemon-maintenance-command.d.ts +22 -0
- package/dist/lib/daemon-maintenance-command.js +39 -0
- package/dist/lib/daemon-maintenance.d.ts +275 -0
- package/dist/lib/daemon-maintenance.js +1035 -0
- package/dist/lib/dedicated-service.d.ts +3 -2
- package/dist/lib/dedicated-service.js +11 -3
- package/dist/lib/governed-store.d.ts +8 -0
- package/dist/lib/governed-store.js +5 -0
- package/dist/lib/hosted-machines.d.ts +33 -2
- package/dist/lib/hosted-machines.js +19 -1
- package/dist/lib/json-output.d.ts +1 -1
- package/dist/lib/machines.d.ts +9 -1
- package/dist/lib/machines.js +15 -1
- package/dist/lib/node-adapter.js +13 -0
- package/dist/lib/record-output.d.ts +22 -0
- package/dist/lib/record-output.js +78 -0
- package/dist/lib/roles.d.ts +3 -3
- package/dist/lib/scoped-memory-command.d.ts +18 -0
- package/dist/lib/scoped-memory-command.js +53 -2
- package/dist/lib/search-display.d.ts +7 -0
- package/dist/lib/search-display.js +31 -0
- package/dist/lib/skill-store-pagination.d.ts +8 -0
- package/dist/lib/skill-store-pagination.js +35 -0
- package/dist/lib/space-charter.d.ts +49 -0
- package/dist/lib/space-charter.js +78 -0
- package/dist/lib/space-loop-migration.d.ts +18 -0
- package/dist/lib/space-loop-migration.js +128 -0
- package/dist/lib/space-notebook.d.ts +22 -0
- package/dist/lib/space-notebook.js +140 -0
- package/dist/lib/space-proposal-decision-command.d.ts +36 -0
- package/dist/lib/space-proposal-decision-command.js +108 -0
- package/dist/lib/space-proposals.d.ts +44 -0
- package/dist/lib/space-proposals.js +186 -0
- package/dist/lib/space-templates.d.ts +8 -0
- package/dist/lib/space-templates.js +17 -0
- package/dist/lib/space-write-policy.d.ts +24 -0
- package/dist/lib/space-write-policy.js +103 -0
- package/dist/lib/task-agent-context-capabilities.d.ts +10 -0
- package/dist/lib/task-agent-context-capabilities.js +22 -0
- package/dist/lib/task-agent-context-compatibility.d.ts +9 -0
- package/dist/lib/task-agent-context-compatibility.js +30 -0
- package/dist/lib/task-asset-upload.d.ts +22 -0
- package/dist/lib/task-asset-upload.js +257 -29
- package/dist/lib/task-batch.d.ts +15 -1
- package/dist/lib/task-batch.js +35 -2
- package/dist/lib/task-operation-watch.d.ts +66 -0
- package/dist/lib/task-operation-watch.js +159 -0
- package/dist/lib/task-resolver.d.ts +1 -1
- package/dist/lib/task-resolver.js +4 -3
- package/dist/lib/task-work.d.ts +1 -1
- package/dist/lib/task-work.js +35 -1
- package/dist/lib/task-write-attribution.d.ts +14 -0
- package/dist/lib/task-write-attribution.js +35 -2
- package/dist/lib/triggers.d.ts +11 -0
- package/dist/lib/triggers.js +7 -0
- package/dist/lib/wiki-update.d.ts +1 -0
- package/dist/lib/wiki-update.js +10 -1
- package/dist/lib/wiki.js +4 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +9 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +31 -15
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +10 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonConsentProof.d.ts +20 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonConsentProof.js +72 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +13 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/desktopDaemonProof.d.ts +21 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/desktopDaemonProof.js +96 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +2 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +102 -20
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.d.ts +370 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.js +315 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineReservation.d.ts +27 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineReservation.js +161 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineScopes.d.ts +26 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineScopes.js +41 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +15 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/macos.js +20 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.d.ts +178 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.js +262 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.js +106 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +9 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +32 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +10 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +172 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonConsentProof.d.ts +20 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonConsentProof.js +43 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +33 -14
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/desktopDaemonProof.d.ts +21 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/desktopDaemonProof.js +62 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +2 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +133 -25
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.d.ts +370 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.js +404 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineReservation.d.ts +27 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineReservation.js +132 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineScopes.d.ts +26 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineScopes.js +15 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/macos.js +28 -15
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.d.ts +178 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.js +342 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.js +138 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +51 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +27181 -20293
- package/oclif.manifest.json +5745 -1813
- package/package.json +20 -2
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* when not active. CLI leaves it unset; legacy `OVERSKY_KEK_REQUIRED=1`
|
|
41
41
|
* env var is still honored as a back-compat trigger.
|
|
42
42
|
*/
|
|
43
|
-
import { createHash } from 'node:crypto';
|
|
43
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
44
44
|
import fs from 'node:fs';
|
|
45
45
|
import path from 'node:path';
|
|
46
46
|
import lockfile from 'proper-lockfile';
|
|
@@ -429,6 +429,172 @@ function retiredDekFiles(profile) {
|
|
|
429
429
|
return [];
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
|
+
/** Open only the existing wrapped DEK. Recovery must never enroll a new key,
|
|
433
|
+
* consolidate profile paths or replace an unreadable envelope. A caller gets
|
|
434
|
+
* an error if the existing file or the platform's read-only unwrap is unsafe
|
|
435
|
+
* or unavailable. This intentionally does not load retired keys; credentials
|
|
436
|
+
* that require one remain unavailable rather than triggering a migration. */
|
|
437
|
+
export async function initExistingCredEnvelope(profile) {
|
|
438
|
+
if (_state.kind === 'active' && _state.profile === profile)
|
|
439
|
+
return;
|
|
440
|
+
if (_state.kind !== 'uninit' || typeof profile !== 'string' || !profile) {
|
|
441
|
+
throw new Error('Existing credential envelope is unavailable');
|
|
442
|
+
}
|
|
443
|
+
let fd;
|
|
444
|
+
let candidate;
|
|
445
|
+
try {
|
|
446
|
+
const target = wrappedDekFilePath(profile);
|
|
447
|
+
fd = fs.openSync(target, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
448
|
+
const before = fs.fstatSync(fd);
|
|
449
|
+
const safe = (stat) => stat.isFile() && stat.nlink === 1 && stat.size > 0 && stat.size <= 65_536 &&
|
|
450
|
+
(stat.mode & 0o077) === 0 && (typeof process.getuid !== 'function' || stat.uid === process.getuid());
|
|
451
|
+
if (!safe(before))
|
|
452
|
+
throw new Error('unsafe envelope');
|
|
453
|
+
const readBounded = () => {
|
|
454
|
+
const bytes = Buffer.alloc(before.size);
|
|
455
|
+
let offset = 0;
|
|
456
|
+
while (offset < bytes.length) {
|
|
457
|
+
const count = fs.readSync(fd, bytes, offset, bytes.length - offset, offset);
|
|
458
|
+
if (count <= 0)
|
|
459
|
+
throw new Error('changed envelope');
|
|
460
|
+
offset += count;
|
|
461
|
+
}
|
|
462
|
+
return bytes;
|
|
463
|
+
};
|
|
464
|
+
const wrapped = readBounded();
|
|
465
|
+
const kek = getKekStrategy();
|
|
466
|
+
if (!kek.unwrapExisting || !(await kek.available()))
|
|
467
|
+
throw new Error('read-only KEK unavailable');
|
|
468
|
+
candidate = await kek.unwrapExisting(wrapped, buildCredentialAad(profile));
|
|
469
|
+
if (!Buffer.isBuffer(candidate) || candidate.length !== 32)
|
|
470
|
+
throw new Error('invalid envelope');
|
|
471
|
+
// The unwrap can wait for Keychain. A different file, a replaced path or
|
|
472
|
+
// a second initializer must not make this process adopt a stale key.
|
|
473
|
+
if (!readBounded().equals(wrapped))
|
|
474
|
+
throw new Error('changed envelope');
|
|
475
|
+
const after = fs.fstatSync(fd);
|
|
476
|
+
const current = fs.lstatSync(target);
|
|
477
|
+
if (!safe(after) || !safe(current) || before.dev !== after.dev || before.ino !== after.ino ||
|
|
478
|
+
before.dev !== current.dev || before.ino !== current.ino || before.size !== after.size ||
|
|
479
|
+
before.size !== current.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs ||
|
|
480
|
+
before.mtimeMs !== current.mtimeMs || before.ctimeMs !== current.ctimeMs ||
|
|
481
|
+
wrappedDekFilePath(profile) !== target || _state.kind !== 'uninit') {
|
|
482
|
+
throw new Error('changed envelope');
|
|
483
|
+
}
|
|
484
|
+
for (const old of _retiredDeks)
|
|
485
|
+
old.fill(0);
|
|
486
|
+
_retiredDeks = [];
|
|
487
|
+
_state = { kind: 'active', dek: candidate, kekId: kek.id,
|
|
488
|
+
kekKind: typeof kek.kind === 'string' || kek.kind === null ? (kek.kind ?? null) : null, profile };
|
|
489
|
+
candidate = undefined;
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
if (Buffer.isBuffer(candidate))
|
|
493
|
+
candidate.fill(0);
|
|
494
|
+
throw new Error('Existing credential envelope is unavailable');
|
|
495
|
+
}
|
|
496
|
+
finally {
|
|
497
|
+
if (fd !== undefined)
|
|
498
|
+
fs.closeSync(fd);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/** First enrollment for a reserved desktop background profile. Existing or
|
|
502
|
+
* ambiguous state is opened read-only or refused; it is never re-enrolled.
|
|
503
|
+
* The caller must validate the reservation before calling this function. */
|
|
504
|
+
export async function initNewIndependentCredEnvelope(profile) {
|
|
505
|
+
if (!/^desktop-bg-[a-f0-9]{32}$/.test(profile))
|
|
506
|
+
throw new Error('Background credential enrollment is unavailable');
|
|
507
|
+
if (_state.kind === 'active' && _state.profile === profile)
|
|
508
|
+
return;
|
|
509
|
+
if (_state.kind !== 'uninit')
|
|
510
|
+
throw new Error('Background credential enrollment is unavailable');
|
|
511
|
+
const resolved = resolveProfileStateFile(profile, ACCOUNT_CRED_DEK_WRAPPED);
|
|
512
|
+
const paths = [resolved.canonicalPath, ...(resolved.legacyPath ? [resolved.legacyPath] : [])];
|
|
513
|
+
const present = (file) => {
|
|
514
|
+
try {
|
|
515
|
+
fs.lstatSync(file);
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
if (err.code === 'ENOENT')
|
|
520
|
+
return false;
|
|
521
|
+
throw err;
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
if (paths.some(present))
|
|
525
|
+
return initExistingCredEnvelope(profile);
|
|
526
|
+
// A missing DEK alongside a previously written identity or credential is
|
|
527
|
+
// recovery, not first enrollment, even if a retried caller says otherwise.
|
|
528
|
+
const dir = resolved.canonicalDir;
|
|
529
|
+
const stat = fs.lstatSync(dir);
|
|
530
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || (stat.mode & 0o077) !== 0 ||
|
|
531
|
+
(typeof process.getuid === 'function' && stat.uid !== process.getuid())) {
|
|
532
|
+
throw new Error('Background credential enrollment is unavailable');
|
|
533
|
+
}
|
|
534
|
+
const artifacts = ['device-private-key', 'device-public-key.jwk', 'auth-binding.json', 'auth.json'];
|
|
535
|
+
const artifactDirs = [dir, ...(resolved.legacyDir ? [resolved.legacyDir] : [])];
|
|
536
|
+
const assertEmpty = () => {
|
|
537
|
+
if (paths.some(present) || artifactDirs.some((root) => artifacts.some((name) => present(path.join(root, name)))) ||
|
|
538
|
+
wrappedDekFilePath(profile) !== resolved.canonicalPath) {
|
|
539
|
+
throw new Error('Background credential enrollment needs recovery');
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
assertEmpty();
|
|
543
|
+
const kek = getKekStrategy();
|
|
544
|
+
if (!kek.unwrapExisting || !(await kek.available()))
|
|
545
|
+
throw new Error('Background credential enrollment is unavailable');
|
|
546
|
+
assertEmpty();
|
|
547
|
+
const dek = generateDek();
|
|
548
|
+
let temp;
|
|
549
|
+
try {
|
|
550
|
+
const wrapped = await kek.wrap(dek, buildCredentialAad(profile));
|
|
551
|
+
// Another enrollment may have won while the platform store was busy.
|
|
552
|
+
// Its file is opened read-only; this process never overwrites it.
|
|
553
|
+
if (paths.some(present))
|
|
554
|
+
return await initExistingCredEnvelope(profile);
|
|
555
|
+
assertEmpty();
|
|
556
|
+
const temporaryPath = path.join(dir, `.cred-dek-enrollment.${randomBytes(12).toString('hex')}.tmp`);
|
|
557
|
+
const fd = fs.openSync(temporaryPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL |
|
|
558
|
+
(fs.constants.O_NOFOLLOW ?? 0), 0o600);
|
|
559
|
+
temp = temporaryPath;
|
|
560
|
+
try {
|
|
561
|
+
fs.writeFileSync(fd, wrapped);
|
|
562
|
+
fs.fsyncSync(fd);
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
fs.closeSync(fd);
|
|
566
|
+
}
|
|
567
|
+
assertEmpty();
|
|
568
|
+
try {
|
|
569
|
+
fs.linkSync(temp, resolved.canonicalPath);
|
|
570
|
+
}
|
|
571
|
+
catch (err) {
|
|
572
|
+
if (err.code !== 'EEXIST')
|
|
573
|
+
throw err;
|
|
574
|
+
}
|
|
575
|
+
const dirFd = fs.openSync(dir, 'r');
|
|
576
|
+
try {
|
|
577
|
+
fs.fsyncSync(dirFd);
|
|
578
|
+
}
|
|
579
|
+
finally {
|
|
580
|
+
fs.closeSync(dirFd);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
finally {
|
|
584
|
+
dek.fill(0);
|
|
585
|
+
if (temp) {
|
|
586
|
+
fs.unlinkSync(temp);
|
|
587
|
+
const dirFd = fs.openSync(dir, 'r');
|
|
588
|
+
try {
|
|
589
|
+
fs.fsyncSync(dirFd);
|
|
590
|
+
}
|
|
591
|
+
finally {
|
|
592
|
+
fs.closeSync(dirFd);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
await initExistingCredEnvelope(profile);
|
|
597
|
+
}
|
|
432
598
|
/**
|
|
433
599
|
* Initialize the envelope path. Idempotent — second call when state is
|
|
434
600
|
* non-uninit returns immediately.
|
|
@@ -440,6 +606,11 @@ function retiredDekFiles(profile) {
|
|
|
440
606
|
* 4. Any throw → disabled+init_failed; transforms stay passthrough.
|
|
441
607
|
*/
|
|
442
608
|
export async function initCredEnvelope(profile) {
|
|
609
|
+
// A reserved background profile has an independent owner and device key.
|
|
610
|
+
// Generic status, startup and CLI paths may read it, but cannot replace a
|
|
611
|
+
// missing or unreadable key. Only validated first enrollment can create one.
|
|
612
|
+
if (/^desktop-bg-[a-f0-9]{32}$/i.test(profile))
|
|
613
|
+
return initExistingCredEnvelope(profile);
|
|
443
614
|
if (_state.kind !== 'uninit')
|
|
444
615
|
return;
|
|
445
616
|
if (typeof profile !== 'string' || profile.length === 0) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DaemonConsentPayload {
|
|
2
|
+
consentVersion: number;
|
|
3
|
+
consentSource: string;
|
|
4
|
+
isLegacy: boolean;
|
|
5
|
+
isReConsent: boolean;
|
|
6
|
+
scopes: string[];
|
|
7
|
+
scopeDescriptions: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
/** A single canonical, bounded representation on both sides of the log. */
|
|
10
|
+
export declare function normalizeDaemonConsent(input: unknown): DaemonConsentPayload | null;
|
|
11
|
+
export interface DaemonConsentIdentity {
|
|
12
|
+
pid: number;
|
|
13
|
+
daemonId: string;
|
|
14
|
+
userId: string;
|
|
15
|
+
origin: string;
|
|
16
|
+
}
|
|
17
|
+
/** The bootstrap secret is never logged. This MAC binds the exact consent,
|
|
18
|
+
* live process, daemon and cloud owner; an old log entry is not authority. */
|
|
19
|
+
export declare function daemonConsentProof(secret: string, identity: DaemonConsentIdentity, payload: DaemonConsentPayload): string;
|
|
20
|
+
export declare function verifyDaemonConsentProof(secret: string, identity: DaemonConsentIdentity, payload: DaemonConsentPayload, proof: string): boolean;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/** A single canonical, bounded representation on both sides of the log. */
|
|
3
|
+
export function normalizeDaemonConsent(input) {
|
|
4
|
+
if (!input || typeof input !== 'object' || Array.isArray(input))
|
|
5
|
+
return null;
|
|
6
|
+
const value = input;
|
|
7
|
+
if (!Number.isSafeInteger(value.consentVersion) || value.consentVersion < 0 ||
|
|
8
|
+
typeof value.consentSource !== 'string' || value.consentSource.length > 64 ||
|
|
9
|
+
typeof value.isLegacy !== 'boolean' || typeof value.isReConsent !== 'boolean' ||
|
|
10
|
+
!Array.isArray(value.scopes) || value.scopes.length > 128 ||
|
|
11
|
+
!value.scopes.every((scope) => typeof scope === 'string' && scope.length <= 256) ||
|
|
12
|
+
!value.scopeDescriptions || typeof value.scopeDescriptions !== 'object' || Array.isArray(value.scopeDescriptions))
|
|
13
|
+
return null;
|
|
14
|
+
const entries = Object.entries(value.scopeDescriptions)
|
|
15
|
+
.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
16
|
+
if (entries.length > 128 || entries.some(([key, text]) => key.length > 256 || typeof text !== 'string' || text.length > 1024))
|
|
17
|
+
return null;
|
|
18
|
+
return { consentVersion: value.consentVersion, consentSource: value.consentSource,
|
|
19
|
+
isLegacy: value.isLegacy, isReConsent: value.isReConsent, scopes: value.scopes,
|
|
20
|
+
scopeDescriptions: Object.fromEntries(entries) };
|
|
21
|
+
}
|
|
22
|
+
/** The bootstrap secret is never logged. This MAC binds the exact consent,
|
|
23
|
+
* live process, daemon and cloud owner; an old log entry is not authority. */
|
|
24
|
+
export function daemonConsentProof(secret, identity, payload) {
|
|
25
|
+
const key = Buffer.from(secret, 'base64');
|
|
26
|
+
if (key.length !== 32 || !Number.isSafeInteger(identity.pid) || identity.pid <= 0 ||
|
|
27
|
+
!identity.daemonId || !identity.userId)
|
|
28
|
+
throw new Error('Invalid daemon consent identity');
|
|
29
|
+
const origin = new URL(identity.origin).origin;
|
|
30
|
+
return createHmac('sha256', key).update('skrr-daemon-consent:v1\0').update(JSON.stringify([
|
|
31
|
+
identity.pid, identity.daemonId, identity.userId.toLowerCase(), origin, payload,
|
|
32
|
+
])).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
export function verifyDaemonConsentProof(secret, identity, payload, proof) {
|
|
35
|
+
if (!/^[a-f0-9]{64}$/.test(proof))
|
|
36
|
+
return false;
|
|
37
|
+
try {
|
|
38
|
+
return timingSafeEqual(Buffer.from(proof, 'hex'), Buffer.from(daemonConsentProof(secret, identity, payload), 'hex'));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
* heard is `firstPartyHarness.ts`'s and the API's `sessionProvider.js`'s
|
|
47
47
|
* decision, not this table's.
|
|
48
48
|
*/
|
|
49
|
-
import { COMPUTER_DESKTOP_CAPABILITY, COMPUTER_DESKTOP_CONTROL_CAPABILITY, DAEMON_COMPUTER_DESKTOP_EVENTS } from './desktopWire.js';
|
|
50
|
-
import { COMPUTER_CAPABILITIES, DAEMON_COMPUTER_EVENTS, DAEMON_COMPUTER_REPORT_EVENT_KEYS, } from './computerWire.js';
|
|
49
|
+
import { COMPUTER_DESKTOP_CAPABILITY, COMPUTER_DESKTOP_CONTROL_CAPABILITY, DAEMON_COMPUTER_DESKTOP_EVENTS, } from './desktopWire.js';
|
|
50
|
+
import { COMPUTER_CAPABILITIES, DAEMON_COMPUTER_EVENTS, DAEMON_COMPUTER_REPORT_EVENT_KEYS, DAEMON_COMPUTER_USE_CONTROL_EVENT, } from './computerWire.js';
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
52
|
// Vocabulary
|
|
53
53
|
// ---------------------------------------------------------------------------
|
|
@@ -283,34 +283,53 @@ const TOOLS = [
|
|
|
283
283
|
capability: TOOL_RESPONSE_OUTBOX,
|
|
284
284
|
}),
|
|
285
285
|
toDaemon('daemon:tool:cancel', 'Abandon a running tool call.'),
|
|
286
|
-
toDaemon(
|
|
287
|
-
stability: 'experimental',
|
|
286
|
+
toDaemon(DAEMON_COMPUTER_USE_CONTROL_EVENT, 'An authorized person observes, configures, stops or recovers the machine Computer Use provider.', {
|
|
287
|
+
stability: 'experimental',
|
|
288
|
+
capability: 'computer_use_mcp_v1',
|
|
289
|
+
delivery: 'acked',
|
|
290
|
+
answeredBy: DAEMON_COMPUTER_EVENTS.ack,
|
|
288
291
|
payload: { kind: 'zod', schema: 'daemonComputerUseControlSchema' },
|
|
289
292
|
}),
|
|
290
293
|
toDaemon('daemon:computer-use:rotate-credential', 'The approved hosted-desktop owner requests credential rotation for this exact active machine lease generation.', {
|
|
291
|
-
stability: 'experimental',
|
|
294
|
+
stability: 'experimental',
|
|
295
|
+
capability: 'computer_use_mcp_v1',
|
|
296
|
+
delivery: 'acked',
|
|
297
|
+
answeredBy: DAEMON_COMPUTER_EVENTS.ack,
|
|
292
298
|
payload: { kind: 'zod', schema: 'daemonComputerUseRotateCredentialSchema' },
|
|
293
299
|
}),
|
|
294
300
|
toDaemon('daemon:computer-use:revoke', 'Revoke cloud Computer Use bindings after owner machine authority was removed.', {
|
|
295
|
-
stability: 'experimental',
|
|
301
|
+
stability: 'experimental',
|
|
302
|
+
capability: 'computer_use_mcp_v1',
|
|
296
303
|
payload: { kind: 'zod', schema: 'daemonComputerUseRevokeSchema' },
|
|
297
304
|
}),
|
|
298
305
|
...['subscribe', 'protocol', 'unsubscribe'].map((operation) => toDaemon(DAEMON_COMPUTER_DESKTOP_EVENTS[operation], `Read-only desktop stream: ${operation}; bound to an authenticated attachment and generation.`, {
|
|
299
|
-
stability: 'experimental',
|
|
300
|
-
|
|
301
|
-
|
|
306
|
+
stability: 'experimental',
|
|
307
|
+
capability: COMPUTER_DESKTOP_CAPABILITY,
|
|
308
|
+
delivery: 'acked',
|
|
309
|
+
answeredBy: DAEMON_COMPUTER_EVENTS.ack,
|
|
310
|
+
payload: {
|
|
311
|
+
kind: 'zod',
|
|
312
|
+
schema: `daemonComputerDesktop${operation[0].toUpperCase()}${operation.slice(1)}Schema`,
|
|
313
|
+
},
|
|
302
314
|
})),
|
|
303
315
|
...['control', 'input'].map((operation) => toDaemon(DAEMON_COMPUTER_DESKTOP_EVENTS[operation], `Human-only typed desktop ${operation}; exact authenticated attachment, stream and generation.`, {
|
|
304
|
-
stability: 'experimental',
|
|
305
|
-
|
|
306
|
-
|
|
316
|
+
stability: 'experimental',
|
|
317
|
+
capability: COMPUTER_DESKTOP_CONTROL_CAPABILITY,
|
|
318
|
+
delivery: 'acked',
|
|
319
|
+
answeredBy: DAEMON_COMPUTER_EVENTS.ack,
|
|
320
|
+
payload: {
|
|
321
|
+
kind: 'zod',
|
|
322
|
+
schema: `daemonComputerDesktop${operation[0].toUpperCase()}${operation.slice(1)}Schema`,
|
|
323
|
+
},
|
|
307
324
|
})),
|
|
308
325
|
toServer(DAEMON_COMPUTER_DESKTOP_EVENTS.controlChanged, 'Private desktop holder and input revocation fact; never carries a control token.', {
|
|
309
|
-
stability: 'experimental',
|
|
326
|
+
stability: 'experimental',
|
|
327
|
+
capability: COMPUTER_DESKTOP_CONTROL_CAPABILITY,
|
|
310
328
|
payload: { kind: 'zod', schema: 'computerDesktopControlResultSchema' },
|
|
311
329
|
}),
|
|
312
330
|
toServer(DAEMON_COMPUTER_DESKTOP_EVENTS.data, 'Read-only desktop stream data or a terminal close fact for one attachment generation.', {
|
|
313
|
-
stability: 'experimental',
|
|
331
|
+
stability: 'experimental',
|
|
332
|
+
capability: COMPUTER_DESKTOP_CAPABILITY,
|
|
314
333
|
payload: { kind: 'zod', schema: 'computerDesktopDataSchema' },
|
|
315
334
|
}),
|
|
316
335
|
toServer('daemon:wire:chunk', 'One slice of a tool or session response above the single-message ceiling, sha256-checked and reassembled by the API before the reassembled event reaches its normal handler. Sent only to a server that announced `serverCaps.wireChunking`; the last slice is acked by that handler (OSK-16616).', {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const DESKTOP_DAEMON_PROOF_NAMESPACE = "/desktop-proof-v1";
|
|
2
|
+
export declare const DESKTOP_DAEMON_RENEWAL_NAMESPACE = "/desktop-renewal-v1";
|
|
3
|
+
export interface DesktopDaemonOwner {
|
|
4
|
+
userId: string;
|
|
5
|
+
serverUrl: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DesktopDaemonChallenge {
|
|
8
|
+
owner: DesktopDaemonOwner;
|
|
9
|
+
port: number;
|
|
10
|
+
clientNonce: string;
|
|
11
|
+
serverNonce: string;
|
|
12
|
+
}
|
|
13
|
+
/** The same canonical owner is used by the daemon's owner boundary and MAC. */
|
|
14
|
+
export declare function normalizeDesktopDaemonOwner(input: unknown): DesktopDaemonOwner | null;
|
|
15
|
+
/** Role separation prevents reflecting the daemon's proof as client proof. */
|
|
16
|
+
export declare function desktopDaemonProof(secret: string, role: 'daemon' | 'desktop', challenge: DesktopDaemonChallenge): string;
|
|
17
|
+
/** Renewal uses a distinct domain so a proof for this narrow channel cannot
|
|
18
|
+
* be reflected or replayed as a normal desktop attachment. */
|
|
19
|
+
export declare function desktopRenewalProof(secret: string, role: 'daemon' | 'desktop', challenge: DesktopDaemonChallenge): string;
|
|
20
|
+
export declare function verifyDesktopDaemonProof(secret: string, role: 'daemon' | 'desktop', challenge: DesktopDaemonChallenge, proof: unknown): boolean;
|
|
21
|
+
export declare function verifyDesktopRenewalProof(secret: string, role: 'daemon' | 'desktop', challenge: DesktopDaemonChallenge, proof: unknown): boolean;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
export const DESKTOP_DAEMON_PROOF_NAMESPACE = '/desktop-proof-v1';
|
|
3
|
+
export const DESKTOP_DAEMON_RENEWAL_NAMESPACE = '/desktop-renewal-v1';
|
|
4
|
+
/** The same canonical owner is used by the daemon's owner boundary and MAC. */
|
|
5
|
+
export function normalizeDesktopDaemonOwner(input) {
|
|
6
|
+
if (!input || typeof input !== 'object' || Array.isArray(input))
|
|
7
|
+
return null;
|
|
8
|
+
const value = input;
|
|
9
|
+
if (typeof value.userId !== 'string' || !value.userId || value.userId.length > 128 ||
|
|
10
|
+
typeof value.serverUrl !== 'string' || value.serverUrl.length > 2048)
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
const url = new URL(value.serverUrl);
|
|
14
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:')
|
|
15
|
+
return null;
|
|
16
|
+
return { userId: value.userId, serverUrl: url.origin };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Role separation prevents reflecting the daemon's proof as client proof. */
|
|
23
|
+
export function desktopDaemonProof(secret, role, challenge) {
|
|
24
|
+
return proofForPurpose(secret, role, challenge, 'skrr-desktop-daemon-proof:v1\0');
|
|
25
|
+
}
|
|
26
|
+
/** Renewal uses a distinct domain so a proof for this narrow channel cannot
|
|
27
|
+
* be reflected or replayed as a normal desktop attachment. */
|
|
28
|
+
export function desktopRenewalProof(secret, role, challenge) {
|
|
29
|
+
return proofForPurpose(secret, role, challenge, 'skrr-desktop-renewal-proof:v1\0');
|
|
30
|
+
}
|
|
31
|
+
function proofForPurpose(secret, role, challenge, domain) {
|
|
32
|
+
const key = Buffer.from(secret, 'base64');
|
|
33
|
+
const owner = normalizeDesktopDaemonOwner(challenge.owner);
|
|
34
|
+
if (key.length !== 32 || key.toString('base64') !== secret || !owner ||
|
|
35
|
+
!Number.isInteger(challenge.port) || challenge.port < 1 || challenge.port > 65535 ||
|
|
36
|
+
!/^[a-f0-9]{64}$/.test(challenge.clientNonce) || !/^[a-f0-9]{64}$/.test(challenge.serverNonce)) {
|
|
37
|
+
throw new Error('Invalid desktop daemon challenge');
|
|
38
|
+
}
|
|
39
|
+
return createHmac('sha256', key).update(domain).update(JSON.stringify([
|
|
40
|
+
role, challenge.port, owner.userId, owner.serverUrl, challenge.clientNonce, challenge.serverNonce,
|
|
41
|
+
])).digest('hex');
|
|
42
|
+
}
|
|
43
|
+
export function verifyDesktopDaemonProof(secret, role, challenge, proof) {
|
|
44
|
+
if (typeof proof !== 'string' || !/^[a-f0-9]{64}$/.test(proof))
|
|
45
|
+
return false;
|
|
46
|
+
try {
|
|
47
|
+
return timingSafeEqual(Buffer.from(proof, 'hex'), Buffer.from(desktopDaemonProof(secret, role, challenge), 'hex'));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function verifyDesktopRenewalProof(secret, role, challenge, proof) {
|
|
54
|
+
if (typeof proof !== 'string' || !/^[a-f0-9]{64}$/.test(proof))
|
|
55
|
+
return false;
|
|
56
|
+
try {
|
|
57
|
+
return timingSafeEqual(Buffer.from(proof, 'hex'), Buffer.from(desktopRenewalProof(secret, role, challenge), 'hex'));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -51,9 +51,8 @@ export declare function describeDeviceKeySplit(profile: string): {
|
|
|
51
51
|
* `createIfMissing: false` stops before step 2 (disabled+no_stored_key) and
|
|
52
52
|
* never replaces anything: for a short-lived process that must only READ the
|
|
53
53
|
* daemon's key while the daemon itself may be creating it (OSK-15990's
|
|
54
|
-
* `skrrd device-proof`).
|
|
55
|
-
*
|
|
56
|
-
* memory and the disk another.
|
|
54
|
+
* `skrrd device-proof`). Readers and writers both take the profile lock so
|
|
55
|
+
* they cannot observe an incomplete write from another updated process.
|
|
57
56
|
*/
|
|
58
57
|
export declare function initDeviceIdentity(profile: string, options?: {
|
|
59
58
|
recoverUnreadable?: boolean;
|