@skrr-ai/cli 0.1.50 → 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.
- package/dist/base-command.js +59 -2
- package/dist/commands/agents/chat.d.ts +14 -0
- package/dist/commands/agents/chat.js +37 -1
- package/dist/commands/agents/show.d.ts +9 -0
- package/dist/commands/agents/show.js +53 -0
- package/dist/commands/agents/update.d.ts +9 -0
- package/dist/commands/agents/update.js +27 -0
- package/dist/commands/balance/show.d.ts +34 -0
- package/dist/commands/balance/show.js +92 -2
- package/dist/commands/commitments/doctor.d.ts +1 -0
- package/dist/commands/commitments/doctor.js +19 -4
- package/dist/commands/commitments/effective-policy.js +15 -1
- package/dist/commands/commitments/next-tick.js +3 -1
- package/dist/commands/commitments/preflight.js +10 -0
- package/dist/commands/commitments/show.js +11 -1
- package/dist/commands/daemon/config.d.ts +15 -0
- package/dist/commands/daemon/config.js +38 -0
- package/dist/commands/daemon/index.js +1 -0
- package/dist/commands/daemon/status.js +15 -1
- package/dist/commands/harnesses/models.d.ts +54 -0
- package/dist/commands/harnesses/models.js +100 -7
- package/dist/commands/harnesses/show.d.ts +11 -0
- package/dist/commands/harnesses/show.js +35 -0
- package/dist/commands/harnesses/update.d.ts +28 -1
- package/dist/commands/harnesses/update.js +73 -10
- package/dist/commands/outbox/list.d.ts +12 -1
- package/dist/commands/outbox/list.js +31 -2
- package/dist/commands/revoke-daemon.js +9 -0
- package/dist/commands/spaces/create.d.ts +1 -0
- package/dist/commands/spaces/create.js +17 -0
- package/dist/commands/spaces/show.js +5 -0
- package/dist/commands/spaces/update.d.ts +1 -0
- package/dist/commands/spaces/update.js +16 -0
- package/dist/commands/tasks/create.d.ts +1 -0
- package/dist/commands/tasks/create.js +5 -0
- package/dist/commands/tasks/show.js +45 -0
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +21 -0
- package/dist/commands/tasks/upsert.d.ts +1 -0
- package/dist/commands/tasks/upsert.js +16 -0
- package/dist/lib/agentic-stream.d.ts +68 -1
- package/dist/lib/agentic-stream.js +294 -7
- package/dist/lib/auth-core-init.d.ts +2 -10
- package/dist/lib/auth-core-init.js +20 -67
- package/dist/lib/auth-failure.d.ts +33 -0
- package/dist/lib/auth-failure.js +65 -0
- package/dist/lib/commitments.d.ts +21 -1
- package/dist/lib/commitments.js +34 -0
- package/dist/lib/daemonBroker.d.ts +14 -0
- package/dist/lib/daemonBroker.js +64 -12
- package/dist/lib/delegated-cli.js +4 -1
- package/dist/lib/exec-runtime-binary.d.ts +3 -1
- package/dist/lib/exec-runtime-binary.js +4 -2
- package/dist/lib/first-party-harness-broker.js +4 -1
- package/dist/lib/harnesses.d.ts +10 -0
- package/dist/lib/live-runtime-binary.d.ts +40 -0
- package/dist/lib/live-runtime-binary.js +193 -0
- package/dist/lib/login.d.ts +23 -0
- package/dist/lib/login.js +44 -10
- package/dist/lib/message-intent.js +3 -1
- package/dist/lib/tasks.d.ts +20 -0
- package/dist/lib/tasks.js +54 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +26 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.d.ts +28 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +23 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +41 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +25 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loopbackHttp.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loopbackHttp.js +268 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineId.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineId.js +11 -10
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +36 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +156 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.d.ts +8 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.js +8 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +22 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +61 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +25 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.d.ts +28 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +22 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +41 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +10 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loopbackHttp.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loopbackHttp.js +257 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineId.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineId.js +11 -10
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +36 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.d.ts +8 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.js +8 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +22 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +58 -1
- package/dist/node_modules/@skrr-ai/auth-core/package.json +21 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +1928 -1924
- package/oclif.manifest.json +23832 -23688
- package/package.json +1 -1
package/dist/lib/login.d.ts
CHANGED
|
@@ -38,6 +38,29 @@ export interface CliLoginWithRefreshTokenOptions {
|
|
|
38
38
|
*/
|
|
39
39
|
cliId: string;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* What a shell with no terminal can do instead (OSK-12117). The refusal used to
|
|
43
|
+
* name only CI tokens, which is not a sign-in: an agent session asking for one
|
|
44
|
+
* could only hand the job to a person typing `! skrr login`.
|
|
45
|
+
*/
|
|
46
|
+
export declare const HEADLESS_LOGIN_HINT: string;
|
|
47
|
+
/**
|
|
48
|
+
* The line printed when the daemon descriptor this CLI found is for another
|
|
49
|
+
* server (OSK-12117).
|
|
50
|
+
*
|
|
51
|
+
* It used to assert "the skrr background service on this computer is logged in
|
|
52
|
+
* to <url>" and recommend repointing the CLI there. What the CLI actually knows
|
|
53
|
+
* is narrower: ONE descriptor file it read names that server. That file can
|
|
54
|
+
* belong to another profile's daemon (observed: a localhost:3080 descriptor
|
|
55
|
+
* while this machine's default daemon was connected to skrr.ai), and in that
|
|
56
|
+
* case both the claim and the advice were wrong — the advice by moving a
|
|
57
|
+
* production CLI onto a development server. So say what was read, from where,
|
|
58
|
+
* and how to check the daemon itself.
|
|
59
|
+
*/
|
|
60
|
+
export declare function describeBrokerBaseUrlMismatch(failure: {
|
|
61
|
+
daemonServerUrl?: string;
|
|
62
|
+
descriptorPath?: string;
|
|
63
|
+
}, serverUrl: string): string;
|
|
41
64
|
/**
|
|
42
65
|
* Top-level dispatcher. Picks PKCE localhost vs device-code per the
|
|
43
66
|
* decision tree in the roadmap and persists the result.
|
package/dist/lib/login.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HEADLESS_LOGIN_HINT = void 0;
|
|
4
|
+
exports.describeBrokerBaseUrlMismatch = describeBrokerBaseUrlMismatch;
|
|
3
5
|
exports.cliLogin = cliLogin;
|
|
4
6
|
exports.cliLoginWithRefreshToken = cliLoginWithRefreshToken;
|
|
5
7
|
/**
|
|
@@ -35,6 +37,33 @@ const device_code_1 = require("./device-code");
|
|
|
35
37
|
const device_identity_1 = require("./device-identity");
|
|
36
38
|
const oauthLogin_1 = require("./oauthLogin");
|
|
37
39
|
const daemonBroker_1 = require("./daemonBroker");
|
|
40
|
+
/**
|
|
41
|
+
* What a shell with no terminal can do instead (OSK-12117). The refusal used to
|
|
42
|
+
* name only CI tokens, which is not a sign-in: an agent session asking for one
|
|
43
|
+
* could only hand the job to a person typing `! skrr login`.
|
|
44
|
+
*/
|
|
45
|
+
exports.HEADLESS_LOGIN_HINT = '`skrr login --device-code` works without a terminal: it prints a URL and a code for a ' +
|
|
46
|
+
'person to approve in any browser, and finishes when they do. For CI, use ' +
|
|
47
|
+
'`skrr create-token` and set OVERSKY_TOKEN=osk_ci_...';
|
|
48
|
+
/**
|
|
49
|
+
* The line printed when the daemon descriptor this CLI found is for another
|
|
50
|
+
* server (OSK-12117).
|
|
51
|
+
*
|
|
52
|
+
* It used to assert "the skrr background service on this computer is logged in
|
|
53
|
+
* to <url>" and recommend repointing the CLI there. What the CLI actually knows
|
|
54
|
+
* is narrower: ONE descriptor file it read names that server. That file can
|
|
55
|
+
* belong to another profile's daemon (observed: a localhost:3080 descriptor
|
|
56
|
+
* while this machine's default daemon was connected to skrr.ai), and in that
|
|
57
|
+
* case both the claim and the advice were wrong — the advice by moving a
|
|
58
|
+
* production CLI onto a development server. So say what was read, from where,
|
|
59
|
+
* and how to check the daemon itself.
|
|
60
|
+
*/
|
|
61
|
+
function describeBrokerBaseUrlMismatch(failure, serverUrl) {
|
|
62
|
+
const where = failure.descriptorPath ? ` (${failure.descriptorPath})` : '';
|
|
63
|
+
return (` Skipped the daemon hand-off: the daemon descriptor found${where} is for ` +
|
|
64
|
+
`${failure.daemonServerUrl}, not ${serverUrl}. It may belong to a different profile's ` +
|
|
65
|
+
'daemon; `skrr daemon status` shows which server this profile’s daemon is on.');
|
|
66
|
+
}
|
|
38
67
|
/**
|
|
39
68
|
* Top-level dispatcher. Picks PKCE localhost vs device-code per the
|
|
40
69
|
* decision tree in the roadmap and persists the result.
|
|
@@ -148,25 +177,30 @@ async function cliLogin(opts = {}) {
|
|
|
148
177
|
}
|
|
149
178
|
if (brokerResult.reason === 'base_url_mismatch' && brokerResult.daemonServerUrl) {
|
|
150
179
|
console.log('');
|
|
151
|
-
console.log(
|
|
152
|
-
console.log(` Skipping the daemon-broker handoff. Run \`skrr login --base-url ${brokerResult.daemonServerUrl}\` to use the daemon instead.`);
|
|
180
|
+
console.log(describeBrokerBaseUrlMismatch(brokerResult, serverUrl));
|
|
153
181
|
}
|
|
154
182
|
}
|
|
155
|
-
if ((0, ssh_detect_1.isNonInteractive)()) {
|
|
156
|
-
throw new Error(brokerRateLimited
|
|
157
|
-
? 'The skrr background service on this computer is being rate-limited while brokering logins, and this ' +
|
|
158
|
-
'shell cannot run the browser flow instead. The limit is per 15-minute window — ' +
|
|
159
|
-
'retry shortly. For CI, use `skrr create-token` and set OVERSKY_TOKEN=osk_ci_...'
|
|
160
|
-
: 'skrr login requires an interactive terminal or an SSH session. ' +
|
|
161
|
-
'For CI, use `skrr create-token` and set OVERSKY_TOKEN=osk_ci_...');
|
|
162
|
-
}
|
|
163
183
|
// --- Decision tree (first match wins) ---
|
|
184
|
+
//
|
|
185
|
+
// Device code BEFORE the terminal check (OSK-12117). It needs no terminal of
|
|
186
|
+
// its own: it prints a URL and a code, a person approves them in any browser,
|
|
187
|
+
// and this process polls until they do. That is the one way an agent session
|
|
188
|
+
// or any other shell without a TTY can finish a login, and the refusal below
|
|
189
|
+
// used to fire first — so `--device-code` was refused for lacking the very
|
|
190
|
+
// thing it does not use.
|
|
164
191
|
if (opts.forceDeviceCode) {
|
|
165
192
|
return runDeviceCode(serverUrl, cliId, 'user_requested', ssh);
|
|
166
193
|
}
|
|
167
194
|
if (envForce) {
|
|
168
195
|
return runDeviceCode(serverUrl, cliId, 'env_force', ssh);
|
|
169
196
|
}
|
|
197
|
+
if ((0, ssh_detect_1.isNonInteractive)()) {
|
|
198
|
+
throw new Error(brokerRateLimited
|
|
199
|
+
? 'The skrr background service on this computer is being rate-limited while brokering logins, and this ' +
|
|
200
|
+
'shell cannot run the browser flow instead. The limit is per 15-minute window — ' +
|
|
201
|
+
`retry shortly, or sign in now with ${exports.HEADLESS_LOGIN_HINT}`
|
|
202
|
+
: `skrr login opens a browser, and this shell has no terminal to run it from. ${exports.HEADLESS_LOGIN_HINT}`);
|
|
203
|
+
}
|
|
170
204
|
if (ssh.detected) {
|
|
171
205
|
// Device-code's "open this URL on your laptop" UX is the correct
|
|
172
206
|
// move under SSH — the localhost callback would bind on the remote
|
|
@@ -11,6 +11,7 @@ exports.canonicalSlackChannelRequest = canonicalSlackChannelRequest;
|
|
|
11
11
|
exports.canonicalSlackOwnerDmRequest = canonicalSlackOwnerDmRequest;
|
|
12
12
|
exports.rejectAutonomousSlackTarget = rejectAutonomousSlackTarget;
|
|
13
13
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
14
|
+
const loopback_http_1 = require("@skrr-ai/auth-core/loopback-http");
|
|
14
15
|
const api_fetch_1 = require("./api-fetch");
|
|
15
16
|
const daemonBroker_1 = require("./daemonBroker");
|
|
16
17
|
const runtime_context_1 = require("./runtime-context");
|
|
@@ -152,7 +153,8 @@ async function submitDaemonLoopbackMessageIntent({ context, request, env, bootst
|
|
|
152
153
|
});
|
|
153
154
|
try {
|
|
154
155
|
const response = await Promise.race([
|
|
155
|
-
|
|
156
|
+
// The daemon secret and session token stay on loopback (OSK-12134).
|
|
157
|
+
(fetchImpl || loopback_http_1.loopbackFetch)(`http://${bootstrap.host}:${bootstrap.port}/v1/agent/message-intents`, {
|
|
156
158
|
method: 'POST',
|
|
157
159
|
headers: {
|
|
158
160
|
Authorization: `Bearer ${bootstrap.secret}`,
|
package/dist/lib/tasks.d.ts
CHANGED
|
@@ -8,6 +8,25 @@ import type { ParsedDeliverableCoordinate, ParsedExpectationSpec } from '@skrr-a
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function resolveTaskGoalFlag(value: string | undefined): string | null | undefined;
|
|
10
10
|
export declare const TASK_GOAL_NOT_FOUND = "TASK_GOAL_NOT_FOUND";
|
|
11
|
+
/**
|
|
12
|
+
* `--working-directory` on `tasks create` / `update` / `upsert`. `none`,
|
|
13
|
+
* `null` and `clear` mean "no per-task override": on update the PATCH sends
|
|
14
|
+
* `workingDirectory: null` ($unset → inherit the agent default), on create it
|
|
15
|
+
* stores as absent. The sentinel set mirrors `resolveTaskGoalFlag` — it is the
|
|
16
|
+
* spelling a user who knows `--goal none` reaches for.
|
|
17
|
+
* Undefined means the flag was not passed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function workingDirectoryFromFlag(value: string | undefined): string | null | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Validate a RESOLVED `workingDirectory` payload value (post-merge, so flag
|
|
22
|
+
* and `--from-json` inputs are checked identically). `null`/`undefined` are
|
|
23
|
+
* always fine — they mean "no override". A string must be absolute: the path
|
|
24
|
+
* is resolved on the daemon's filesystem, so a relative value can neither be
|
|
25
|
+
* verified here nor trusted there — it would fall back, silently, at run
|
|
26
|
+
* time. Failing here is the same contract as every other bad spec in this
|
|
27
|
+
* file: refuse before the write, never store a value that cannot take effect.
|
|
28
|
+
*/
|
|
29
|
+
export declare function workingDirectoryValidationError(value: unknown): string | null;
|
|
11
30
|
/**
|
|
12
31
|
* The refusal for a goal id that names no goal, in CLI terms.
|
|
13
32
|
*
|
|
@@ -91,6 +110,7 @@ export type TaskCreateFlagValues = {
|
|
|
91
110
|
estimate?: number;
|
|
92
111
|
tag?: string[];
|
|
93
112
|
'label-id'?: string[];
|
|
113
|
+
'working-directory'?: string;
|
|
94
114
|
worktree?: boolean;
|
|
95
115
|
'lifecycle-definition'?: string;
|
|
96
116
|
'lifecycle-version'?: string;
|
package/dist/lib/tasks.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RECOGNIZED_TASK_VERDICTS = exports.DELIVERABLE_LIFECYCLE_VALUES = exports.EXPECT_FLAG_HELP = exports.TASK_WORK_EVENT_KINDS = exports.TASK_PRIORITIES = exports.TASK_GOAL_NOT_FOUND = void 0;
|
|
4
4
|
exports.resolveTaskGoalFlag = resolveTaskGoalFlag;
|
|
5
|
+
exports.workingDirectoryFromFlag = workingDirectoryFromFlag;
|
|
6
|
+
exports.workingDirectoryValidationError = workingDirectoryValidationError;
|
|
5
7
|
exports.describeTaskGoalNotFound = describeTaskGoalNotFound;
|
|
6
8
|
exports.releasePinRepairGuard = releasePinRepairGuard;
|
|
7
9
|
exports.taskUpdateMutationGuard = taskUpdateMutationGuard;
|
|
@@ -38,6 +40,7 @@ exports.verdictIsRecognized = verdictIsRecognized;
|
|
|
38
40
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
39
41
|
const prompt_1 = require("./prompt");
|
|
40
42
|
const projects_1 = require("./projects");
|
|
43
|
+
const node_path_1 = require("node:path");
|
|
41
44
|
/**
|
|
42
45
|
* `--goal` on `tasks update`. `none`, `null` and `clear` detach the goal: PATCH
|
|
43
46
|
* sends `goalId: null`. Anything else names a goal. `clear` is accepted because
|
|
@@ -54,6 +57,47 @@ function resolveTaskGoalFlag(value) {
|
|
|
54
57
|
return v;
|
|
55
58
|
}
|
|
56
59
|
exports.TASK_GOAL_NOT_FOUND = 'TASK_GOAL_NOT_FOUND';
|
|
60
|
+
/**
|
|
61
|
+
* `--working-directory` on `tasks create` / `update` / `upsert`. `none`,
|
|
62
|
+
* `null` and `clear` mean "no per-task override": on update the PATCH sends
|
|
63
|
+
* `workingDirectory: null` ($unset → inherit the agent default), on create it
|
|
64
|
+
* stores as absent. The sentinel set mirrors `resolveTaskGoalFlag` — it is the
|
|
65
|
+
* spelling a user who knows `--goal none` reaches for.
|
|
66
|
+
* Undefined means the flag was not passed.
|
|
67
|
+
*/
|
|
68
|
+
function workingDirectoryFromFlag(value) {
|
|
69
|
+
if (value === undefined)
|
|
70
|
+
return undefined;
|
|
71
|
+
const v = value.trim();
|
|
72
|
+
if (!v || ['none', 'null', 'clear'].includes(v.toLowerCase()))
|
|
73
|
+
return null;
|
|
74
|
+
return v;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate a RESOLVED `workingDirectory` payload value (post-merge, so flag
|
|
78
|
+
* and `--from-json` inputs are checked identically). `null`/`undefined` are
|
|
79
|
+
* always fine — they mean "no override". A string must be absolute: the path
|
|
80
|
+
* is resolved on the daemon's filesystem, so a relative value can neither be
|
|
81
|
+
* verified here nor trusted there — it would fall back, silently, at run
|
|
82
|
+
* time. Failing here is the same contract as every other bad spec in this
|
|
83
|
+
* file: refuse before the write, never store a value that cannot take effect.
|
|
84
|
+
*/
|
|
85
|
+
function workingDirectoryValidationError(value) {
|
|
86
|
+
if (value === undefined || value === null)
|
|
87
|
+
return null;
|
|
88
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
89
|
+
return 'workingDirectory must be an absolute path string (or null for no override)';
|
|
90
|
+
}
|
|
91
|
+
if (value.length > 1024) {
|
|
92
|
+
return 'workingDirectory must be at most 1024 characters';
|
|
93
|
+
}
|
|
94
|
+
if (!(0, node_path_1.isAbsolute)(value.trim())) {
|
|
95
|
+
return (`workingDirectory must be an absolute path on the daemon's filesystem ` +
|
|
96
|
+
`(got '${value}'). ` +
|
|
97
|
+
'A relative path cannot be resolved there — pass the full path, or "none" to clear.');
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
57
101
|
/**
|
|
58
102
|
* The refusal for a goal id that names no goal, in CLI terms.
|
|
59
103
|
*
|
|
@@ -331,6 +375,8 @@ function buildTaskCreatePayload(body, flags = {}) {
|
|
|
331
375
|
// is a Task→Task edge rather than a pointer at a chat message.
|
|
332
376
|
spawnedByTaskId: flags['spawned-by'],
|
|
333
377
|
spawnReason: flags['spawn-reason'],
|
|
378
|
+
// Per-task working-directory override; 'none' ⇒ null (no override).
|
|
379
|
+
workingDirectory: workingDirectoryFromFlag(flags['working-directory']),
|
|
334
380
|
// Worktree isolation opt-in. --worktree ⇒ 'ephemeral', --no-worktree ⇒
|
|
335
381
|
// 'shared', omitted ⇒ undefined (inherit space policy / global default).
|
|
336
382
|
executionIsolation: executionIsolationFromWorktreeFlag(flags.worktree),
|
|
@@ -409,6 +455,9 @@ function validateTaskCreatePayload(payload) {
|
|
|
409
455
|
if (payload.templateId !== undefined && !isBoundedString(payload.templateId, 1, 100)) {
|
|
410
456
|
errors.push('templateId must be a non-empty string at most 100 characters');
|
|
411
457
|
}
|
|
458
|
+
const workingDirectoryError = workingDirectoryValidationError(payload.workingDirectory);
|
|
459
|
+
if (workingDirectoryError)
|
|
460
|
+
errors.push(workingDirectoryError);
|
|
412
461
|
if (payload.lifecycle !== undefined) {
|
|
413
462
|
const binding = payload.lifecycle;
|
|
414
463
|
if (!binding || typeof binding !== 'object' || Array.isArray(binding)) {
|
|
@@ -756,6 +805,11 @@ function createPatchFromCreatePayload(payload) {
|
|
|
756
805
|
'executionMode',
|
|
757
806
|
'tags',
|
|
758
807
|
'labelIds',
|
|
808
|
+
// Per-task working-directory override — part of the same execution shape
|
|
809
|
+
// the create payload carries, and equally meaningful on the update leg of
|
|
810
|
+
// an upsert. Absent here, `upsert --working-directory` silently no-ops on
|
|
811
|
+
// an existing task.
|
|
812
|
+
'workingDirectory',
|
|
759
813
|
];
|
|
760
814
|
for (const field of copyFields) {
|
|
761
815
|
if (payload[field] !== undefined)
|
|
@@ -21,6 +21,8 @@ type DisabledReason = 'kek_unavailable' | 'init_failed' | 'platform_unsupported'
|
|
|
21
21
|
export declare function __setStateForTest(next: CredEnvelopeState): CredEnvelopeState;
|
|
22
22
|
/** @internal Test seam — read state for assertions. */
|
|
23
23
|
export declare function __getStateForTest(): CredEnvelopeState;
|
|
24
|
+
/** @internal Test seam — each spec owns its warn-dedupe horizon. */
|
|
25
|
+
export declare function __clearWarnDedupeForTest(): void;
|
|
24
26
|
/**
|
|
25
27
|
* Initialize the envelope path. Idempotent — second call when state is
|
|
26
28
|
* non-uninit returns immediately.
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.__setStateForTest = __setStateForTest;
|
|
7
7
|
exports.__getStateForTest = __getStateForTest;
|
|
8
|
+
exports.__clearWarnDedupeForTest = __clearWarnDedupeForTest;
|
|
8
9
|
exports.initCredEnvelope = initCredEnvelope;
|
|
9
10
|
exports.resetCredEnvelope = resetCredEnvelope;
|
|
10
11
|
exports.shutdownCredEnvelope = shutdownCredEnvelope;
|
|
@@ -141,7 +142,21 @@ function atomicWrite0600(target, value) {
|
|
|
141
142
|
* The event name passed in is the FULLY-QUALIFIED `cred_envelope.*` form
|
|
142
143
|
* because daemon-side telemetry consumers (`event-log.ts`, dashboards,
|
|
143
144
|
* spec assertions) key off the prefixed name. Do not strip the prefix.
|
|
145
|
+
*
|
|
146
|
+
* Identical failure warnings hit the logger only once per process.
|
|
147
|
+
* Telemetry still records every occurrence — the dedupe exists because a
|
|
148
|
+
* single unreadable envelope produced the same warning once per stored
|
|
149
|
+
* field (token, refresh token, expiry…), which read to users as three
|
|
150
|
+
* crashes rather than one stale credential (OSK-12074). The signature is
|
|
151
|
+
* event + kek identity + message, so a genuinely different failure mode
|
|
152
|
+
* still gets its own line.
|
|
144
153
|
*/
|
|
154
|
+
const warnedSignatures = new Set();
|
|
155
|
+
const MAX_WARNED_SIGNATURES = 64;
|
|
156
|
+
/** @internal Test seam — each spec owns its warn-dedupe horizon. */
|
|
157
|
+
function __clearWarnDedupeForTest() {
|
|
158
|
+
warnedSignatures.clear();
|
|
159
|
+
}
|
|
145
160
|
function emit(eventName, success, metadata) {
|
|
146
161
|
(0, runtime_js_1.emitAuthTelemetry)({
|
|
147
162
|
timestamp: new Date().toISOString(),
|
|
@@ -150,7 +165,13 @@ function emit(eventName, success, metadata) {
|
|
|
150
165
|
metadata: { event: eventName, ...metadata },
|
|
151
166
|
});
|
|
152
167
|
if (!success) {
|
|
153
|
-
|
|
168
|
+
const signature = `${eventName}:${metadata.kekId ?? ''}:${metadata.kekKind ?? ''}:${metadata.message ?? ''}`;
|
|
169
|
+
if (!warnedSignatures.has(signature)) {
|
|
170
|
+
if (warnedSignatures.size >= MAX_WARNED_SIGNATURES)
|
|
171
|
+
warnedSignatures.clear();
|
|
172
|
+
warnedSignatures.add(signature);
|
|
173
|
+
(0, runtime_js_1.getAuthLogger)().warn(`[credEnvelope] ${eventName}`, metadata);
|
|
174
|
+
}
|
|
154
175
|
}
|
|
155
176
|
}
|
|
156
177
|
// ---------------------------------------------------------------------------
|
|
@@ -763,6 +784,10 @@ function maybeDecryptOnRead(stored) {
|
|
|
763
784
|
kekId: _state.kekId,
|
|
764
785
|
kekKind: _state.kekKind,
|
|
765
786
|
message: err?.message ?? 'unknown',
|
|
787
|
+
// The crypto error alone reads as a crash; the actionable truth is
|
|
788
|
+
// that this stored credential is unreadable by THIS binary (usually
|
|
789
|
+
// a signing-identity or profile drift) and re-login rebuilds it.
|
|
790
|
+
hint: 'stored credential is unreadable on this machine — signing in again rebuilds it',
|
|
766
791
|
});
|
|
767
792
|
return { plaintext: null, needsMigration: false };
|
|
768
793
|
}
|
|
@@ -23,9 +23,14 @@
|
|
|
23
23
|
*
|
|
24
24
|
* What the daemon verifies is the binding, not the server's honesty: the
|
|
25
25
|
* request already arrives over the daemon's authenticated server channel, so
|
|
26
|
-
* the field IS the attestation
|
|
27
|
-
*
|
|
28
|
-
* one call cannot be replayed
|
|
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.
|
|
29
34
|
*/
|
|
30
35
|
/** Announced by a daemon that honours `attestedApproval` on daemon:tool:request. */
|
|
31
36
|
export declare const DAEMON_TOOL_ATTESTED_APPROVAL_CAPABILITY = "daemon_tool_attested_approval_v1";
|
|
@@ -89,12 +94,31 @@ export declare function mintAttestedToolApproval(args: {
|
|
|
89
94
|
now?: number;
|
|
90
95
|
ttlMs?: number;
|
|
91
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';
|
|
92
111
|
export type AttestedToolApprovalVerdict = {
|
|
93
112
|
ok: true;
|
|
94
113
|
} | {
|
|
95
114
|
ok: false;
|
|
96
|
-
reason:
|
|
115
|
+
reason: AttestedToolApprovalRefusalReason;
|
|
97
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;
|
|
98
122
|
/**
|
|
99
123
|
* Daemon side: verify an attestation against the call it claims to approve.
|
|
100
124
|
* Untrusted wire input — every field is checked, and any mismatch is a plain
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ATTESTED_TOOL_APPROVAL_TTL_MS = exports.DAEMON_TOOL_CATEGORIES = exports.DAEMON_TOOL_READ_ONLY_CAPABILITY = exports.DAEMON_TOOL_ATTESTED_APPROVAL_CAPABILITY = void 0;
|
|
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
4
|
exports.categoryForDaemonTool = categoryForDaemonTool;
|
|
5
5
|
exports.canonicalToolInputDigest = canonicalToolInputDigest;
|
|
6
6
|
exports.mintAttestedToolApproval = mintAttestedToolApproval;
|
|
@@ -31,9 +31,14 @@ const node_crypto_1 = require("node:crypto");
|
|
|
31
31
|
*
|
|
32
32
|
* What the daemon verifies is the binding, not the server's honesty: the
|
|
33
33
|
* request already arrives over the daemon's authenticated server channel, so
|
|
34
|
-
* the field IS the attestation
|
|
35
|
-
*
|
|
36
|
-
* one call cannot be replayed
|
|
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.
|
|
37
42
|
*/
|
|
38
43
|
/** Announced by a daemon that honours `attestedApproval` on daemon:tool:request. */
|
|
39
44
|
exports.DAEMON_TOOL_ATTESTED_APPROVAL_CAPABILITY = 'daemon_tool_attested_approval_v1';
|
|
@@ -136,6 +141,11 @@ function mintAttestedToolApproval(args) {
|
|
|
136
141
|
expiresAt: now + (args.ttlMs ?? exports.ATTESTED_TOOL_APPROVAL_TTL_MS),
|
|
137
142
|
};
|
|
138
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;
|
|
139
149
|
/**
|
|
140
150
|
* Daemon side: verify an attestation against the call it claims to approve.
|
|
141
151
|
* Untrusted wire input — every field is checked, and any mismatch is a plain
|
|
@@ -159,9 +169,17 @@ function verifyAttestedToolApproval(approval, call) {
|
|
|
159
169
|
return { ok: false, reason: 'malformed' };
|
|
160
170
|
}
|
|
161
171
|
const now = call.now ?? Date.now();
|
|
162
|
-
if (a.expiresAt <= now || a.issuedAt > now +
|
|
172
|
+
if (a.expiresAt <= now || a.issuedAt > now + exports.ATTESTED_TOOL_APPROVAL_CLOCK_SKEW_MS) {
|
|
163
173
|
return { ok: false, reason: 'expired' };
|
|
164
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
|
+
}
|
|
165
183
|
if (a.toolUseId !== call.toolUseId ||
|
|
166
184
|
a.toolName.toLowerCase() !== String(call.toolName).toLowerCase() ||
|
|
167
185
|
a.inputSha256 !== canonicalToolInputDigest(call.toolInput)) {
|
|
@@ -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
|
|
608
|
-
// for
|
|
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.
|
|
28
|
-
exports.
|
|
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;
|