@skrr-ai/cli 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-command.d.ts +7 -0
- package/dist/base-command.js +95 -8
- package/dist/commands/agents/chat.d.ts +14 -0
- package/dist/commands/agents/chat.js +37 -1
- package/dist/commands/agents/create.js +8 -4
- package/dist/commands/agents/show.d.ts +9 -0
- package/dist/commands/agents/show.js +53 -0
- package/dist/commands/agents/update.d.ts +32 -0
- package/dist/commands/agents/update.js +81 -9
- 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 +12 -2
- package/dist/commands/commitments/preflight.js +10 -0
- package/dist/commands/commitments/show.js +11 -1
- package/dist/commands/context.js +2 -1
- package/dist/commands/daemon/config.d.ts +15 -0
- package/dist/commands/daemon/config.js +53 -0
- package/dist/commands/daemon/index.js +1 -0
- package/dist/commands/daemon/status.js +15 -1
- package/dist/commands/doctor.js +8 -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/login.d.ts +2 -1
- package/dist/commands/login.js +26 -7
- package/dist/commands/logout.js +3 -0
- package/dist/commands/machines/dedicated/show.js +25 -2
- 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 +6 -0
- package/dist/commands/tasks/create.js +31 -6
- package/dist/commands/tasks/show.js +45 -0
- package/dist/commands/tasks/start.d.ts +14 -0
- package/dist/commands/tasks/start.js +31 -1
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +29 -1
- package/dist/commands/tasks/upsert.d.ts +1 -0
- package/dist/commands/tasks/upsert.js +16 -0
- package/dist/commands/whoami.js +5 -3
- package/dist/lib/agent-config.d.ts +2 -0
- package/dist/lib/agent-config.js +14 -10
- package/dist/lib/agentic-stream.d.ts +88 -1
- package/dist/lib/agentic-stream.js +330 -9
- 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 +68 -0
- package/dist/lib/auth-storage.d.ts +24 -0
- package/dist/lib/auth-storage.js +342 -11
- package/dist/lib/cli-id.d.ts +22 -0
- package/dist/lib/cli-id.js +56 -0
- package/dist/lib/cli-identity.d.ts +49 -0
- package/dist/lib/cli-identity.js +159 -0
- package/dist/lib/commitments.d.ts +25 -1
- package/dist/lib/commitments.js +46 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.js +15 -3
- package/dist/lib/daemonBroker.d.ts +37 -1
- package/dist/lib/daemonBroker.js +146 -22
- package/dist/lib/daemonBrokerRefusal.d.ts +51 -0
- package/dist/lib/daemonBrokerRefusal.js +127 -0
- package/dist/lib/dedicated-machines.d.ts +33 -2
- package/dist/lib/dedicated-machines.js +50 -1
- package/dist/lib/delegated-cli.js +20 -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 +10 -5
- package/dist/lib/harnesses.d.ts +10 -0
- package/dist/lib/keychain.d.ts +39 -0
- package/dist/lib/keychain.js +228 -39
- package/dist/lib/label-ref.d.ts +34 -0
- package/dist/lib/label-ref.js +68 -1
- 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 +67 -21
- package/dist/lib/message-intent.js +3 -1
- package/dist/lib/oauthLogin.js +2 -8
- package/dist/lib/refresh.js +6 -2
- package/dist/lib/tasks.d.ts +20 -0
- package/dist/lib/tasks.js +87 -8
- 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 +3139 -3097
- package/oclif.manifest.json +28591 -28438
- package/package.json +1 -1
|
@@ -94,6 +94,20 @@ export type AgenticStreamOptions = {
|
|
|
94
94
|
/** How long to keep trying to attach before reporting a detach. Default 30s. */
|
|
95
95
|
attachTimeoutMs?: number;
|
|
96
96
|
};
|
|
97
|
+
/**
|
|
98
|
+
* What a permission request would DO, in one line a person can judge.
|
|
99
|
+
*
|
|
100
|
+
* The prompt used to be `Allow tool <name>?` and nothing else, so a shell
|
|
101
|
+
* approval read "Allow tool unknown? [y/N]" and a person approved a command
|
|
102
|
+
* they never saw (OSK-12119). The command, or else the path, or else the
|
|
103
|
+
* arguments, is the part that decides the answer.
|
|
104
|
+
*
|
|
105
|
+
* The text is chosen by the agent and printed to a terminal, so control
|
|
106
|
+
* characters are escaped: an embedded carriage return or escape sequence could
|
|
107
|
+
* otherwise redraw the line and show the person a different command than the
|
|
108
|
+
* one they are approving.
|
|
109
|
+
*/
|
|
110
|
+
export declare function describePermissionTarget(toolInput: unknown): string | null;
|
|
97
111
|
/**
|
|
98
112
|
* Small Node-side adapter for the existing Agentic Socket.IO contract.
|
|
99
113
|
* It deliberately normalizes only lifecycle/text/tool events; the server's
|
|
@@ -159,6 +173,25 @@ export type ProjectedToolEvent = {
|
|
|
159
173
|
* of the run. The pairing is unambiguous within a turn.
|
|
160
174
|
*/
|
|
161
175
|
export declare function projectDaemonToolEvents(envelopes: unknown[], namesByCall: Map<string, string>): ProjectedToolEvent[];
|
|
176
|
+
/**
|
|
177
|
+
* WHO or WHAT decided a permission request, as a reader needs it (OSK-12113).
|
|
178
|
+
*
|
|
179
|
+
* `by` names the surface or fallback that resolved the ask and is a closed wire
|
|
180
|
+
* enum; `basis` beside it says what decided. The distinction is the finding: an
|
|
181
|
+
* edit acceptEdits applied on its own was recorded `by: "cli"`, which read as
|
|
182
|
+
* the CLI user having approved something nobody was asked about.
|
|
183
|
+
*/
|
|
184
|
+
export declare function describePermissionDecider(ev: {
|
|
185
|
+
by?: unknown;
|
|
186
|
+
basis?: unknown;
|
|
187
|
+
mode?: unknown;
|
|
188
|
+
surface?: unknown;
|
|
189
|
+
}): string;
|
|
190
|
+
/**
|
|
191
|
+
* Daemon `permission-resolved` envelopes as `permission.resolved` events, named
|
|
192
|
+
* by the tool their `permission-ask` announced and carrying who decided.
|
|
193
|
+
*/
|
|
194
|
+
export declare function projectPermissionResolutions(envelopes: unknown[], toolsByRequest: Map<string, string>): AgenticStreamEvent[];
|
|
162
195
|
/**
|
|
163
196
|
* The part of a cumulative snapshot that has not been shown yet.
|
|
164
197
|
*
|
|
@@ -262,8 +295,32 @@ export type SeqGap = {
|
|
|
262
295
|
from: number;
|
|
263
296
|
to: number;
|
|
264
297
|
};
|
|
298
|
+
/**
|
|
299
|
+
* `service` envelopes that carry a `notice` — words for the person, not
|
|
300
|
+
* diagnostics — projected as `session.notice` events. Today the only notice is
|
|
301
|
+
* `harness-session-reset`: the local harness could not resume its previous
|
|
302
|
+
* session and began a new one, so its local working context is gone.
|
|
303
|
+
*/
|
|
304
|
+
export declare function projectSessionNotices(envelopes: unknown[]): AgenticStreamEvent[];
|
|
265
305
|
export declare function detectSeqGap(highestApplied: number, incoming: number): SeqGap | null;
|
|
266
|
-
|
|
306
|
+
/**
|
|
307
|
+
* The notice a reader sees when chunks went missing.
|
|
308
|
+
*
|
|
309
|
+
* It names `run.result` only when this client will actually fill `run.result`
|
|
310
|
+
* from the persisted message (`recordBackfill`). The sentence used to promise
|
|
311
|
+
* it unconditionally, while `run.result.text` was folded from the envelopes
|
|
312
|
+
* that DID arrive — so the one run told to trust `run.result` was the run whose
|
|
313
|
+
* `run.result.text` was empty, with the answer only in the record (OSK-12132).
|
|
314
|
+
*/
|
|
315
|
+
export declare function seqGapNotice(gap: SeqGap, options?: {
|
|
316
|
+
recordBackfill?: boolean;
|
|
317
|
+
}): string;
|
|
318
|
+
/**
|
|
319
|
+
* The permission mode a `run.started` reports, and where it came from
|
|
320
|
+
* (OSK-12089). `null` when the event carries no mode report at all — a cloud
|
|
321
|
+
* turn from an older server, say — so nothing is claimed.
|
|
322
|
+
*/
|
|
323
|
+
export declare function describeRunPermission(event: AgenticStreamEvent): string | null;
|
|
267
324
|
export declare class AgenticStreamClient {
|
|
268
325
|
private readonly options;
|
|
269
326
|
private socket;
|
|
@@ -289,8 +346,15 @@ export declare class AgenticStreamClient {
|
|
|
289
346
|
* repeat count varies run to run and cannot be inferred by a consumer.
|
|
290
347
|
*/
|
|
291
348
|
private readonly seenLifecycle;
|
|
349
|
+
/**
|
|
350
|
+
* Whether this client's transport has dropped since it first connected. Until
|
|
351
|
+
* it has, every `session:join` it sends declares a FIRST join (OSK-12124).
|
|
352
|
+
*/
|
|
353
|
+
private transportDropped;
|
|
292
354
|
/** `call` → tool name, so a daemon `tool-call-end` can be named (OSK-7730). */
|
|
293
355
|
private readonly toolNamesByCall;
|
|
356
|
+
/** `permission-ask` requestId → tool name, to name its resolution (OSK-12113). */
|
|
357
|
+
private readonly toolNamesByPermission;
|
|
294
358
|
/** Text already written to the terminal, for suffix-only rendering. */
|
|
295
359
|
private renderedText;
|
|
296
360
|
private idleTimer;
|
|
@@ -314,6 +378,21 @@ export declare class AgenticStreamClient {
|
|
|
314
378
|
settledResult(): AgenticStreamResult | null;
|
|
315
379
|
constructor(options: AgenticStreamOptions);
|
|
316
380
|
run(): Promise<AgenticStreamResult>;
|
|
381
|
+
/**
|
|
382
|
+
* The `session:join` payload.
|
|
383
|
+
*
|
|
384
|
+
* This client learns its session id from the HTTP acceptance and only then
|
|
385
|
+
* connects, while a daemon turn publishes its `session:init` at once. The
|
|
386
|
+
* server drops buffered frames older than the socket's connect time — right
|
|
387
|
+
* for a RE-connect, which already rendered them, and wrong here, where they
|
|
388
|
+
* were never seen: `run.started` (the dispatched mode and its source) never
|
|
389
|
+
* reached a daemon turn's stream (OSK-12124). `firstJoin` says so, and the
|
|
390
|
+
* server then replays the pre-connect lifecycle frames. Both joins of the
|
|
391
|
+
* first connection carry it, because the server's replay drains the shared
|
|
392
|
+
* buffer and either join may be the one that reaches it first; the stream's
|
|
393
|
+
* own lifecycle dedupe absorbs the overlap. After a drop it is not sent.
|
|
394
|
+
*/
|
|
395
|
+
private sessionJoinPayload;
|
|
317
396
|
/**
|
|
318
397
|
* (Re)start the silence watchdog that asks the durable record what happened.
|
|
319
398
|
*
|
|
@@ -372,6 +451,14 @@ export declare class AgenticStreamClient {
|
|
|
372
451
|
private emitToolEvent;
|
|
373
452
|
private emit;
|
|
374
453
|
private finish;
|
|
454
|
+
/**
|
|
455
|
+
* The answer as the durable record holds it, for a run whose stream had a
|
|
456
|
+
* hole. Bounded: the record is written before the terminal event is sent, so
|
|
457
|
+
* it is normally there at once, and a record that stays silent must not hold
|
|
458
|
+
* the run open — the reader then gets what the stream delivered.
|
|
459
|
+
*/
|
|
460
|
+
private readRecordedText;
|
|
461
|
+
private settle;
|
|
375
462
|
/**
|
|
376
463
|
* OSK-2944 — a transient connect failure must not end an ACCEPTED run.
|
|
377
464
|
*
|
|
@@ -2,17 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = exports.NON_INTERACTIVE_QUESTION_DISMISSAL = void 0;
|
|
4
4
|
exports.writeJsonLine = writeJsonLine;
|
|
5
|
+
exports.describePermissionTarget = describePermissionTarget;
|
|
5
6
|
exports.envelopesText = envelopesText;
|
|
6
7
|
exports.envelopeKey = envelopeKey;
|
|
7
8
|
exports.unseenEnvelopes = unseenEnvelopes;
|
|
8
9
|
exports.projectDaemonToolEvents = projectDaemonToolEvents;
|
|
10
|
+
exports.describePermissionDecider = describePermissionDecider;
|
|
11
|
+
exports.projectPermissionResolutions = projectPermissionResolutions;
|
|
9
12
|
exports.renderableDelta = renderableDelta;
|
|
10
13
|
exports.transportRefusal = transportRefusal;
|
|
11
14
|
exports.transportRefusalMessage = transportRefusalMessage;
|
|
12
15
|
exports.attachDeadlineFor = attachDeadlineFor;
|
|
13
16
|
exports.jitteredRetryDelay = jitteredRetryDelay;
|
|
17
|
+
exports.projectSessionNotices = projectSessionNotices;
|
|
14
18
|
exports.detectSeqGap = detectSeqGap;
|
|
15
19
|
exports.seqGapNotice = seqGapNotice;
|
|
20
|
+
exports.describeRunPermission = describeRunPermission;
|
|
16
21
|
exports.toWebSocketOrigin = toWebSocketOrigin;
|
|
17
22
|
const node_readline_1 = require("node:readline");
|
|
18
23
|
const socket_io_client_1 = require("socket.io-client");
|
|
@@ -48,6 +53,71 @@ exports.NON_INTERACTIVE_QUESTION_DISMISSAL = 'No answer: this turn was started n
|
|
|
48
53
|
function writeJsonLine(output, value) {
|
|
49
54
|
output.write(`${JSON.stringify(value) ?? 'null'}\n`);
|
|
50
55
|
}
|
|
56
|
+
const PERMISSION_DETAIL_MAX = 400;
|
|
57
|
+
/**
|
|
58
|
+
* What a permission request would DO, in one line a person can judge.
|
|
59
|
+
*
|
|
60
|
+
* The prompt used to be `Allow tool <name>?` and nothing else, so a shell
|
|
61
|
+
* approval read "Allow tool unknown? [y/N]" and a person approved a command
|
|
62
|
+
* they never saw (OSK-12119). The command, or else the path, or else the
|
|
63
|
+
* arguments, is the part that decides the answer.
|
|
64
|
+
*
|
|
65
|
+
* The text is chosen by the agent and printed to a terminal, so control
|
|
66
|
+
* characters are escaped: an embedded carriage return or escape sequence could
|
|
67
|
+
* otherwise redraw the line and show the person a different command than the
|
|
68
|
+
* one they are approving.
|
|
69
|
+
*/
|
|
70
|
+
function describePermissionTarget(toolInput) {
|
|
71
|
+
if (!toolInput || typeof toolInput !== 'object' || Array.isArray(toolInput))
|
|
72
|
+
return null;
|
|
73
|
+
const input = toolInput;
|
|
74
|
+
const pick = (...keys) => {
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
const value = input[key];
|
|
77
|
+
if (typeof value === 'string' && value.trim())
|
|
78
|
+
return value;
|
|
79
|
+
if (Array.isArray(value) && value.length > 0 && value.every((v) => typeof v === 'string')) {
|
|
80
|
+
return value.join(' ');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
85
|
+
let detail = pick('command', 'cmd', 'script');
|
|
86
|
+
if (!detail) {
|
|
87
|
+
const target = pick('file_path', 'filePath', 'path', 'url');
|
|
88
|
+
if (target)
|
|
89
|
+
detail = target;
|
|
90
|
+
}
|
|
91
|
+
if (!detail) {
|
|
92
|
+
if (Object.keys(input).length === 0)
|
|
93
|
+
return null;
|
|
94
|
+
try {
|
|
95
|
+
detail = JSON.stringify(input);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let escaped = '';
|
|
102
|
+
for (const c of detail) {
|
|
103
|
+
const code = c.charCodeAt(0);
|
|
104
|
+
const control = code < 0x20 || (code >= 0x7f && code <= 0x9f);
|
|
105
|
+
if (!control)
|
|
106
|
+
escaped += c;
|
|
107
|
+
else
|
|
108
|
+
escaped += c === '\n' ? '\\n' : `\\x${code.toString(16).padStart(2, '0')}`;
|
|
109
|
+
}
|
|
110
|
+
return escaped.length > PERMISSION_DETAIL_MAX
|
|
111
|
+
? `${escaped.slice(0, PERMISSION_DETAIL_MAX)}… (${escaped.length - PERMISSION_DETAIL_MAX} more characters)`
|
|
112
|
+
: escaped;
|
|
113
|
+
}
|
|
114
|
+
function describePermission(event) {
|
|
115
|
+
// Permission events carry toolName/toolInput beyond the base event type.
|
|
116
|
+
const fields = event;
|
|
117
|
+
const name = String(fields.toolName ?? 'unknown');
|
|
118
|
+
const detail = describePermissionTarget(fields.toolInput);
|
|
119
|
+
return detail ? `${name}: ${detail}` : name;
|
|
120
|
+
}
|
|
51
121
|
/**
|
|
52
122
|
* Small Node-side adapter for the existing Agentic Socket.IO contract.
|
|
53
123
|
* It deliberately normalizes only lifecycle/text/tool events; the server's
|
|
@@ -179,6 +249,87 @@ function projectDaemonToolEvents(envelopes, namesByCall) {
|
|
|
179
249
|
}
|
|
180
250
|
return out;
|
|
181
251
|
}
|
|
252
|
+
const SURFACE_LABELS = {
|
|
253
|
+
web: 'the web app',
|
|
254
|
+
cli: 'the skrr CLI',
|
|
255
|
+
desktop: 'the desktop app',
|
|
256
|
+
mobile: 'the mobile app',
|
|
257
|
+
};
|
|
258
|
+
/** `by` alone, for a daemon that predates `basis` — said no more precisely than it knows. */
|
|
259
|
+
const BY_LABELS = {
|
|
260
|
+
cockpit: 'an answer relayed by the server',
|
|
261
|
+
avatar: 'a person on the desktop app',
|
|
262
|
+
timeout: 'nobody — the request timed out',
|
|
263
|
+
server: 'the server (the run is unattended, so nobody could be asked)',
|
|
264
|
+
cli: 'the harness side, without asking anyone',
|
|
265
|
+
cache: 'an earlier decision for this session',
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* WHO or WHAT decided a permission request, as a reader needs it (OSK-12113).
|
|
269
|
+
*
|
|
270
|
+
* `by` names the surface or fallback that resolved the ask and is a closed wire
|
|
271
|
+
* enum; `basis` beside it says what decided. The distinction is the finding: an
|
|
272
|
+
* edit acceptEdits applied on its own was recorded `by: "cli"`, which read as
|
|
273
|
+
* the CLI user having approved something nobody was asked about.
|
|
274
|
+
*/
|
|
275
|
+
function describePermissionDecider(ev) {
|
|
276
|
+
const mode = typeof ev.mode === 'string' && ev.mode ? ev.mode : '';
|
|
277
|
+
switch (ev.basis) {
|
|
278
|
+
case 'mode':
|
|
279
|
+
return mode
|
|
280
|
+
? `the ${mode} permission mode (nobody was asked)`
|
|
281
|
+
: "the session's permission mode (nobody was asked)";
|
|
282
|
+
case 'policy':
|
|
283
|
+
if (ev.by === 'server')
|
|
284
|
+
return 'the unattended-run policy (nobody could be asked)';
|
|
285
|
+
// A client that denied on its own because it could not ask anyone — the
|
|
286
|
+
// skrr CLI without --approve-tools, say (OSK-12113).
|
|
287
|
+
if (typeof ev.surface === 'string' && SURFACE_LABELS[ev.surface]) {
|
|
288
|
+
return `${SURFACE_LABELS[ev.surface]}, which had nobody to ask (nobody was asked)`;
|
|
289
|
+
}
|
|
290
|
+
return 'a runtime policy (nobody was asked)';
|
|
291
|
+
case 'session-grant':
|
|
292
|
+
return 'an earlier "allow for this session"';
|
|
293
|
+
case 'person': {
|
|
294
|
+
const where = typeof ev.surface === 'string' ? SURFACE_LABELS[ev.surface] : undefined;
|
|
295
|
+
return where ? `a person on ${where}` : 'a person';
|
|
296
|
+
}
|
|
297
|
+
default:
|
|
298
|
+
return (typeof ev.by === 'string' && BY_LABELS[ev.by]) || 'an unrecorded decider';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Daemon `permission-resolved` envelopes as `permission.resolved` events, named
|
|
303
|
+
* by the tool their `permission-ask` announced and carrying who decided.
|
|
304
|
+
*/
|
|
305
|
+
function projectPermissionResolutions(envelopes, toolsByRequest) {
|
|
306
|
+
const out = [];
|
|
307
|
+
for (const envelope of envelopes) {
|
|
308
|
+
const ev = envelope?.ev;
|
|
309
|
+
if (!ev || typeof ev.requestId !== 'string')
|
|
310
|
+
continue;
|
|
311
|
+
if (ev.t === 'permission-ask') {
|
|
312
|
+
if (typeof ev.toolName === 'string')
|
|
313
|
+
toolsByRequest.set(ev.requestId, ev.toolName);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (ev.t !== 'permission-resolved')
|
|
317
|
+
continue;
|
|
318
|
+
const toolName = toolsByRequest.get(ev.requestId);
|
|
319
|
+
out.push({
|
|
320
|
+
type: 'permission.resolved',
|
|
321
|
+
requestId: ev.requestId,
|
|
322
|
+
...(toolName ? { toolName } : {}),
|
|
323
|
+
decision: ev.decision,
|
|
324
|
+
by: ev.by,
|
|
325
|
+
...(typeof ev.basis === 'string' ? { basis: ev.basis } : {}),
|
|
326
|
+
...(typeof ev.mode === 'string' ? { mode: ev.mode } : {}),
|
|
327
|
+
...(typeof ev.surface === 'string' ? { surface: ev.surface } : {}),
|
|
328
|
+
decidedBy: describePermissionDecider(ev),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return out;
|
|
332
|
+
}
|
|
182
333
|
/**
|
|
183
334
|
* The part of a cumulative snapshot that has not been shown yet.
|
|
184
335
|
*
|
|
@@ -271,6 +422,23 @@ function attachDeadlineFor(timeoutMs, refusal) {
|
|
|
271
422
|
function jitteredRetryDelay(retryAfterMs, random = Math.random) {
|
|
272
423
|
return Math.round(retryAfterMs * (1 + random() * 0.25));
|
|
273
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* `service` envelopes that carry a `notice` — words for the person, not
|
|
427
|
+
* diagnostics — projected as `session.notice` events. Today the only notice is
|
|
428
|
+
* `harness-session-reset`: the local harness could not resume its previous
|
|
429
|
+
* session and began a new one, so its local working context is gone.
|
|
430
|
+
*/
|
|
431
|
+
function projectSessionNotices(envelopes) {
|
|
432
|
+
const out = [];
|
|
433
|
+
for (const envelope of envelopes) {
|
|
434
|
+
const ev = envelope?.ev;
|
|
435
|
+
if (ev?.t !== 'service' || typeof ev.notice !== 'string' || typeof ev.text !== 'string') {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
out.push({ type: 'session.notice', notice: ev.notice, message: ev.text });
|
|
439
|
+
}
|
|
440
|
+
return out;
|
|
441
|
+
}
|
|
274
442
|
function detectSeqGap(highestApplied, incoming) {
|
|
275
443
|
if (!Number.isFinite(incoming) || incoming <= 0)
|
|
276
444
|
return null;
|
|
@@ -281,11 +449,54 @@ function detectSeqGap(highestApplied, incoming) {
|
|
|
281
449
|
return null;
|
|
282
450
|
return { from: highestApplied + 1, to: incoming - 1 };
|
|
283
451
|
}
|
|
284
|
-
|
|
452
|
+
/**
|
|
453
|
+
* The notice a reader sees when chunks went missing.
|
|
454
|
+
*
|
|
455
|
+
* It names `run.result` only when this client will actually fill `run.result`
|
|
456
|
+
* from the persisted message (`recordBackfill`). The sentence used to promise
|
|
457
|
+
* it unconditionally, while `run.result.text` was folded from the envelopes
|
|
458
|
+
* that DID arrive — so the one run told to trust `run.result` was the run whose
|
|
459
|
+
* `run.result.text` was empty, with the answer only in the record (OSK-12132).
|
|
460
|
+
*/
|
|
461
|
+
function seqGapNotice(gap, options = {}) {
|
|
285
462
|
const count = gap.to - gap.from + 1;
|
|
463
|
+
const where = options.recordBackfill === false
|
|
464
|
+
? 'The full answer is in the persisted message.'
|
|
465
|
+
: 'The full answer is in the persisted message and in run.result.';
|
|
286
466
|
return (`[${count} stream chunk${count === 1 ? '' : 's'} (seq ${gap.from}` +
|
|
287
467
|
`${count === 1 ? '' : `-${gap.to}`}) never arrived — the text above is incomplete. ` +
|
|
288
|
-
|
|
468
|
+
`${where}]`);
|
|
469
|
+
}
|
|
470
|
+
/** How long, at most, a gapped run waits for the record before settling anyway. */
|
|
471
|
+
const GAP_BACKFILL_ATTEMPTS = 3;
|
|
472
|
+
const GAP_BACKFILL_RETRY_MS = 400;
|
|
473
|
+
const PERMISSION_SOURCE_LABELS = {
|
|
474
|
+
request: 'this turn',
|
|
475
|
+
agent: "the agent's default",
|
|
476
|
+
harness: "the harness's default",
|
|
477
|
+
contract: 'the execution contract',
|
|
478
|
+
policy: 'the unattended-run policy',
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* The permission mode a `run.started` reports, and where it came from
|
|
482
|
+
* (OSK-12089). `null` when the event carries no mode report at all — a cloud
|
|
483
|
+
* turn from an older server, say — so nothing is claimed.
|
|
484
|
+
*/
|
|
485
|
+
function describeRunPermission(event) {
|
|
486
|
+
const source = typeof event.permissionModeSource === 'string' ? event.permissionModeSource : '';
|
|
487
|
+
const mode = typeof event.permissionMode === 'string' && event.permissionMode ? event.permissionMode : '';
|
|
488
|
+
if (!source)
|
|
489
|
+
return null;
|
|
490
|
+
if (!mode) {
|
|
491
|
+
return source === 'daemon'
|
|
492
|
+
? "set by the daemon's own config on that machine (no agent, harness or turn mode is set)"
|
|
493
|
+
: 'unknown';
|
|
494
|
+
}
|
|
495
|
+
const from = PERMISSION_SOURCE_LABELS[source] ?? source;
|
|
496
|
+
const coerced = typeof event.permissionModeCoercedFrom === 'string' && event.permissionModeCoercedFrom
|
|
497
|
+
? ` — ${event.permissionModeCoercedFrom} was asked for, which this harness cannot honour`
|
|
498
|
+
: '';
|
|
499
|
+
return `${mode} (from ${from})${coerced}`;
|
|
289
500
|
}
|
|
290
501
|
class AgenticStreamClient {
|
|
291
502
|
options;
|
|
@@ -314,8 +525,15 @@ class AgenticStreamClient {
|
|
|
314
525
|
* repeat count varies run to run and cannot be inferred by a consumer.
|
|
315
526
|
*/
|
|
316
527
|
seenLifecycle = new Set();
|
|
528
|
+
/**
|
|
529
|
+
* Whether this client's transport has dropped since it first connected. Until
|
|
530
|
+
* it has, every `session:join` it sends declares a FIRST join (OSK-12124).
|
|
531
|
+
*/
|
|
532
|
+
transportDropped = false;
|
|
317
533
|
/** `call` → tool name, so a daemon `tool-call-end` can be named (OSK-7730). */
|
|
318
534
|
toolNamesByCall = new Map();
|
|
535
|
+
/** `permission-ask` requestId → tool name, to name its resolution (OSK-12113). */
|
|
536
|
+
toolNamesByPermission = new Map();
|
|
319
537
|
/** Text already written to the terminal, for suffix-only rendering. */
|
|
320
538
|
renderedText = '';
|
|
321
539
|
idleTimer = null;
|
|
@@ -373,11 +591,30 @@ class AgenticStreamClient {
|
|
|
373
591
|
// the result instead of awaiting an attach nobody is still attempting.
|
|
374
592
|
if (this.settled || this.closed || !this.socket)
|
|
375
593
|
return result;
|
|
376
|
-
this.socket.emit('session:join',
|
|
594
|
+
this.socket.emit('session:join', this.sessionJoinPayload());
|
|
377
595
|
this.emit({ type: 'session.joined', sessionId: this.options.sessionId });
|
|
378
596
|
this.armIdleProbe();
|
|
379
597
|
return result;
|
|
380
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* The `session:join` payload.
|
|
601
|
+
*
|
|
602
|
+
* This client learns its session id from the HTTP acceptance and only then
|
|
603
|
+
* connects, while a daemon turn publishes its `session:init` at once. The
|
|
604
|
+
* server drops buffered frames older than the socket's connect time — right
|
|
605
|
+
* for a RE-connect, which already rendered them, and wrong here, where they
|
|
606
|
+
* were never seen: `run.started` (the dispatched mode and its source) never
|
|
607
|
+
* reached a daemon turn's stream (OSK-12124). `firstJoin` says so, and the
|
|
608
|
+
* server then replays the pre-connect lifecycle frames. Both joins of the
|
|
609
|
+
* first connection carry it, because the server's replay drains the shared
|
|
610
|
+
* buffer and either join may be the one that reaches it first; the stream's
|
|
611
|
+
* own lifecycle dedupe absorbs the overlap. After a drop it is not sent.
|
|
612
|
+
*/
|
|
613
|
+
sessionJoinPayload() {
|
|
614
|
+
return this.transportDropped
|
|
615
|
+
? { sessionId: this.options.sessionId }
|
|
616
|
+
: { sessionId: this.options.sessionId, firstJoin: true };
|
|
617
|
+
}
|
|
381
618
|
/**
|
|
382
619
|
* (Re)start the silence watchdog that asks the durable record what happened.
|
|
383
620
|
*
|
|
@@ -507,7 +744,7 @@ class AgenticStreamClient {
|
|
|
507
744
|
sessionId: this.options.sessionId,
|
|
508
745
|
fromSeq: gap.from,
|
|
509
746
|
toSeq: gap.to,
|
|
510
|
-
message: seqGapNotice(gap),
|
|
747
|
+
message: seqGapNotice(gap, { recordBackfill: Boolean(this.options.reconcileTerminal) }),
|
|
511
748
|
});
|
|
512
749
|
}
|
|
513
750
|
if (seq > this.highestSeq)
|
|
@@ -518,10 +755,11 @@ class AgenticStreamClient {
|
|
|
518
755
|
const socket = this.socket;
|
|
519
756
|
const match = (event) => !event.sessionId || event.sessionId === this.options.sessionId;
|
|
520
757
|
socket.on('connect', () => {
|
|
521
|
-
socket.emit('session:join',
|
|
758
|
+
socket.emit('session:join', this.sessionJoinPayload());
|
|
522
759
|
this.emit({ type: 'transport.connected', sessionId: this.options.sessionId });
|
|
523
760
|
});
|
|
524
761
|
socket.on('disconnect', (reason) => {
|
|
762
|
+
this.transportDropped = true;
|
|
525
763
|
if (this.settled)
|
|
526
764
|
return;
|
|
527
765
|
this.emit({ type: 'transport.disconnected', sessionId: this.options.sessionId, reason });
|
|
@@ -605,6 +843,18 @@ class AgenticStreamClient {
|
|
|
605
843
|
for (const projected of projectDaemonToolEvents(fresh, this.toolNamesByCall)) {
|
|
606
844
|
this.emitToolEvent({ ...projected, sessionId: this.options.sessionId });
|
|
607
845
|
}
|
|
846
|
+
// Notices meant for the person — a local harness that could not resume
|
|
847
|
+
// its previous session and started a fresh one (OSK-12131). A plain
|
|
848
|
+
// `service` line is diagnostic chatter and stays in the envelope; one
|
|
849
|
+
// marked `notice` is said out loud.
|
|
850
|
+
for (const notice of projectSessionNotices(fresh)) {
|
|
851
|
+
this.emit({ ...notice, sessionId: this.options.sessionId });
|
|
852
|
+
}
|
|
853
|
+
// Who decided each permission request — a person (and where), the
|
|
854
|
+
// mode, a policy — said beside the decision (OSK-12113).
|
|
855
|
+
for (const resolved of projectPermissionResolutions(fresh, this.toolNamesByPermission)) {
|
|
856
|
+
this.emit({ ...resolved, sessionId: this.options.sessionId });
|
|
857
|
+
}
|
|
608
858
|
this.emit({ ...scoped, type: 'session.output' });
|
|
609
859
|
return;
|
|
610
860
|
}
|
|
@@ -693,6 +943,10 @@ class AgenticStreamClient {
|
|
|
693
943
|
toolUseId: event.toolUseId,
|
|
694
944
|
toolName: event.toolName,
|
|
695
945
|
reason: 'CLI was not started with --approve-tools',
|
|
946
|
+
// Nobody was asked: this client could not put the prompt to anyone.
|
|
947
|
+
// The server records it as a policy decision, not a person's
|
|
948
|
+
// (OSK-12113) — it used to read "a person on the skrr CLI".
|
|
949
|
+
automatic: true,
|
|
696
950
|
});
|
|
697
951
|
}
|
|
698
952
|
this.emit({ ...event, type: 'permission.denied', reason: 'noninteractive_or_not_enabled' });
|
|
@@ -731,7 +985,7 @@ class AgenticStreamClient {
|
|
|
731
985
|
});
|
|
732
986
|
return;
|
|
733
987
|
}
|
|
734
|
-
const answer = await this.ask(`Allow
|
|
988
|
+
const answer = await this.ask(`Allow ${describePermission(event)}? [y/N] `);
|
|
735
989
|
const approved = /^y(es)?$/i.test(answer.trim());
|
|
736
990
|
this.socket.emit(approved ? 'permission:approve' : 'permission:deny', {
|
|
737
991
|
sessionId: this.options.sessionId,
|
|
@@ -843,7 +1097,19 @@ class AgenticStreamClient {
|
|
|
843
1097
|
writeJsonLine(output, event);
|
|
844
1098
|
return;
|
|
845
1099
|
}
|
|
846
|
-
if (event.type === '
|
|
1100
|
+
if (event.type === 'run.started') {
|
|
1101
|
+
const permission = describeRunPermission(event);
|
|
1102
|
+
if (permission)
|
|
1103
|
+
output.write(`[run] permission: ${permission}\n`);
|
|
1104
|
+
}
|
|
1105
|
+
else if ((event.type === 'run.completed' || event.type === 'run.failed') &&
|
|
1106
|
+
typeof event.harnessModel === 'string' &&
|
|
1107
|
+
event.harnessModel) {
|
|
1108
|
+
// Which model the harness ran — the agent's pick, or what `default`
|
|
1109
|
+
// resolved to on that machine (OSK-12111).
|
|
1110
|
+
output.write(`[run] model: ${event.harnessModel} (reported by the harness)\n`);
|
|
1111
|
+
}
|
|
1112
|
+
else if (event.type === 'text.delta' && typeof event.text === 'string') {
|
|
847
1113
|
// Print what is NEW. See `renderableDelta` — the old `\r` repaint could
|
|
848
1114
|
// not rewind past a newline, so a multi-line answer was reprinted once
|
|
849
1115
|
// per snapshot and its blocks fused (OSK-7701, OSK-7731).
|
|
@@ -870,18 +1136,31 @@ class AgenticStreamClient {
|
|
|
870
1136
|
output.write(`\n${String(event.message ?? '')}\n`);
|
|
871
1137
|
this.renderedText = this.accumulated;
|
|
872
1138
|
}
|
|
1139
|
+
else if (event.type === 'permission.resolved') {
|
|
1140
|
+
const verb = event.decision === 'allow' ? 'allowed' : 'denied';
|
|
1141
|
+
output.write(`\n[permission] ${String(event.toolName ?? 'tool')} ${verb} by ${String(event.decidedBy ?? '')}\n`);
|
|
1142
|
+
this.renderedText = this.accumulated;
|
|
1143
|
+
}
|
|
1144
|
+
else if (event.type === 'session.notice') {
|
|
1145
|
+
output.write(`\n[session] ${String(event.message ?? '')}\n`);
|
|
1146
|
+
this.renderedText = this.accumulated;
|
|
1147
|
+
}
|
|
873
1148
|
else if (event.type === 'transport.disconnected') {
|
|
874
1149
|
output.write(`\n[reconnecting] ${String(event.reason ?? '')}\n`);
|
|
875
1150
|
this.renderedText = this.accumulated;
|
|
876
1151
|
}
|
|
877
|
-
else if (event.type === 'permission.requested'
|
|
1152
|
+
else if (event.type === 'permission.requested') {
|
|
1153
|
+
output.write(`\n[${event.type}] ${describePermission(event)}\n`);
|
|
1154
|
+
this.renderedText = this.accumulated;
|
|
1155
|
+
}
|
|
1156
|
+
else if (event.type === 'question.requested') {
|
|
878
1157
|
output.write(`\n[${event.type}]\n`);
|
|
879
1158
|
this.renderedText = this.accumulated;
|
|
880
1159
|
}
|
|
881
1160
|
else if (event.type === 'permission.approved' && event.auto === true) {
|
|
882
1161
|
// Said out loud, because nobody was asked: the flag approved it, and a
|
|
883
1162
|
// reader of this log should see which tools that covered (OSK-10323).
|
|
884
|
-
output.write(`\n[permission] ${
|
|
1163
|
+
output.write(`\n[permission] ${describePermission(event)} allowed by --approve-tools\n`);
|
|
885
1164
|
this.renderedText = this.accumulated;
|
|
886
1165
|
}
|
|
887
1166
|
else if (event.type === 'question.answered' && event.nonInteractive) {
|
|
@@ -894,6 +1173,42 @@ class AgenticStreamClient {
|
|
|
894
1173
|
return;
|
|
895
1174
|
this.settled = true;
|
|
896
1175
|
this.lastEvent = event;
|
|
1176
|
+
// A stream that lost chunks told its reader the full answer would be in
|
|
1177
|
+
// `run.result`. The terminal event does not always carry it — a failed
|
|
1178
|
+
// turn's `execution:error` has no `result` — so read it from the record
|
|
1179
|
+
// before settling, or the promise is false exactly when it was made
|
|
1180
|
+
// (OSK-12132).
|
|
1181
|
+
if (this.reportedGap && this.options.reconcileTerminal) {
|
|
1182
|
+
void this.readRecordedText().then((recorded) => this.settle(status, event, recorded));
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
this.settle(status, event, undefined);
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* The answer as the durable record holds it, for a run whose stream had a
|
|
1189
|
+
* hole. Bounded: the record is written before the terminal event is sent, so
|
|
1190
|
+
* it is normally there at once, and a record that stays silent must not hold
|
|
1191
|
+
* the run open — the reader then gets what the stream delivered.
|
|
1192
|
+
*/
|
|
1193
|
+
async readRecordedText() {
|
|
1194
|
+
const reconcile = this.options.reconcileTerminal;
|
|
1195
|
+
if (!reconcile)
|
|
1196
|
+
return undefined;
|
|
1197
|
+
for (let attempt = 0; attempt < GAP_BACKFILL_ATTEMPTS; attempt += 1) {
|
|
1198
|
+
if (attempt > 0)
|
|
1199
|
+
await new Promise((r) => setTimeout(r, GAP_BACKFILL_RETRY_MS));
|
|
1200
|
+
try {
|
|
1201
|
+
const terminal = await reconcile();
|
|
1202
|
+
if (terminal && typeof terminal.text === 'string' && terminal.text)
|
|
1203
|
+
return terminal.text;
|
|
1204
|
+
}
|
|
1205
|
+
catch {
|
|
1206
|
+
// No evidence either way; try again, then settle on the stream.
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return undefined;
|
|
1210
|
+
}
|
|
1211
|
+
settle(status, event, recordedText) {
|
|
897
1212
|
// `execution:complete` carries the final answer as `result`, and it is
|
|
898
1213
|
// AUTHORITATIVE — preferred over whatever the deltas added up to, not just
|
|
899
1214
|
// used when they added up to nothing.
|
|
@@ -922,6 +1237,12 @@ class AgenticStreamClient {
|
|
|
922
1237
|
if (finalText && finalText !== this.accumulated && !accumulationIsSuperset) {
|
|
923
1238
|
this.accumulated = finalText;
|
|
924
1239
|
}
|
|
1240
|
+
// The record wins over a stream known to have lost chunks, unless what
|
|
1241
|
+
// arrived is already longer (the record lags a completed stream, never the
|
|
1242
|
+
// other way round).
|
|
1243
|
+
if (recordedText && recordedText.length > this.accumulated.length) {
|
|
1244
|
+
this.accumulated = recordedText;
|
|
1245
|
+
}
|
|
925
1246
|
// The finished answer, as every other surface shows it: the web applies
|
|
926
1247
|
// this same sanitizer, so a scheduling JSON block or leaked function-call
|
|
927
1248
|
// XML the model wrote into its reply is removed here too instead of being
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type AuthBundle } from './auth-storage';
|
|
2
1
|
type DedicatedGenerationPreparationOptions = {
|
|
3
2
|
machineUuid?: string;
|
|
4
3
|
configDir?: string;
|
|
@@ -43,15 +42,9 @@ export declare class CliCredentialInitializationError extends Error {
|
|
|
43
42
|
* `isCredEnvelopeActive()` to decide whether to wrap the private
|
|
44
43
|
* key on persist.
|
|
45
44
|
* 2. `initDeviceIdentity` second.
|
|
46
|
-
* 3. Server enrollment is fire-and-forget; only attempts if a current
|
|
47
|
-
* access token is already on disk (peek via the file backend slot
|
|
48
|
-
* so a missing token short-circuits cleanly without creating a
|
|
49
|
-
* whole CredentialResolver chain here).
|
|
50
45
|
*
|
|
51
|
-
* The
|
|
52
|
-
*
|
|
53
|
-
* spawn the auth-helper when the user is unauthenticated. Mirrors the
|
|
54
|
-
* daemon's `peekAuthAccessToken()` shape.
|
|
46
|
+
* The CLI never enrolls the device key with the server; see the comment
|
|
47
|
+
* in `ensureCliAuthPrefetchStarted` (OSK-12063).
|
|
55
48
|
*/
|
|
56
49
|
export declare function prefetchCliAuth(): Promise<void>;
|
|
57
50
|
/**
|
|
@@ -63,5 +56,4 @@ export declare function prefetchCliAuth(): Promise<void>;
|
|
|
63
56
|
export declare function waitForCliCredentialEnvelope(): Promise<void>;
|
|
64
57
|
/** @internal Test seam — reset the module-level one-shot initialization. */
|
|
65
58
|
export declare function __resetPrefetchCliAuthForTest(): void;
|
|
66
|
-
export declare function shouldUseAccessTokenForEnrollment(bundle: Pick<AuthBundle, 'token' | 'expiresAt'> | null | undefined, nowMs?: number, minTtlMs?: number): boolean;
|
|
67
59
|
export {};
|