@skrr-ai/cli 0.1.49 → 0.1.51

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.
Files changed (180) hide show
  1. package/README.md +5 -4
  2. package/dist/base-command.js +75 -6
  3. package/dist/commands/agents/chat.d.ts +14 -0
  4. package/dist/commands/agents/chat.js +37 -1
  5. package/dist/commands/agents/create.js +14 -7
  6. package/dist/commands/agents/show.d.ts +9 -0
  7. package/dist/commands/agents/show.js +53 -0
  8. package/dist/commands/agents/update.d.ts +9 -0
  9. package/dist/commands/agents/update.js +38 -5
  10. package/dist/commands/balance/show.d.ts +34 -0
  11. package/dist/commands/balance/show.js +92 -2
  12. package/dist/commands/code/index.d.ts +1 -0
  13. package/dist/commands/code/index.js +49 -5
  14. package/dist/commands/code/run.js +5 -1
  15. package/dist/commands/commitments/cycles.d.ts +19 -0
  16. package/dist/commands/commitments/cycles.js +46 -0
  17. package/dist/commands/commitments/doctor.d.ts +1 -0
  18. package/dist/commands/commitments/doctor.js +19 -4
  19. package/dist/commands/commitments/effective-policy.js +40 -1
  20. package/dist/commands/commitments/explain.d.ts +17 -0
  21. package/dist/commands/commitments/explain.js +40 -0
  22. package/dist/commands/commitments/next-tick.js +3 -1
  23. package/dist/commands/commitments/preflight.js +10 -0
  24. package/dist/commands/commitments/show.js +11 -1
  25. package/dist/commands/daemon/config.d.ts +15 -0
  26. package/dist/commands/daemon/config.js +38 -0
  27. package/dist/commands/daemon/index.js +1 -0
  28. package/dist/commands/daemon/status.js +15 -1
  29. package/dist/commands/goals/create.d.ts +1 -0
  30. package/dist/commands/goals/create.js +22 -0
  31. package/dist/commands/harnesses/models.d.ts +54 -0
  32. package/dist/commands/harnesses/models.js +100 -7
  33. package/dist/commands/harnesses/show.d.ts +11 -0
  34. package/dist/commands/harnesses/show.js +35 -0
  35. package/dist/commands/harnesses/update.d.ts +28 -1
  36. package/dist/commands/harnesses/update.js +73 -10
  37. package/dist/commands/logout.js +12 -1
  38. package/dist/commands/machines/dedicated/index.js +1 -1
  39. package/dist/commands/machines/dedicated/sign-in.js +4 -1
  40. package/dist/commands/machines/dedicated/terminal/kill.d.ts +21 -0
  41. package/dist/commands/machines/dedicated/terminal/kill.js +55 -0
  42. package/dist/commands/machines/dedicated/terminal/ls.d.ts +22 -0
  43. package/dist/commands/machines/dedicated/terminal/ls.js +70 -0
  44. package/dist/commands/machines/dedicated/terminal/rename.d.ts +23 -0
  45. package/dist/commands/machines/dedicated/terminal/rename.js +64 -0
  46. package/dist/commands/machines/dedicated/terminal.d.ts +9 -0
  47. package/dist/commands/machines/dedicated/terminal.js +27 -2
  48. package/dist/commands/machines/hosted/connect.js +2 -2
  49. package/dist/commands/machines/hosted/destroy.js +1 -1
  50. package/dist/commands/machines/hosted/exec.js +1 -1
  51. package/dist/commands/machines/hosted/list.js +1 -1
  52. package/dist/commands/machines/hosted/pause.js +1 -1
  53. package/dist/commands/machines/hosted/pull.js +1 -1
  54. package/dist/commands/machines/hosted/resume.js +1 -1
  55. package/dist/commands/machines/hosted/start.js +2 -2
  56. package/dist/commands/machines/hosted/status.js +2 -1
  57. package/dist/commands/outbox/list.d.ts +12 -1
  58. package/dist/commands/outbox/list.js +31 -2
  59. package/dist/commands/revoke-daemon.js +9 -0
  60. package/dist/commands/spaces/create.d.ts +1 -0
  61. package/dist/commands/spaces/create.js +17 -0
  62. package/dist/commands/spaces/show.js +5 -0
  63. package/dist/commands/spaces/update.d.ts +1 -0
  64. package/dist/commands/spaces/update.js +16 -0
  65. package/dist/commands/tasks/create.d.ts +1 -0
  66. package/dist/commands/tasks/create.js +6 -0
  67. package/dist/commands/tasks/list.js +1 -0
  68. package/dist/commands/tasks/show.js +45 -0
  69. package/dist/commands/tasks/update.d.ts +1 -0
  70. package/dist/commands/tasks/update.js +22 -0
  71. package/dist/commands/tasks/upsert.d.ts +1 -0
  72. package/dist/commands/tasks/upsert.js +16 -0
  73. package/dist/lib/agent-config.d.ts +2 -0
  74. package/dist/lib/agent-config.js +3 -1
  75. package/dist/lib/agentic-stream.d.ts +68 -1
  76. package/dist/lib/agentic-stream.js +294 -7
  77. package/dist/lib/api-fetch.js +19 -0
  78. package/dist/lib/auth-core-init.d.ts +2 -10
  79. package/dist/lib/auth-core-init.js +20 -67
  80. package/dist/lib/auth-failure.d.ts +33 -0
  81. package/dist/lib/auth-failure.js +65 -0
  82. package/dist/lib/auth-storage.d.ts +14 -0
  83. package/dist/lib/auth-storage.js +14 -0
  84. package/dist/lib/commitments.d.ts +37 -2
  85. package/dist/lib/commitments.js +148 -0
  86. package/dist/lib/daemonBroker.d.ts +134 -31
  87. package/dist/lib/daemonBroker.js +376 -31
  88. package/dist/lib/dedicated-lease-command.d.ts +14 -0
  89. package/dist/lib/dedicated-lease-command.js +30 -1
  90. package/dist/lib/dedicated-machines.js +8 -25
  91. package/dist/lib/dedicated-service-command.d.ts +11 -3
  92. package/dist/lib/dedicated-service-command.js +20 -3
  93. package/dist/lib/dedicated-service.d.ts +46 -0
  94. package/dist/lib/dedicated-service.js +85 -7
  95. package/dist/lib/dedicated-terminal.d.ts +21 -0
  96. package/dist/lib/dedicated-terminal.js +104 -9
  97. package/dist/lib/delegated-cli.js +4 -1
  98. package/dist/lib/exec-runtime-binary.d.ts +3 -1
  99. package/dist/lib/exec-runtime-binary.js +4 -2
  100. package/dist/lib/first-party-harness-broker.d.ts +10 -0
  101. package/dist/lib/first-party-harness-broker.js +13 -1
  102. package/dist/lib/first-party-harness-doctor.js +41 -1
  103. package/dist/lib/first-party-harness-managed.d.ts +4 -3
  104. package/dist/lib/first-party-harness-project-trust.d.ts +125 -0
  105. package/dist/lib/first-party-harness-project-trust.js +364 -0
  106. package/dist/lib/first-party-harness.d.ts +9 -2
  107. package/dist/lib/first-party-harness.js +6 -5
  108. package/dist/lib/harnesses.d.ts +10 -0
  109. package/dist/lib/hosted-machines.d.ts +10 -1
  110. package/dist/lib/hosted-machines.js +36 -2
  111. package/dist/lib/live-runtime-binary.d.ts +40 -0
  112. package/dist/lib/live-runtime-binary.js +193 -0
  113. package/dist/lib/login.d.ts +23 -0
  114. package/dist/lib/login.js +60 -10
  115. package/dist/lib/machine-spend-cap.d.ts +23 -0
  116. package/dist/lib/machine-spend-cap.js +68 -0
  117. package/dist/lib/message-intent.js +3 -1
  118. package/dist/lib/node-adapter.js +15 -3
  119. package/dist/lib/tasks.d.ts +20 -0
  120. package/dist/lib/tasks.js +54 -0
  121. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +90 -0
  122. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +113 -0
  123. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +2 -0
  124. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +26 -1
  125. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.d.ts +47 -0
  126. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.js +69 -0
  127. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.d.ts +133 -0
  128. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +189 -0
  129. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +41 -3
  130. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -1
  131. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +25 -3
  132. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loopbackHttp.d.ts +32 -0
  133. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loopbackHttp.js +268 -0
  134. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineId.d.ts +2 -2
  135. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineId.js +11 -10
  136. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +36 -0
  137. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +156 -0
  138. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +8 -3
  139. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +9 -4
  140. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.d.ts +8 -0
  141. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.js +16 -10
  142. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refreshClassification.d.ts +6 -0
  143. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refreshClassification.js +32 -9
  144. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.d.ts +81 -0
  145. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.js +87 -0
  146. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +22 -0
  147. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +61 -2
  148. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/types.d.ts +6 -1
  149. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +90 -0
  150. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +105 -0
  151. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +2 -0
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +25 -1
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.d.ts +47 -0
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.js +66 -0
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.d.ts +133 -0
  156. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +182 -0
  157. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +41 -3
  158. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -1
  159. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +10 -1
  160. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loopbackHttp.d.ts +32 -0
  161. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loopbackHttp.js +257 -0
  162. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineId.d.ts +2 -2
  163. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineId.js +11 -10
  164. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +36 -0
  165. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +147 -0
  166. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +8 -3
  167. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +9 -4
  168. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.d.ts +8 -0
  169. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.js +16 -10
  170. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refreshClassification.d.ts +6 -0
  171. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refreshClassification.js +31 -9
  172. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.d.ts +81 -0
  173. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.js +80 -0
  174. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +22 -0
  175. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +58 -1
  176. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/types.d.ts +6 -1
  177. package/dist/node_modules/@skrr-ai/auth-core/package.json +61 -1
  178. package/dist/node_modules/@skrr-ai/data-provider/index.js +3418 -3344
  179. package/oclif.manifest.json +24813 -24242
  180. package/package.json +4 -1
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Server-attested tool approval (OSK-11975 follow-up to the `manual` mode
3
+ * refusal on `daemon:tool:request`).
4
+ *
5
+ * A daemon category in `manual` mode means "a PERSON approves each call". On
6
+ * the harness-session path the daemon asks through the permission bridge; on
7
+ * the daemon-tool path a cloud agent's call used to be refused outright,
8
+ * because the request carried no approval and there was no channel back to a
9
+ * person. The attested-approval protocol closes that gap without weakening
10
+ * the guarantee:
11
+ *
12
+ * 1. The daemon announces `daemon_tool_attested_approval_v1` and its
13
+ * resolved per-category posture (`toolPermissionModes`) at register time.
14
+ * 2. Before proxying a call, the server reads the announced posture for the
15
+ * tool's category. `manual` means it must ask first — through the same
16
+ * permission surface (`canUseTool` → `permission:request`) an in-process
17
+ * SDK turn would have used — and only then dispatch, carrying an
18
+ * `attestedApproval` object on the request.
19
+ * 3. The daemon verifies the attestation's SHAPE and BINDING — it is bound
20
+ * to this exact (toolUseId, toolName, toolInput digest) and expires
21
+ * quickly — and allows the call. A missing or malformed attestation gets
22
+ * the same named refusal as before.
23
+ *
24
+ * What the daemon verifies is the binding, not the server's honesty: the
25
+ * request already arrives over the daemon's authenticated server channel, so
26
+ * the field IS the attestation — it is unsigned, and `decidedBy` is a literal.
27
+ * What the binding buys is that the model — which authors `toolInput` —
28
+ * cannot mint an approval, and an approval for one call cannot be replayed
29
+ * onto another. The verifier also bounds the validity window itself (never
30
+ * trusting the issuer's `expiresAt` beyond `ATTESTED_TOOL_APPROVAL_TTL_MS`),
31
+ * and the daemon spends each approval once: a second delivery of the same
32
+ * attestation inside its window is refused as `replayed`. That ledger lives
33
+ * in daemon memory, so a daemon restart inside the (≤60s) window forgets it.
34
+ */
35
+ /** Announced by a daemon that honours `attestedApproval` on daemon:tool:request. */
36
+ export declare const DAEMON_TOOL_ATTESTED_APPROVAL_CAPABILITY = "daemon_tool_attested_approval_v1";
37
+ /**
38
+ * Announced by a daemon that honours `toolPosture: 'read_only'` on
39
+ * daemon:tool:request — the daemon-tool-path expression of the wire `plan`
40
+ * session mode: reads run, every mutating category is refused with a named
41
+ * reason. Carries INTENT like `browserPolicy: 'read_only'` — the daemon owns
42
+ * what read-only means, so the server cannot widen the fence by shipping a
43
+ * wider value.
44
+ */
45
+ export declare const DAEMON_TOOL_READ_ONLY_CAPABILITY = "daemon_tool_read_only_v1";
46
+ /**
47
+ * Tool categories the daemon recognizes for permission purposes. This is the
48
+ * wire vocabulary the daemon's announced `toolPermissionModes` is keyed on —
49
+ * shared so the server's pre-dispatch check and the daemon's own enforcement
50
+ * classify a tool name identically (a third map was exactly the drift this
51
+ * package exists to prevent).
52
+ */
53
+ export type DaemonToolCategory = 'read' | 'write' | 'bash' | 'browser' | 'claude_session' | 'native_ui';
54
+ export declare const DAEMON_TOOL_CATEGORIES: readonly DaemonToolCategory[];
55
+ /** Map a tool name to its category. Unknown tools take the restrictive answer. */
56
+ export declare function categoryForDaemonTool(toolName: string): DaemonToolCategory;
57
+ /**
58
+ * The posture a daemon announces per category for the daemon-tool path:
59
+ * - `auto` — the call runs once guardrails pass; no attestation needed
60
+ * - `manual` — the call needs a person; the server must attach a fresh
61
+ * `attestedApproval` or the daemon refuses it
62
+ * - `delegated` — the category's own policy decides per action (browser,
63
+ * native UI); asking up front would ask the wrong question
64
+ */
65
+ export type DaemonToolPathPosture = 'auto' | 'manual' | 'delegated';
66
+ export type DaemonToolPermissionModes = Readonly<Partial<Record<DaemonToolCategory, DaemonToolPathPosture>>>;
67
+ /** The wire field values `toolPosture` accepts. Unknown values must be ignored. */
68
+ export type DaemonToolPosture = 'read_only';
69
+ /**
70
+ * How long an attestation stays valid. Short: it answers ONE person, about
71
+ * THIS call, moments ago — it is not a standing grant. The server asks and
72
+ * dispatches back-to-back, so the window only has to outlive publish latency.
73
+ */
74
+ export declare const ATTESTED_TOOL_APPROVAL_TTL_MS: number;
75
+ export interface AttestedToolApproval {
76
+ v: 1;
77
+ /** Binds the approval to this exact call — a different call gets nothing. */
78
+ toolUseId: string;
79
+ toolName: string;
80
+ /** SHA-256 of the canonical tool input (see `canonicalToolInputDigest`). */
81
+ inputSha256: string;
82
+ /** Who answered on the server's permission surface. */
83
+ decidedBy: 'user';
84
+ issuedAt: number;
85
+ expiresAt: number;
86
+ }
87
+ /** The digest both sides compute over `toolInput`. */
88
+ export declare function canonicalToolInputDigest(toolInput: unknown): string;
89
+ /** Server side: mint the attestation after a person approved THIS call. */
90
+ export declare function mintAttestedToolApproval(args: {
91
+ toolUseId: string;
92
+ toolName: string;
93
+ toolInput: unknown;
94
+ now?: number;
95
+ ttlMs?: number;
96
+ }): AttestedToolApproval;
97
+ /**
98
+ * Why an attestation did not approve a call — logged by the daemon, never
99
+ * shown to the caller beyond "absent" vs "did not verify".
100
+ * - `absent` — no attestation arrived (the server did not ask anyone)
101
+ * - `malformed` — not the v1 shape
102
+ * - `expired` — past `expiresAt`, or issued implausibly far in the future
103
+ * - `window` — the issuer claimed a validity window longer than
104
+ * `ATTESTED_TOOL_APPROVAL_TTL_MS` (or an empty/negative one)
105
+ * - `binding` — minted for a different call (toolUseId, tool, or input)
106
+ * - `replayed` — this exact approval was already spent on this daemon
107
+ * (the ledger is the verifier's caller's, see the daemon's
108
+ * `permissions/attestation-ledger.ts`)
109
+ */
110
+ export type AttestedToolApprovalRefusalReason = 'absent' | 'malformed' | 'binding' | 'expired' | 'window' | 'replayed';
111
+ export type AttestedToolApprovalVerdict = {
112
+ ok: true;
113
+ } | {
114
+ ok: false;
115
+ reason: AttestedToolApprovalRefusalReason;
116
+ };
117
+ /**
118
+ * Clock skew tolerated on `issuedAt`: a server clock slightly ahead of the
119
+ * daemon's must not refuse a fresh approval.
120
+ */
121
+ export declare const ATTESTED_TOOL_APPROVAL_CLOCK_SKEW_MS: number;
122
+ /**
123
+ * Daemon side: verify an attestation against the call it claims to approve.
124
+ * Untrusted wire input — every field is checked, and any mismatch is a plain
125
+ * "no approval", never a partial credit. `reason` is for daemon logs; the
126
+ * refusal text stays generic so a probing caller learns nothing new.
127
+ */
128
+ export declare function verifyAttestedToolApproval(approval: unknown, call: {
129
+ toolUseId: string;
130
+ toolName: string;
131
+ toolInput: unknown;
132
+ now?: number;
133
+ }): AttestedToolApprovalVerdict;
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ATTESTED_TOOL_APPROVAL_CLOCK_SKEW_MS = exports.ATTESTED_TOOL_APPROVAL_TTL_MS = exports.DAEMON_TOOL_CATEGORIES = exports.DAEMON_TOOL_READ_ONLY_CAPABILITY = exports.DAEMON_TOOL_ATTESTED_APPROVAL_CAPABILITY = void 0;
4
+ exports.categoryForDaemonTool = categoryForDaemonTool;
5
+ exports.canonicalToolInputDigest = canonicalToolInputDigest;
6
+ exports.mintAttestedToolApproval = mintAttestedToolApproval;
7
+ exports.verifyAttestedToolApproval = verifyAttestedToolApproval;
8
+ const node_crypto_1 = require("node:crypto");
9
+ /**
10
+ * Server-attested tool approval (OSK-11975 follow-up to the `manual` mode
11
+ * refusal on `daemon:tool:request`).
12
+ *
13
+ * A daemon category in `manual` mode means "a PERSON approves each call". On
14
+ * the harness-session path the daemon asks through the permission bridge; on
15
+ * the daemon-tool path a cloud agent's call used to be refused outright,
16
+ * because the request carried no approval and there was no channel back to a
17
+ * person. The attested-approval protocol closes that gap without weakening
18
+ * the guarantee:
19
+ *
20
+ * 1. The daemon announces `daemon_tool_attested_approval_v1` and its
21
+ * resolved per-category posture (`toolPermissionModes`) at register time.
22
+ * 2. Before proxying a call, the server reads the announced posture for the
23
+ * tool's category. `manual` means it must ask first — through the same
24
+ * permission surface (`canUseTool` → `permission:request`) an in-process
25
+ * SDK turn would have used — and only then dispatch, carrying an
26
+ * `attestedApproval` object on the request.
27
+ * 3. The daemon verifies the attestation's SHAPE and BINDING — it is bound
28
+ * to this exact (toolUseId, toolName, toolInput digest) and expires
29
+ * quickly — and allows the call. A missing or malformed attestation gets
30
+ * the same named refusal as before.
31
+ *
32
+ * What the daemon verifies is the binding, not the server's honesty: the
33
+ * request already arrives over the daemon's authenticated server channel, so
34
+ * the field IS the attestation — it is unsigned, and `decidedBy` is a literal.
35
+ * What the binding buys is that the model — which authors `toolInput` —
36
+ * cannot mint an approval, and an approval for one call cannot be replayed
37
+ * onto another. The verifier also bounds the validity window itself (never
38
+ * trusting the issuer's `expiresAt` beyond `ATTESTED_TOOL_APPROVAL_TTL_MS`),
39
+ * and the daemon spends each approval once: a second delivery of the same
40
+ * attestation inside its window is refused as `replayed`. That ledger lives
41
+ * in daemon memory, so a daemon restart inside the (≤60s) window forgets it.
42
+ */
43
+ /** Announced by a daemon that honours `attestedApproval` on daemon:tool:request. */
44
+ exports.DAEMON_TOOL_ATTESTED_APPROVAL_CAPABILITY = 'daemon_tool_attested_approval_v1';
45
+ /**
46
+ * Announced by a daemon that honours `toolPosture: 'read_only'` on
47
+ * daemon:tool:request — the daemon-tool-path expression of the wire `plan`
48
+ * session mode: reads run, every mutating category is refused with a named
49
+ * reason. Carries INTENT like `browserPolicy: 'read_only'` — the daemon owns
50
+ * what read-only means, so the server cannot widen the fence by shipping a
51
+ * wider value.
52
+ */
53
+ exports.DAEMON_TOOL_READ_ONLY_CAPABILITY = 'daemon_tool_read_only_v1';
54
+ exports.DAEMON_TOOL_CATEGORIES = [
55
+ 'read',
56
+ 'write',
57
+ 'bash',
58
+ 'browser',
59
+ 'claude_session',
60
+ 'native_ui',
61
+ ];
62
+ /** Map a tool name to its category. Unknown tools take the restrictive answer. */
63
+ function categoryForDaemonTool(toolName) {
64
+ switch (String(toolName || '').toLowerCase()) {
65
+ case 'read':
66
+ case 'glob':
67
+ case 'grep':
68
+ case 'filetransferread':
69
+ return 'read';
70
+ case 'write':
71
+ case 'edit':
72
+ case 'multiedit':
73
+ case 'apply_patch':
74
+ case 'filetransferwrite':
75
+ return 'write';
76
+ // Reading or stopping a background command is part of running it.
77
+ case 'bash':
78
+ case 'taskoutput':
79
+ case 'taskstop':
80
+ return 'bash';
81
+ case 'browser':
82
+ return 'browser';
83
+ case 'native':
84
+ return 'native_ui';
85
+ case 'claude_session':
86
+ case 'claude-session':
87
+ return 'claude_session';
88
+ default:
89
+ return 'write';
90
+ }
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Attested approval
94
+ // ---------------------------------------------------------------------------
95
+ /**
96
+ * How long an attestation stays valid. Short: it answers ONE person, about
97
+ * THIS call, moments ago — it is not a standing grant. The server asks and
98
+ * dispatches back-to-back, so the window only has to outlive publish latency.
99
+ */
100
+ exports.ATTESTED_TOOL_APPROVAL_TTL_MS = 60 * 1000;
101
+ /**
102
+ * Deterministic serialization for the input digest: objects sort keys
103
+ * recursively, arrays keep order, non-JSON values normalize the way
104
+ * `JSON.stringify` would emit them (undefined/functions in objects drop, in
105
+ * arrays become null). Both runtimes must digest the SAME bytes — the server
106
+ * mints and the daemon verifies — so this lives here, once.
107
+ */
108
+ function canonicalJson(value) {
109
+ if (value === null || typeof value !== 'object') {
110
+ if (value === undefined || typeof value === 'function' || typeof value === 'symbol') {
111
+ return 'null';
112
+ }
113
+ return JSON.stringify(value);
114
+ }
115
+ if (Array.isArray(value)) {
116
+ return `[${value.map((entry) => canonicalJson(entry)).join(',')}]`;
117
+ }
118
+ const record = value;
119
+ const keys = Object.keys(record).filter((key) => record[key] !== undefined &&
120
+ typeof record[key] !== 'function' &&
121
+ typeof record[key] !== 'symbol');
122
+ keys.sort();
123
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(',')}}`;
124
+ }
125
+ /** The digest both sides compute over `toolInput`. */
126
+ function canonicalToolInputDigest(toolInput) {
127
+ return (0, node_crypto_1.createHash)('sha256')
128
+ .update(canonicalJson(toolInput ?? {}))
129
+ .digest('hex');
130
+ }
131
+ /** Server side: mint the attestation after a person approved THIS call. */
132
+ function mintAttestedToolApproval(args) {
133
+ const now = args.now ?? Date.now();
134
+ return {
135
+ v: 1,
136
+ toolUseId: args.toolUseId,
137
+ toolName: args.toolName,
138
+ inputSha256: canonicalToolInputDigest(args.toolInput),
139
+ decidedBy: 'user',
140
+ issuedAt: now,
141
+ expiresAt: now + (args.ttlMs ?? exports.ATTESTED_TOOL_APPROVAL_TTL_MS),
142
+ };
143
+ }
144
+ /**
145
+ * Clock skew tolerated on `issuedAt`: a server clock slightly ahead of the
146
+ * daemon's must not refuse a fresh approval.
147
+ */
148
+ exports.ATTESTED_TOOL_APPROVAL_CLOCK_SKEW_MS = 30 * 1000;
149
+ /**
150
+ * Daemon side: verify an attestation against the call it claims to approve.
151
+ * Untrusted wire input — every field is checked, and any mismatch is a plain
152
+ * "no approval", never a partial credit. `reason` is for daemon logs; the
153
+ * refusal text stays generic so a probing caller learns nothing new.
154
+ */
155
+ function verifyAttestedToolApproval(approval, call) {
156
+ if (approval === undefined || approval === null)
157
+ return { ok: false, reason: 'absent' };
158
+ if (typeof approval !== 'object' || Array.isArray(approval)) {
159
+ return { ok: false, reason: 'malformed' };
160
+ }
161
+ const a = approval;
162
+ if (a.v !== 1 ||
163
+ typeof a.toolUseId !== 'string' ||
164
+ typeof a.toolName !== 'string' ||
165
+ typeof a.inputSha256 !== 'string' ||
166
+ a.decidedBy !== 'user' ||
167
+ typeof a.issuedAt !== 'number' ||
168
+ typeof a.expiresAt !== 'number') {
169
+ return { ok: false, reason: 'malformed' };
170
+ }
171
+ const now = call.now ?? Date.now();
172
+ if (a.expiresAt <= now || a.issuedAt > now + exports.ATTESTED_TOOL_APPROVAL_CLOCK_SKEW_MS) {
173
+ return { ok: false, reason: 'expired' };
174
+ }
175
+ // The TTL is the VERIFIER's bound, not the issuer's claim (OSK-12108): an
176
+ // attestation whose own window is longer than one approval may stay valid
177
+ // is refused whatever `expiresAt` says, so "answers one person about this
178
+ // call, moments ago" holds even against an issuer that set a year.
179
+ const window = a.expiresAt - a.issuedAt;
180
+ if (!(window > 0) || window > exports.ATTESTED_TOOL_APPROVAL_TTL_MS) {
181
+ return { ok: false, reason: 'window' };
182
+ }
183
+ if (a.toolUseId !== call.toolUseId ||
184
+ a.toolName.toLowerCase() !== String(call.toolName).toLowerCase() ||
185
+ a.inputSha256 !== canonicalToolInputDigest(call.toolInput)) {
186
+ return { ok: false, reason: 'binding' };
187
+ }
188
+ return { ok: true };
189
+ }
@@ -604,11 +604,49 @@ async function enrollWithServer(opts) {
604
604
  // Enrollment is the CLI's (JWT) job; the daemon holding an opaque
605
605
  // token should not enroll. Guarded at the daemon call site, this is
606
606
  // defense-in-depth for any other opaque caller.
607
- // • 409 already-enrolled — a concurrent binary won the enrollment race
608
- // for the shared keypair; idempotent, nothing to do.
607
+ // • 409 ALREADY_ENROLLED with OUR thumbprint — a concurrent binary won
608
+ // the enrollment race for this keypair; idempotent, nothing to do.
609
+ //
610
+ // A 409 is NOT benign when the row is pinned to a DIFFERENT key
611
+ // (OSK-12063). The server keeps one pin per (user, machine) row while
612
+ // every local profile has its own key, so a 409 can mean "every refresh
613
+ // this key signs will be refused with DEVICE_PROOF_THUMBPRINT_MISMATCH".
614
+ // The server names the pinned thumbprint (`enrolledThumbprint`) so the
615
+ // two cases can be told apart. An older server that omits it keeps the
616
+ // old reading, because there is nothing to compare. A 409
617
+ // ENROLLMENT_DUPLICATE_KEY — this key is pinned on another active row —
618
+ // is never benign either.
619
+ let conflictCode = null;
620
+ let enrolledThumbprint = null;
621
+ if (response.status === 409) {
622
+ try {
623
+ const parsed = JSON.parse(responseText);
624
+ conflictCode = typeof parsed.code === 'string' ? parsed.code : null;
625
+ enrolledThumbprint =
626
+ typeof parsed.enrolledThumbprint === 'string' ? parsed.enrolledThumbprint : null;
627
+ }
628
+ catch {
629
+ /* non-JSON 409 — treat as the legacy benign race */
630
+ }
631
+ }
632
+ const pinnedToOtherKey = response.status === 409 &&
633
+ enrolledThumbprint !== null &&
634
+ enrolledThumbprint !== keypair.thumbprint;
635
+ const keyPinnedElsewhere = response.status === 409 && conflictCode === 'ENROLLMENT_DUPLICATE_KEY';
609
636
  const isBenignRejection = (response.status === 404 && responseText.includes('DAEMON_NOT_FOUND')) ||
610
637
  (response.status === 403 && responseText.includes('OPAQUE_AUTH_REFUSED')) ||
611
- response.status === 409;
638
+ (response.status === 409 && !pinnedToOtherKey && !keyPinnedElsewhere);
639
+ if (pinnedToOtherKey) {
640
+ emit('enrollment_key_mismatch', false, {
641
+ profile,
642
+ thumbprint: keypair.thumbprint,
643
+ enrolledThumbprint,
644
+ serverUrl,
645
+ message: 'this machine is registered with a different device key than this profile signs ' +
646
+ 'with; its refreshes will be refused. An interactive daemon login (skrrd login) ' +
647
+ 're-registers this key.',
648
+ }, { logLevel: 'warn' });
649
+ }
612
650
  if (response.status === 404 && responseText.includes('DAEMON_NOT_FOUND')) {
613
651
  try {
614
652
  atomicWrite0600(backoffPath, JSON.stringify({
@@ -29,11 +29,13 @@ export { __setKekStrategyForTest, getKekStrategy, InMemoryKek, UnavailableKek, K
29
29
  export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, normalizeHtu, publicJwkFromX, tokenHash, verifyProof as verifyDeviceProof, type DeviceKeyPair, type DeviceProofClaims, type DeviceProofHeader, type Ed25519PrivateJwk, type Ed25519PublicJwk, type VerifyFailureReason as DeviceProofVerifyFailureReason, type VerifyFailure as DeviceProofVerifyFailure, type VerifyOptions as DeviceProofVerifyOptions, type VerifySuccess as DeviceProofVerifySuccess, } from './deviceKey.js';
30
30
  export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, maybeEncryptForWrite, maybeDecryptOnRead, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
31
31
  export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
32
+ export { getMachineUuid, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
32
33
  export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest, } from './machineId.js';
33
34
  export { PAIR_AAD_PREFIX, PAIR_AAD_MAX_LEN, PAIR_BUNDLE_MAX_BASE64_LEN, PAIR_NONCE_LEN, PAIR_PUBKEY_LEN, PAIR_SECRET_LEN, PairBundleError, assemblePairBundleBase64, base64ToUint8Array as pairBase64ToUint8Array, base64UrlToUint8Array as pairBase64UrlToUint8Array, buildPairAad, buildPairUrl, generatePairKeyPair, openPairBundleBase64, parsePairBundle, parsePairPlaintext, parsePairUrl, uint8ArrayToBase64 as pairUint8ArrayToBase64, uint8ArrayToBase64Url as pairUint8ArrayToBase64Url, type AssembleBundleOptions as PairAssembleBundleOptions, type OpenBundleOptions as PairOpenBundleOptions, type PairBundle, type PairBundleErrorCode, type PairKeyPair, type PairPlaintextV1, type PairUrlFields, } from './daemonPair.js';
34
35
  export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
35
36
  export { HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT, claudeSkillsLayoutEvidence, harnessLoadsClaudeSkillsLayout, } from './harnessSkillLayout.js';
36
- export { sanitizeSpawnEnv, SENSITIVE_ENV_VARS, SENSITIVE_ENV_PREFIXES, SENSITIVE_ENV_SUFFIXES, SENSITIVE_ENV_NAME_TOKENS, SENSITIVE_ENV_PREFIX_EXCEPTIONS, } from './spawnEnv.js';
37
+ export { sanitizeSpawnEnv, SENSITIVE_ENV_VARS, SENSITIVE_ENV_PREFIXES, SENSITIVE_ENV_SUFFIXES, SENSITIVE_ENV_NAME_TOKENS, SENSITIVE_ENV_PREFIX_EXCEPTIONS, LOOPBACK_NO_PROXY_HOSTS, envHasProxy, withLoopbackNoProxy, } from './spawnEnv.js';
38
+ export { loopbackFetch, assertLoopbackHttpUrl, isLoopbackHostname, isLoopbackAddress, LoopbackTargetRefusedError, LOOPBACK_DEFAULT_MAX_BODY_BYTES, type LoopbackFetch, type LoopbackFetchInit, } from './loopbackHttp.js';
37
39
  export { CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAMES, NATIVE_ID_PREFIX, LEGACY_NATIVE_ID_PREFIXES, DEFAULT_BINARY_NAME, } from './localIdentity.js';
38
40
  export { findLegacyLocalState, describeLegacyState, type LegacyStateFinding, type LegacyStateReport, } from './legacyStatePreflight.js';
39
41
  export * from './firstPartyHarness.js';
@@ -24,9 +24,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.generateVerifier = exports.invalidateHeadersHelperCache = exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__resetAuthHelperFailureReasonForTest = exports.getLastAuthHelperFailureReason = exports.getDefaultHelperTtlMs = exports.invokeAuthHelper = exports.AuthHelperUnavailableError = exports.AuthHelperUntrustedError = exports.CI_TOKEN_PREFIX = exports.classifyTokenKind = exports.invalidateAuthHelperCache = exports.__resetCredentialResolverForTest = exports.CREDENTIAL_PRECEDENCE = exports.CredentialResolver = exports.__setFdReaderForTest = exports.readAuthFromFd = exports.createRefreshScheduler = exports.UNKNOWN_401_BUDGET = exports.__resetUnknown401StreaksForTest = exports.__refreshDiagnostics = exports.installLockExitHandlers = exports.clearReauthState = exports.needsReauthState = exports.persistReauthState = exports.withAuthLock = exports.revokeDaemonRefreshSession = exports.refreshDaemonToken = exports.classifyRefreshResponse = exports.EXIT_NEEDS_REAUTH = exports.reauthCommand = exports.formatReauthMessage = exports.handleMidStreamAuthExpiry = exports.isAuthExpiredSseEvent = exports.decodeJwtPayload = exports.decodeJwtExpiry = exports.TransientAuthFailure = exports.PermanentAuthFailure = exports.emitAuthTelemetry = exports.getAuthBinaryName = exports.getAuthKillSwitches = exports.getAuthConfigDir = exports.getAuthLogger = exports.configureAuthCore = exports.OAUTH_CLI_SCOPE = exports.isDaemonScope = exports.VALID_DAEMON_SCOPE_SET = exports.ALL_DAEMON_SCOPES = exports.SCOPES = void 0;
26
26
  exports.publicJwkFromX = exports.normalizeHtu = 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 = void 0;
27
- 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.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = 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.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDeviceProof = exports.tokenHash = void 0;
28
- 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 = 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 = void 0;
29
- exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = void 0;
27
+ 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.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = 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.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDeviceProof = exports.tokenHash = void 0;
28
+ 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 = 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.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 = 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 = 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
@@ -258,7 +258,16 @@ Object.defineProperty(exports, "__enrollmentMarkerPathForTest", { enumerable: tr
258
258
  Object.defineProperty(exports, "__enrollmentBackoffPathForTest", { enumerable: true, get: function () { return deviceIdentityBridge_js_1.__enrollmentBackoffPathForTest; } });
259
259
  Object.defineProperty(exports, "__setDeviceIdentityStateForTest", { enumerable: true, get: function () { return deviceIdentityBridge_js_1.__setStateForTest; } });
260
260
  Object.defineProperty(exports, "__getDeviceIdentityStateForTest", { enumerable: true, get: function () { return deviceIdentityBridge_js_1.__getStateForTest; } });
261
+ // Hardware machine identity sent as `machineUuid` (IOPlatformUUID /
262
+ // machine-id / MachineGuid). ONE implementation for daemon and CLI —
263
+ // OSK-12063. Also exposed dependency-free as `@skrr-ai/auth-core/machine-uuid`.
264
+ var machineUuid_js_1 = require("./machineUuid.js");
265
+ Object.defineProperty(exports, "getMachineUuid", { enumerable: true, get: function () { return machineUuid_js_1.getMachineUuid; } });
266
+ Object.defineProperty(exports, "hostedRuntimeIdOverride", { enumerable: true, get: function () { return machineUuid_js_1.hostedRuntimeIdOverride; } });
267
+ Object.defineProperty(exports, "HOSTED_RUNTIME_MACHINE_SOURCES", { enumerable: true, get: function () { return machineUuid_js_1.HOSTED_RUNTIME_MACHINE_SOURCES; } });
268
+ Object.defineProperty(exports, "__resetMachineUuidCacheForTest", { enumerable: true, get: function () { return machineUuid_js_1.__resetMachineUuidCacheForTest; } });
261
269
  // Stable per-machine UUIDv7 identity persisted at <configDir>/daemon.id.
270
+ // NOT a `machineUuid` — see machineUuid.ts.
262
271
  var machineId_js_1 = require("./machineId.js");
263
272
  Object.defineProperty(exports, "ensureMachineDaemonId", { enumerable: true, get: function () { return machineId_js_1.ensureMachineDaemonId; } });
264
273
  Object.defineProperty(exports, "legacyDaemonUuids", { enumerable: true, get: function () { return machineId_js_1.legacyDaemonUuids; } });
@@ -316,6 +325,19 @@ Object.defineProperty(exports, "SENSITIVE_ENV_PREFIXES", { enumerable: true, get
316
325
  Object.defineProperty(exports, "SENSITIVE_ENV_SUFFIXES", { enumerable: true, get: function () { return spawnEnv_js_1.SENSITIVE_ENV_SUFFIXES; } });
317
326
  Object.defineProperty(exports, "SENSITIVE_ENV_NAME_TOKENS", { enumerable: true, get: function () { return spawnEnv_js_1.SENSITIVE_ENV_NAME_TOKENS; } });
318
327
  Object.defineProperty(exports, "SENSITIVE_ENV_PREFIX_EXCEPTIONS", { enumerable: true, get: function () { return spawnEnv_js_1.SENSITIVE_ENV_PREFIX_EXCEPTIONS; } });
328
+ Object.defineProperty(exports, "LOOPBACK_NO_PROXY_HOSTS", { enumerable: true, get: function () { return spawnEnv_js_1.LOOPBACK_NO_PROXY_HOSTS; } });
329
+ Object.defineProperty(exports, "envHasProxy", { enumerable: true, get: function () { return spawnEnv_js_1.envHasProxy; } });
330
+ Object.defineProperty(exports, "withLoopbackNoProxy", { enumerable: true, get: function () { return spawnEnv_js_1.withLoopbackNoProxy; } });
331
+ // OSK-12134 — the one proxy-free client for loopback HTTP. Bun's fetch (and
332
+ // Node's under NODE_USE_ENV_PROXY) sends 127.0.0.1 requests to HTTP_PROXY,
333
+ // credentials and all; every loopback caller goes through this instead.
334
+ var loopbackHttp_js_1 = require("./loopbackHttp.js");
335
+ Object.defineProperty(exports, "loopbackFetch", { enumerable: true, get: function () { return loopbackHttp_js_1.loopbackFetch; } });
336
+ Object.defineProperty(exports, "assertLoopbackHttpUrl", { enumerable: true, get: function () { return loopbackHttp_js_1.assertLoopbackHttpUrl; } });
337
+ Object.defineProperty(exports, "isLoopbackHostname", { enumerable: true, get: function () { return loopbackHttp_js_1.isLoopbackHostname; } });
338
+ Object.defineProperty(exports, "isLoopbackAddress", { enumerable: true, get: function () { return loopbackHttp_js_1.isLoopbackAddress; } });
339
+ Object.defineProperty(exports, "LoopbackTargetRefusedError", { enumerable: true, get: function () { return loopbackHttp_js_1.LoopbackTargetRefusedError; } });
340
+ Object.defineProperty(exports, "LOOPBACK_DEFAULT_MAX_BODY_BYTES", { enumerable: true, get: function () { return loopbackHttp_js_1.LOOPBACK_DEFAULT_MAX_BODY_BYTES; } });
319
341
  /* Local-machine identity — the names this product leaves on a disk. Exported so
320
342
  the CLI, daemon and Desktop share one definition instead of 47 literals. */
321
343
  var localIdentity_js_1 = require("./localIdentity.js");
@@ -0,0 +1,32 @@
1
+ /** Upper bound on a buffered response body. Loopback control-plane bodies are small. */
2
+ export declare const LOOPBACK_DEFAULT_MAX_BODY_BYTES: number;
3
+ /** Thrown (synchronously surfaced as a rejection) for a URL that is not loopback HTTP. */
4
+ export declare class LoopbackTargetRefusedError extends Error {
5
+ readonly code = "LOOPBACK_TARGET_REFUSED";
6
+ constructor(message: string);
7
+ }
8
+ /** True for an IP literal in 127.0.0.0/8, `::1`, or an IPv4-mapped 127/8 address. */
9
+ export declare function isLoopbackAddress(address: string): boolean;
10
+ /** True for a host name or address this client accepts as "this machine". */
11
+ export declare function isLoopbackHostname(hostname: string): boolean;
12
+ /**
13
+ * Parse and validate a loopback HTTP URL. Throws {@link LoopbackTargetRefusedError}
14
+ * for anything that is not `http:` to a loopback host.
15
+ */
16
+ export declare function assertLoopbackHttpUrl(input: string | URL): URL;
17
+ /**
18
+ * `RequestInit`, so a `fetch` call site can switch to {@link loopbackFetch}
19
+ * unchanged. Only `method`, `headers`, `signal` and a string/bytes `body` are
20
+ * honoured; a stream, Blob or FormData body is rejected rather than guessed at.
21
+ */
22
+ export interface LoopbackFetchInit extends RequestInit {
23
+ /** Cap on the buffered response body; exceeding it rejects. */
24
+ maxBodyBytes?: number;
25
+ }
26
+ /** A `fetch`-shaped signature, for callers that keep a `fetchImpl` seam. */
27
+ export type LoopbackFetch = (input: string | URL, init?: LoopbackFetchInit) => Promise<Response>;
28
+ /**
29
+ * `fetch` for a loopback URL, over `node:http`, never through a proxy.
30
+ * Refuses (rejects with {@link LoopbackTargetRefusedError}) any other target.
31
+ */
32
+ export declare const loopbackFetch: LoopbackFetch;