@skrr-ai/cli 0.1.54 → 0.1.56
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 +8 -1
- package/dist/base-command.js +24 -1
- package/dist/lib/agentic-stream.d.ts +10 -0
- package/dist/lib/agentic-stream.js +24 -1
- package/dist/lib/auth-core-init.d.ts +48 -0
- package/dist/lib/auth-core-init.js +100 -2
- package/dist/lib/cli-identity.js +1 -2
- package/dist/lib/config.d.ts +10 -0
- package/dist/lib/config.js +29 -0
- package/dist/lib/credential-resolver.d.ts +27 -0
- package/dist/lib/credential-resolver.js +19 -0
- package/dist/lib/daemonBroker.d.ts +7 -0
- package/dist/lib/daemonBroker.js +1 -0
- package/dist/lib/device-id.d.ts +23 -0
- package/dist/lib/device-id.js +68 -0
- package/dist/lib/first-party-harness-broker.js +4 -4
- package/dist/node_modules/@skrr-ai/data-provider/index.js +1746 -1743
- package/oclif.manifest.json +4688 -4688
- package/package.json +1 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { resolveProfileFromArgv } from './lib/keychain';
|
|
|
3
3
|
import { type AssigneeResolution, type MemberKind, type SpaceMember } from './lib/assignee-resolver';
|
|
4
4
|
import { type CliConfig } from './lib/config';
|
|
5
5
|
import { type OutboxMethod } from './lib/outbox';
|
|
6
|
-
import { type ResolvedCredential } from './lib/credential-resolver';
|
|
6
|
+
import { type CredentialProvenance, type ResolvedCredential } from './lib/credential-resolver';
|
|
7
7
|
import { type BrokerRefusalDescription } from './lib/daemonBrokerRefusal';
|
|
8
8
|
import { type DefaultAgentResolution } from './lib/tasks';
|
|
9
9
|
export declare function assigneePayloadNeedsResolution(payload: Record<string, unknown>): boolean;
|
|
@@ -68,6 +68,13 @@ export declare abstract class BaseCommand extends Command {
|
|
|
68
68
|
* from here.
|
|
69
69
|
*/
|
|
70
70
|
protected resolvedCredential: ResolvedCredential;
|
|
71
|
+
/**
|
|
72
|
+
* Where `resolvedCredential` really came from when its `source` slot cannot
|
|
73
|
+
* say — an in-memory daemon hand-off or Agent session token is recorded as
|
|
74
|
+
* `env-token` for refresh semantics, but was not read from OVERSKY_TOKEN
|
|
75
|
+
* (OSK-12185). Null for every stored/env/flag credential.
|
|
76
|
+
*/
|
|
77
|
+
protected credentialProvenance: CredentialProvenance | null;
|
|
71
78
|
protected bareMode: boolean;
|
|
72
79
|
/**
|
|
73
80
|
* Set when the auto-broker reached a live daemon and it REFUSED the hand-off.
|
package/dist/base-command.js
CHANGED
|
@@ -132,6 +132,13 @@ class BaseCommand extends core_1.Command {
|
|
|
132
132
|
* from here.
|
|
133
133
|
*/
|
|
134
134
|
resolvedCredential;
|
|
135
|
+
/**
|
|
136
|
+
* Where `resolvedCredential` really came from when its `source` slot cannot
|
|
137
|
+
* say — an in-memory daemon hand-off or Agent session token is recorded as
|
|
138
|
+
* `env-token` for refresh semantics, but was not read from OVERSKY_TOKEN
|
|
139
|
+
* (OSK-12185). Null for every stored/env/flag credential.
|
|
140
|
+
*/
|
|
141
|
+
credentialProvenance = null;
|
|
135
142
|
bareMode = false;
|
|
136
143
|
/**
|
|
137
144
|
* Set when the auto-broker reached a live daemon and it REFUSED the hand-off.
|
|
@@ -173,6 +180,7 @@ class BaseCommand extends core_1.Command {
|
|
|
173
180
|
return;
|
|
174
181
|
}
|
|
175
182
|
autonomousCredential = delegatedCredential;
|
|
183
|
+
this.credentialProvenance = { kind: 'daemon-delegated' };
|
|
176
184
|
}
|
|
177
185
|
catch (err) {
|
|
178
186
|
this.failWithCliError({
|
|
@@ -214,6 +222,10 @@ class BaseCommand extends core_1.Command {
|
|
|
214
222
|
delete process.env.OVERSKY_WORKSPACE_ID;
|
|
215
223
|
}
|
|
216
224
|
this.resolvedCredential = autonomousCredential;
|
|
225
|
+
if (!this.credentialProvenance && autonomousCredential.source === 'env-token') {
|
|
226
|
+
// `resolveTrustedAutonomousCredential` read OVERSKY_SESSION_TOKEN.
|
|
227
|
+
this.credentialProvenance = { kind: 'agent-session' };
|
|
228
|
+
}
|
|
217
229
|
(0, node_adapter_1.setAdapterCredentialOverride)(this.resolvedCredential);
|
|
218
230
|
(0, data_provider_1.setHttpAdapter)((0, node_adapter_1.createNodeAdapter)());
|
|
219
231
|
return;
|
|
@@ -327,6 +339,17 @@ class BaseCommand extends core_1.Command {
|
|
|
327
339
|
: 'file',
|
|
328
340
|
kind: (0, auth_core_1.classifyTokenKind)(accessToken),
|
|
329
341
|
};
|
|
342
|
+
// The slot above says `env-token`; the label must not. The token
|
|
343
|
+
// came from the daemon's hand-off descriptor, and OVERSKY_TOKEN is
|
|
344
|
+
// typically unset (OSK-12185).
|
|
345
|
+
this.credentialProvenance = autoResult.outcome.brokered
|
|
346
|
+
? {
|
|
347
|
+
kind: 'daemon-handoff',
|
|
348
|
+
...(autoResult.outcome.descriptorPath
|
|
349
|
+
? { descriptorPath: autoResult.outcome.descriptorPath }
|
|
350
|
+
: {}),
|
|
351
|
+
}
|
|
352
|
+
: null;
|
|
330
353
|
}
|
|
331
354
|
else if ((this.brokerRefusal = (0, daemonBrokerRefusal_1.describeBrokerRefusal)(autoResult.outcome, this.config.bin))) {
|
|
332
355
|
// Reported by requireAuth(); nothing to print here.
|
|
@@ -821,7 +844,7 @@ class BaseCommand extends core_1.Command {
|
|
|
821
844
|
credentialSourceLabel() {
|
|
822
845
|
if (!this.resolvedCredential?.token)
|
|
823
846
|
return null;
|
|
824
|
-
return (0, credential_resolver_1.
|
|
847
|
+
return (0, credential_resolver_1.describeCredentialSource)(this.resolvedCredential.source, this.credentialProvenance);
|
|
825
848
|
}
|
|
826
849
|
async catch(err) {
|
|
827
850
|
if (isOclifExitError(err)) {
|
|
@@ -173,6 +173,16 @@ export type ProjectedToolEvent = {
|
|
|
173
173
|
* of the run. The pairing is unambiguous within a turn.
|
|
174
174
|
*/
|
|
175
175
|
export declare function projectDaemonToolEvents(envelopes: unknown[], namesByCall: Map<string, string>): ProjectedToolEvent[];
|
|
176
|
+
/**
|
|
177
|
+
* The line printed when a tool call ends, in the words its status earns.
|
|
178
|
+
*
|
|
179
|
+
* Every end used to print `[tool completed]`, including a failed call. And a
|
|
180
|
+
* call the harness never closed — which the daemon now reports as
|
|
181
|
+
* `unreported` rather than `failed` (OSK-12188) — must read as neither: it may
|
|
182
|
+
* well have run. Its close is made when the session exits, so the line says
|
|
183
|
+
* that too, rather than let the moment it prints stand for when the call ran.
|
|
184
|
+
*/
|
|
185
|
+
export declare function describeToolEnd(toolName: unknown, status: unknown): string;
|
|
176
186
|
/**
|
|
177
187
|
* WHO or WHAT decided a permission request, as a reader needs it (OSK-12113).
|
|
178
188
|
*
|
|
@@ -7,6 +7,7 @@ exports.envelopesText = envelopesText;
|
|
|
7
7
|
exports.envelopeKey = envelopeKey;
|
|
8
8
|
exports.unseenEnvelopes = unseenEnvelopes;
|
|
9
9
|
exports.projectDaemonToolEvents = projectDaemonToolEvents;
|
|
10
|
+
exports.describeToolEnd = describeToolEnd;
|
|
10
11
|
exports.describePermissionDecider = describePermissionDecider;
|
|
11
12
|
exports.projectPermissionResolutions = projectPermissionResolutions;
|
|
12
13
|
exports.renderableDelta = renderableDelta;
|
|
@@ -249,6 +250,28 @@ function projectDaemonToolEvents(envelopes, namesByCall) {
|
|
|
249
250
|
}
|
|
250
251
|
return out;
|
|
251
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* The line printed when a tool call ends, in the words its status earns.
|
|
255
|
+
*
|
|
256
|
+
* Every end used to print `[tool completed]`, including a failed call. And a
|
|
257
|
+
* call the harness never closed — which the daemon now reports as
|
|
258
|
+
* `unreported` rather than `failed` (OSK-12188) — must read as neither: it may
|
|
259
|
+
* well have run. Its close is made when the session exits, so the line says
|
|
260
|
+
* that too, rather than let the moment it prints stand for when the call ran.
|
|
261
|
+
*/
|
|
262
|
+
function describeToolEnd(toolName, status) {
|
|
263
|
+
const name = String(toolName ?? 'unknown');
|
|
264
|
+
switch (status) {
|
|
265
|
+
case 'failed':
|
|
266
|
+
return `[tool failed] ${name}`;
|
|
267
|
+
case 'cancelled':
|
|
268
|
+
return `[tool cancelled] ${name}`;
|
|
269
|
+
case 'unreported':
|
|
270
|
+
return `[tool result not reported] ${name} (the harness never said how it ended; closed when the session ended)`;
|
|
271
|
+
default:
|
|
272
|
+
return `[tool completed] ${name}`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
252
275
|
const SURFACE_LABELS = {
|
|
253
276
|
web: 'the web app',
|
|
254
277
|
cli: 'the skrr CLI',
|
|
@@ -1126,7 +1149,7 @@ class AgenticStreamClient {
|
|
|
1126
1149
|
this.renderedText = this.accumulated;
|
|
1127
1150
|
}
|
|
1128
1151
|
else if (event.type === 'tool.completed') {
|
|
1129
|
-
output.write(
|
|
1152
|
+
output.write(`${describeToolEnd(event.toolName, event.status)}\n`);
|
|
1130
1153
|
this.renderedText = this.accumulated;
|
|
1131
1154
|
}
|
|
1132
1155
|
else if (event.type === 'stream.gap') {
|
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A credential-envelope failure that carries a hint, as ONE plain line
|
|
3
|
+
* (OSK-12180) — or null when the warning is anything else.
|
|
4
|
+
*
|
|
5
|
+
* An unreadable stored credential is reported on every command until someone
|
|
6
|
+
* signs in again, and auth-core's structured form is six lines of object dump
|
|
7
|
+
* ending in the raw CryptoKit/OpenSSL string (`Unsupported state or unable to
|
|
8
|
+
* authenticate data`). The person reading it needs the hint auth-core already
|
|
9
|
+
* wrote for exactly this, and what to do; the key id and the crypto message are
|
|
10
|
+
* for whoever is debugging, which is what `DEBUG` is for everywhere else in
|
|
11
|
+
* this logger. The daemon logs through its own logger and keeps the structured
|
|
12
|
+
* record.
|
|
13
|
+
*
|
|
14
|
+
* The remedy is added only to the DEFAULT hint, which is about this config's
|
|
15
|
+
* own credential. A caller that supplied its own hint knows the case better —
|
|
16
|
+
* another config's credential is not fixed by `skrr login` here, and its hint
|
|
17
|
+
* says so (OSK-12135).
|
|
18
|
+
*/
|
|
19
|
+
export declare function plainCredentialEnvelopeWarning(args: readonly unknown[]): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* The one line an EXPECTED credential-key change prints (OSK-12186).
|
|
22
|
+
*
|
|
23
|
+
* When the key that wraps this profile's credential key changes underneath it
|
|
24
|
+
* — a Dedicated guest moved to a new image gets a new `/etc/machine-id`, which
|
|
25
|
+
* the Linux key is derived from; a new Mac or a reset Keychain does the same —
|
|
26
|
+
* auth-core cannot unwrap the old key, enrolls a new one and carries on. That
|
|
27
|
+
* is recovery, not damage, but it reached the terminal as four warnings: the
|
|
28
|
+
* unwrap failure, the rotation notice, and one decrypt failure per credential
|
|
29
|
+
* still sealed under the old key, each ending in a raw crypto string. The
|
|
30
|
+
* second run was clean, which is exactly what corruption does not look like.
|
|
31
|
+
*
|
|
32
|
+
* It is distinct from a GENUINELY unreadable credential (OSK-12180): there the
|
|
33
|
+
* key unwraps and one stored value still cannot be opened, nothing will fix it
|
|
34
|
+
* by itself, and that line names `skrr login`. Here the old values are
|
|
35
|
+
* unreadable BECAUSE the key was replaced, and the next step happens on its
|
|
36
|
+
* own when a local daemon can hand a credential over.
|
|
37
|
+
*/
|
|
38
|
+
export declare const CREDENTIAL_KEY_CHANGED_NOTICE: string;
|
|
39
|
+
/**
|
|
40
|
+
* What the CLI prints for one auth-core warning: a replacement line, nothing
|
|
41
|
+
* (`''`), or null for "print it as auth-core wrote it".
|
|
42
|
+
*
|
|
43
|
+
* Stateful by design — the process that detected a key change has already
|
|
44
|
+
* told the person why every old credential is unreadable, so the per-value
|
|
45
|
+
* decrypt failures that follow add nothing. A fresh process starts clean, and
|
|
46
|
+
* a later genuinely unreadable credential still gets its own OSK-12180 line.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createCredentialEnvelopeWarningPresenter(): (args: readonly unknown[]) => string | null;
|
|
1
49
|
type DedicatedGenerationPreparationOptions = {
|
|
2
50
|
machineUuid?: string;
|
|
3
51
|
configDir?: string;
|
|
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CliCredentialInitializationError = void 0;
|
|
6
|
+
exports.CliCredentialInitializationError = exports.CREDENTIAL_KEY_CHANGED_NOTICE = void 0;
|
|
7
|
+
exports.plainCredentialEnvelopeWarning = plainCredentialEnvelopeWarning;
|
|
8
|
+
exports.createCredentialEnvelopeWarningPresenter = createCredentialEnvelopeWarningPresenter;
|
|
7
9
|
exports.prepareDedicatedRuntimeAuthGeneration = prepareDedicatedRuntimeAuthGeneration;
|
|
8
10
|
exports.ensureAuthCoreConfigured = ensureAuthCoreConfigured;
|
|
9
11
|
exports.prefetchCliAuth = prefetchCliAuth;
|
|
@@ -44,6 +46,94 @@ function envFlag(name) {
|
|
|
44
46
|
const v = process.env[name];
|
|
45
47
|
return v === '1' || v === 'true' || v === 'TRUE';
|
|
46
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* A credential-envelope failure that carries a hint, as ONE plain line
|
|
51
|
+
* (OSK-12180) — or null when the warning is anything else.
|
|
52
|
+
*
|
|
53
|
+
* An unreadable stored credential is reported on every command until someone
|
|
54
|
+
* signs in again, and auth-core's structured form is six lines of object dump
|
|
55
|
+
* ending in the raw CryptoKit/OpenSSL string (`Unsupported state or unable to
|
|
56
|
+
* authenticate data`). The person reading it needs the hint auth-core already
|
|
57
|
+
* wrote for exactly this, and what to do; the key id and the crypto message are
|
|
58
|
+
* for whoever is debugging, which is what `DEBUG` is for everywhere else in
|
|
59
|
+
* this logger. The daemon logs through its own logger and keeps the structured
|
|
60
|
+
* record.
|
|
61
|
+
*
|
|
62
|
+
* The remedy is added only to the DEFAULT hint, which is about this config's
|
|
63
|
+
* own credential. A caller that supplied its own hint knows the case better —
|
|
64
|
+
* another config's credential is not fixed by `skrr login` here, and its hint
|
|
65
|
+
* says so (OSK-12135).
|
|
66
|
+
*/
|
|
67
|
+
function plainCredentialEnvelopeWarning(args) {
|
|
68
|
+
const [message, metadata] = args;
|
|
69
|
+
if (typeof message !== 'string' || !message.startsWith('[credEnvelope] '))
|
|
70
|
+
return null;
|
|
71
|
+
if (!metadata || typeof metadata !== 'object')
|
|
72
|
+
return null;
|
|
73
|
+
const hint = metadata.hint;
|
|
74
|
+
if (typeof hint !== 'string' || !hint.trim())
|
|
75
|
+
return null;
|
|
76
|
+
const remedy = hint === auth_core_1.DEFAULT_UNREADABLE_CREDENTIAL_HINT ? ' Run `skrr login`.' : '';
|
|
77
|
+
return `warning: ${hint.trim()}.${remedy} (DEBUG=1 shows the underlying error.)`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The one line an EXPECTED credential-key change prints (OSK-12186).
|
|
81
|
+
*
|
|
82
|
+
* When the key that wraps this profile's credential key changes underneath it
|
|
83
|
+
* — a Dedicated guest moved to a new image gets a new `/etc/machine-id`, which
|
|
84
|
+
* the Linux key is derived from; a new Mac or a reset Keychain does the same —
|
|
85
|
+
* auth-core cannot unwrap the old key, enrolls a new one and carries on. That
|
|
86
|
+
* is recovery, not damage, but it reached the terminal as four warnings: the
|
|
87
|
+
* unwrap failure, the rotation notice, and one decrypt failure per credential
|
|
88
|
+
* still sealed under the old key, each ending in a raw crypto string. The
|
|
89
|
+
* second run was clean, which is exactly what corruption does not look like.
|
|
90
|
+
*
|
|
91
|
+
* It is distinct from a GENUINELY unreadable credential (OSK-12180): there the
|
|
92
|
+
* key unwraps and one stored value still cannot be opened, nothing will fix it
|
|
93
|
+
* by itself, and that line names `skrr login`. Here the old values are
|
|
94
|
+
* unreadable BECAUSE the key was replaced, and the next step happens on its
|
|
95
|
+
* own when a local daemon can hand a credential over.
|
|
96
|
+
*/
|
|
97
|
+
exports.CREDENTIAL_KEY_CHANGED_NOTICE = "note: this machine's credential key changed (a new machine or image), so credentials " +
|
|
98
|
+
'saved under the old key can no longer be read. skrr gets a new one from the local daemon when ' +
|
|
99
|
+
'one is running; otherwise run `skrr login`. (DEBUG=1 shows the underlying error.)';
|
|
100
|
+
/**
|
|
101
|
+
* What the CLI prints for one auth-core warning: a replacement line, nothing
|
|
102
|
+
* (`''`), or null for "print it as auth-core wrote it".
|
|
103
|
+
*
|
|
104
|
+
* Stateful by design — the process that detected a key change has already
|
|
105
|
+
* told the person why every old credential is unreadable, so the per-value
|
|
106
|
+
* decrypt failures that follow add nothing. A fresh process starts clean, and
|
|
107
|
+
* a later genuinely unreadable credential still gets its own OSK-12180 line.
|
|
108
|
+
*/
|
|
109
|
+
function createCredentialEnvelopeWarningPresenter() {
|
|
110
|
+
let keyChanged = false;
|
|
111
|
+
return (args) => {
|
|
112
|
+
const [message, metadata] = args;
|
|
113
|
+
const event = typeof message === 'string' && message.startsWith('[credEnvelope] ')
|
|
114
|
+
? message.slice('[credEnvelope] '.length)
|
|
115
|
+
: null;
|
|
116
|
+
const meta = (metadata && typeof metadata === 'object' ? metadata : {});
|
|
117
|
+
// auth-core emits this and then, from the same branch, `kek_rotated`: the
|
|
118
|
+
// wrapped key was present and would not unwrap. The notice below is the
|
|
119
|
+
// one line for both.
|
|
120
|
+
if (event === 'cred_envelope.unwrap.failed' && meta.reason === 'wrapped_dek_unwrap_threw') {
|
|
121
|
+
return '';
|
|
122
|
+
}
|
|
123
|
+
if (event === 'cred_envelope.kek_rotated') {
|
|
124
|
+
if (keyChanged)
|
|
125
|
+
return '';
|
|
126
|
+
keyChanged = true;
|
|
127
|
+
return exports.CREDENTIAL_KEY_CHANGED_NOTICE;
|
|
128
|
+
}
|
|
129
|
+
// Every value sealed under the old key fails here now — the notice has
|
|
130
|
+
// already said why. A MALFORMED envelope is a different fact and still prints.
|
|
131
|
+
if (keyChanged && event === 'cred_envelope.read.decrypt_failed')
|
|
132
|
+
return '';
|
|
133
|
+
return plainCredentialEnvelopeWarning(args);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const presentCredentialEnvelopeWarning = createCredentialEnvelopeWarningPresenter();
|
|
47
137
|
const cliLogger = {
|
|
48
138
|
// The CLI is a short-lived foreground process. Trace/debug get dropped
|
|
49
139
|
// unless DEBUG is set so we don't spam terminal output.
|
|
@@ -55,7 +145,15 @@ const cliLogger = {
|
|
|
55
145
|
if (process.env.DEBUG)
|
|
56
146
|
console.error('[auth-core info]', ...args);
|
|
57
147
|
},
|
|
58
|
-
warn: (...args) =>
|
|
148
|
+
warn: (...args) => {
|
|
149
|
+
const plain = process.env.DEBUG ? null : presentCredentialEnvelopeWarning(args);
|
|
150
|
+
if (plain === '')
|
|
151
|
+
return;
|
|
152
|
+
if (plain)
|
|
153
|
+
console.error(plain);
|
|
154
|
+
else
|
|
155
|
+
console.error('[auth-core warn]', ...args);
|
|
156
|
+
},
|
|
59
157
|
error: (...args) => console.error('[auth-core error]', ...args),
|
|
60
158
|
};
|
|
61
159
|
/**
|
package/dist/lib/cli-identity.js
CHANGED
|
@@ -74,7 +74,6 @@ const path = __importStar(require("node:path"));
|
|
|
74
74
|
const auth_storage_1 = require("./auth-storage");
|
|
75
75
|
const config_1 = require("./config");
|
|
76
76
|
const cli_id_1 = require("./cli-id");
|
|
77
|
-
const device_id_1 = require("./device-id");
|
|
78
77
|
const keychain_1 = require("./keychain");
|
|
79
78
|
/**
|
|
80
79
|
* A named profile's own identity. Deterministic in (host, user, config root,
|
|
@@ -127,7 +126,7 @@ function planLoginCliId(cfg, deps = {}) {
|
|
|
127
126
|
const machine = cfg.cliId?.trim();
|
|
128
127
|
if (machine)
|
|
129
128
|
return { cliId: machine, source: 'machine' };
|
|
130
|
-
const cliId = (0,
|
|
129
|
+
const cliId = (0, config_1.machineCliId)();
|
|
131
130
|
return { cliId, source: 'machine', updatedConfig: { ...cfg, cliId } };
|
|
132
131
|
}
|
|
133
132
|
/**
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -190,6 +190,16 @@ export declare function isEnvAuthOverride(): boolean;
|
|
|
190
190
|
* resolver and every existing install read it.
|
|
191
191
|
*/
|
|
192
192
|
export declare function configRoot(env?: NodeJS.ProcessEnv): string;
|
|
193
|
+
/**
|
|
194
|
+
* The machine cliId THIS config root mints when it has none stored
|
|
195
|
+
* (OSK-12179). The primary root keeps the historical (host, user) id; any other
|
|
196
|
+
* root gets one of its own, so two roots of one user are two server-side
|
|
197
|
+
* families and one root's hand-off confirm cannot retire the other's session.
|
|
198
|
+
*
|
|
199
|
+
* Mints nothing and reads nothing stored: callers persist it (`cli-identity.ts`)
|
|
200
|
+
* or present it only when nothing is stored (the daemon broker).
|
|
201
|
+
*/
|
|
202
|
+
export declare function machineCliId(): string;
|
|
193
203
|
/**
|
|
194
204
|
* The PRE-RENAME config root — the generation between `.sky` and `.skrr`.
|
|
195
205
|
*
|
package/dist/lib/config.js
CHANGED
|
@@ -39,6 +39,7 @@ exports.describeDeployment = describeDeployment;
|
|
|
39
39
|
exports.canonicalizeKnownBaseURL = canonicalizeKnownBaseURL;
|
|
40
40
|
exports.isEnvAuthOverride = isEnvAuthOverride;
|
|
41
41
|
exports.configRoot = configRoot;
|
|
42
|
+
exports.machineCliId = machineCliId;
|
|
42
43
|
exports.priorConfigDir = priorConfigDir;
|
|
43
44
|
exports.loadConfig = loadConfig;
|
|
44
45
|
exports.saveConfig = saveConfig;
|
|
@@ -54,6 +55,7 @@ const path = __importStar(require("node:path"));
|
|
|
54
55
|
const keychain_1 = require("./keychain");
|
|
55
56
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
56
57
|
const cli_id_1 = require("./cli-id");
|
|
58
|
+
const device_id_1 = require("./device-id");
|
|
57
59
|
const publicEndpoints_generated_1 = require("./publicEndpoints.generated");
|
|
58
60
|
// Both from config/public-endpoints.json, the one domain contract, rather than a
|
|
59
61
|
// fourth hand-copied pair. The CLI previously knew only about dev — there was no
|
|
@@ -269,6 +271,26 @@ function configRoot(env = process.env) {
|
|
|
269
271
|
function newConfigDir() {
|
|
270
272
|
return configRoot();
|
|
271
273
|
}
|
|
274
|
+
/** The root this machine uses when nothing relocates it (`~/.skrr`). */
|
|
275
|
+
function defaultConfigRoot() {
|
|
276
|
+
return (0, auth_core_1.resolveConfigRoot)({});
|
|
277
|
+
}
|
|
278
|
+
/** Is this process on the primary root, however it was reached? */
|
|
279
|
+
function isDefaultConfigRoot() {
|
|
280
|
+
return path.resolve(configRoot()) === path.resolve(defaultConfigRoot());
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The machine cliId THIS config root mints when it has none stored
|
|
284
|
+
* (OSK-12179). The primary root keeps the historical (host, user) id; any other
|
|
285
|
+
* root gets one of its own, so two roots of one user are two server-side
|
|
286
|
+
* families and one root's hand-off confirm cannot retire the other's session.
|
|
287
|
+
*
|
|
288
|
+
* Mints nothing and reads nothing stored: callers persist it (`cli-identity.ts`)
|
|
289
|
+
* or present it only when nothing is stored (the daemon broker).
|
|
290
|
+
*/
|
|
291
|
+
function machineCliId() {
|
|
292
|
+
return (0, device_id_1.machineCliIdForRoot)(configRoot(), defaultConfigRoot());
|
|
293
|
+
}
|
|
272
294
|
/**
|
|
273
295
|
* Fields that belong to the MACHINE, not to a profile.
|
|
274
296
|
*
|
|
@@ -472,6 +494,13 @@ function loadConfig() {
|
|
|
472
494
|
if (!fs.existsSync(carriedPath))
|
|
473
495
|
continue;
|
|
474
496
|
const carried = migrateLegacyBaseURL(readJsonOrThrow(carriedPath));
|
|
497
|
+
// The ancestor lives under $HOME whatever root this is, so its `cliId`
|
|
498
|
+
// belongs to the PRIMARY root's sessions. A relocated root that adopted
|
|
499
|
+
// it would present the primary root's identity, and a hand-off confirm
|
|
500
|
+
// there retires the primary root's families (OSK-12179). It mints its own
|
|
501
|
+
// at first login instead (`machineCliId`).
|
|
502
|
+
if (!isDefaultConfigRoot())
|
|
503
|
+
delete carried.cliId;
|
|
475
504
|
try {
|
|
476
505
|
saveConfig(carried);
|
|
477
506
|
}
|
|
@@ -64,6 +64,33 @@ export declare function resolveCredential(opts?: ResolveOptions): Promise<Resolv
|
|
|
64
64
|
* a token (e.g., just post-login) don't need to re-resolve.
|
|
65
65
|
*/
|
|
66
66
|
export declare function kindOf(token: string | null | undefined): TokenKind;
|
|
67
|
+
/**
|
|
68
|
+
* Where an in-memory credential ACTUALLY came from, when the shared
|
|
69
|
+
* `CredentialSource` union has no slot for it.
|
|
70
|
+
*
|
|
71
|
+
* Two CLI paths hold a token that was never read from `OVERSKY_TOKEN` yet
|
|
72
|
+
* are recorded as `env-token` — the union's only "a token this process holds
|
|
73
|
+
* in memory" slot, and the one `ensureFreshCliCredential` correctly skips: a
|
|
74
|
+
* daemon hand-off redeem (renewed by re-redeeming its descriptor) and a
|
|
75
|
+
* daemon-launched Agent session (`OVERSKY_SESSION_TOKEN`). Labelling either
|
|
76
|
+
* by the slot alone printed `Token source: OVERSKY_TOKEN` for a variable
|
|
77
|
+
* that was not set, which sends a debugging user to the wrong place
|
|
78
|
+
* (OSK-12185). The provenance travels BESIDE the credential so the slot keeps
|
|
79
|
+
* its refresh semantics and the label tells the truth.
|
|
80
|
+
*/
|
|
81
|
+
export type CredentialProvenance = {
|
|
82
|
+
kind: 'daemon-handoff';
|
|
83
|
+
descriptorPath?: string;
|
|
84
|
+
} | {
|
|
85
|
+
kind: 'daemon-delegated';
|
|
86
|
+
} | {
|
|
87
|
+
kind: 'agent-session';
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* The label `whoami`, `context` and `doctor` print — text and `--json` read
|
|
91
|
+
* the same value. A recorded provenance wins over the source slot.
|
|
92
|
+
*/
|
|
93
|
+
export declare function describeCredentialSource(source: CredentialSource, provenance?: CredentialProvenance | null): string;
|
|
67
94
|
/**
|
|
68
95
|
* Human-readable label for a `CredentialSource`. Used by `skrr whoami`
|
|
69
96
|
* and (Phase H) `skrr doctor`.
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.resolveTrustedAutonomousCredential = resolveTrustedAutonomousCredential;
|
|
4
4
|
exports.resolveCredential = resolveCredential;
|
|
5
5
|
exports.kindOf = kindOf;
|
|
6
|
+
exports.describeCredentialSource = describeCredentialSource;
|
|
6
7
|
exports.formatCredentialSource = formatCredentialSource;
|
|
7
8
|
/**
|
|
8
9
|
* credential-resolver.ts — Phase H.1 precedence for the Sky CLI.
|
|
@@ -118,6 +119,24 @@ async function resolveCredential(opts = {}) {
|
|
|
118
119
|
function kindOf(token) {
|
|
119
120
|
return (0, auth_core_1.classifyTokenKind)(token);
|
|
120
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* The label `whoami`, `context` and `doctor` print — text and `--json` read
|
|
124
|
+
* the same value. A recorded provenance wins over the source slot.
|
|
125
|
+
*/
|
|
126
|
+
function describeCredentialSource(source, provenance) {
|
|
127
|
+
if (provenance?.kind === 'daemon-handoff') {
|
|
128
|
+
return provenance.descriptorPath
|
|
129
|
+
? `daemon hand-off (${provenance.descriptorPath})`
|
|
130
|
+
: 'daemon hand-off';
|
|
131
|
+
}
|
|
132
|
+
if (provenance?.kind === 'daemon-delegated') {
|
|
133
|
+
return 'daemon delegated credential (OVERSKY_DELEGATED_CLI_BROKER_URL)';
|
|
134
|
+
}
|
|
135
|
+
if (provenance?.kind === 'agent-session') {
|
|
136
|
+
return 'agent session (OVERSKY_SESSION_TOKEN)';
|
|
137
|
+
}
|
|
138
|
+
return formatCredentialSource(source);
|
|
139
|
+
}
|
|
121
140
|
/**
|
|
122
141
|
* Human-readable label for a `CredentialSource`. Used by `skrr whoami`
|
|
123
142
|
* and (Phase H) `skrr doctor`.
|
|
@@ -115,6 +115,13 @@ export interface DaemonBrokeredOutcome {
|
|
|
115
115
|
/** Server the minted credential belongs to (daemon-reported, else the
|
|
116
116
|
* descriptor's own `serverUrl`). */
|
|
117
117
|
serverUrl?: string;
|
|
118
|
+
/**
|
|
119
|
+
* The descriptor file the token was redeemed from. The token lives only in
|
|
120
|
+
* this process's memory, so this path is the only honest answer to "where
|
|
121
|
+
* did this credential come from" — `skrr whoami` prints it as the token
|
|
122
|
+
* source (OSK-12185).
|
|
123
|
+
*/
|
|
124
|
+
descriptorPath?: string;
|
|
118
125
|
}
|
|
119
126
|
/**
|
|
120
127
|
* Explicit reasons the broker path could fail. These exist to keep the
|
package/dist/lib/daemonBroker.js
CHANGED
|
@@ -463,6 +463,7 @@ async function brokerThrough(bootstrap, opts, sourcePath) {
|
|
|
463
463
|
...((response.serverUrl ?? bootstrap.serverUrl)
|
|
464
464
|
? { serverUrl: response.serverUrl ?? bootstrap.serverUrl }
|
|
465
465
|
: {}),
|
|
466
|
+
...(sourcePath ? { descriptorPath: sourcePath } : {}),
|
|
466
467
|
};
|
|
467
468
|
}
|
|
468
469
|
// The descriptor ALSO advertises the durable mint (a laptop island during
|
package/dist/lib/device-id.d.ts
CHANGED
|
@@ -12,3 +12,26 @@
|
|
|
12
12
|
* 128-char + printable-ASCII guard.
|
|
13
13
|
*/
|
|
14
14
|
export declare function generateCliId(): string;
|
|
15
|
+
/**
|
|
16
|
+
* The machine cliId for ONE config root (OSK-12179).
|
|
17
|
+
*
|
|
18
|
+
* `generateCliId()` hashes only (host, user), so every root on the machine got
|
|
19
|
+
* the same id: a fresh `SKRR_CONFIG_DIR` root's default profile minted exactly
|
|
20
|
+
* the id the real `~/.skrr` root had been presenting. The server's
|
|
21
|
+
* `cli-handoff/confirm` retires every OTHER live family of (user, cliId), so a
|
|
22
|
+
* sign-in in the second root could end the first root's session. The Keychain
|
|
23
|
+
* slots were already separated by root (OSK-12135), and a named profile's own
|
|
24
|
+
* id already hashes its root (`profileScopedCliId`); this was the one identity
|
|
25
|
+
* that did not.
|
|
26
|
+
*
|
|
27
|
+
* The DEFAULT root keeps `generateCliId()` byte for byte. Every install already
|
|
28
|
+
* stores or re-derives that value, every live session is bound to it, and a
|
|
29
|
+
* refresh presenting another id is refused — so changing it would sign the
|
|
30
|
+
* primary root out. Only a root somewhere else gets its own id, deterministic in
|
|
31
|
+
* (host, user, root) so a root that loses its config file and signs in again
|
|
32
|
+
* presents the same id rather than scattering families.
|
|
33
|
+
*
|
|
34
|
+
* A root is the default one when it resolves to the default path, however it
|
|
35
|
+
* was reached: `SKRR_CONFIG_DIR=~/.skrr` is the primary root, not a second one.
|
|
36
|
+
*/
|
|
37
|
+
export declare function machineCliIdForRoot(root: string, defaultRoot: string): string;
|
package/dist/lib/device-id.js
CHANGED
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.generateCliId = generateCliId;
|
|
40
|
+
exports.machineCliIdForRoot = machineCliIdForRoot;
|
|
7
41
|
const node_crypto_1 = require("node:crypto");
|
|
8
42
|
const node_os_1 = __importDefault(require("node:os"));
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
9
44
|
/**
|
|
10
45
|
* Derive a stable per-machine identifier the server will treat as a
|
|
11
46
|
* "device" for the device-code flow. Mirrors the daemon's `generateDaemonId`
|
|
@@ -25,3 +60,36 @@ function generateCliId() {
|
|
|
25
60
|
const digest = (0, node_crypto_1.createHash)('sha256').update(`${hostname}|${username}|cli`).digest('hex');
|
|
26
61
|
return `cli_${digest.slice(0, 12)}`;
|
|
27
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The machine cliId for ONE config root (OSK-12179).
|
|
65
|
+
*
|
|
66
|
+
* `generateCliId()` hashes only (host, user), so every root on the machine got
|
|
67
|
+
* the same id: a fresh `SKRR_CONFIG_DIR` root's default profile minted exactly
|
|
68
|
+
* the id the real `~/.skrr` root had been presenting. The server's
|
|
69
|
+
* `cli-handoff/confirm` retires every OTHER live family of (user, cliId), so a
|
|
70
|
+
* sign-in in the second root could end the first root's session. The Keychain
|
|
71
|
+
* slots were already separated by root (OSK-12135), and a named profile's own
|
|
72
|
+
* id already hashes its root (`profileScopedCliId`); this was the one identity
|
|
73
|
+
* that did not.
|
|
74
|
+
*
|
|
75
|
+
* The DEFAULT root keeps `generateCliId()` byte for byte. Every install already
|
|
76
|
+
* stores or re-derives that value, every live session is bound to it, and a
|
|
77
|
+
* refresh presenting another id is refused — so changing it would sign the
|
|
78
|
+
* primary root out. Only a root somewhere else gets its own id, deterministic in
|
|
79
|
+
* (host, user, root) so a root that loses its config file and signs in again
|
|
80
|
+
* presents the same id rather than scattering families.
|
|
81
|
+
*
|
|
82
|
+
* A root is the default one when it resolves to the default path, however it
|
|
83
|
+
* was reached: `SKRR_CONFIG_DIR=~/.skrr` is the primary root, not a second one.
|
|
84
|
+
*/
|
|
85
|
+
function machineCliIdForRoot(root, defaultRoot) {
|
|
86
|
+
const resolved = path.resolve(root);
|
|
87
|
+
if (resolved === path.resolve(defaultRoot))
|
|
88
|
+
return generateCliId();
|
|
89
|
+
const hostname = node_os_1.default.hostname();
|
|
90
|
+
const username = node_os_1.default.userInfo().username || 'unknown';
|
|
91
|
+
const digest = (0, node_crypto_1.createHash)('sha256')
|
|
92
|
+
.update(`${hostname}|${username}|cli|root:${resolved}`)
|
|
93
|
+
.digest('hex');
|
|
94
|
+
return `cli_${digest.slice(0, 12)}`;
|
|
95
|
+
}
|
|
@@ -75,7 +75,6 @@ const auth_core_init_1 = require("./auth-core-init");
|
|
|
75
75
|
const config_1 = require("./config");
|
|
76
76
|
const cli_id_1 = require("./cli-id");
|
|
77
77
|
const cred_envelope_1 = require("./cred-envelope");
|
|
78
|
-
const device_id_1 = require("./device-id");
|
|
79
78
|
const first_party_harness_1 = require("./first-party-harness");
|
|
80
79
|
const first_party_harness_agent_1 = require("./first-party-harness-agent");
|
|
81
80
|
const first_party_harness_managed_1 = require("./first-party-harness-managed");
|
|
@@ -862,8 +861,9 @@ function hasLocalCredential() {
|
|
|
862
861
|
*
|
|
863
862
|
* The id login and refresh present, read through `resolveCliId` (a named
|
|
864
863
|
* profile may have its own, OSK-12156); regenerated deterministically as the
|
|
865
|
-
* machine id when nothing is stored (same
|
|
866
|
-
*
|
|
864
|
+
* machine id of THIS config root when nothing is stored (the same host, user
|
|
865
|
+
* and root always yield the same value; a relocated root does not share the
|
|
866
|
+
* primary root's id, OSK-12179). Normalized to the `cli_` namespace the server requires,
|
|
867
867
|
* because `OVERSKY_CLI_ID` is overlaid verbatim with no format check and a
|
|
868
868
|
* hand-set value would otherwise come back as an opaque
|
|
869
869
|
* `runtime-id-namespace-mismatch`.
|
|
@@ -879,7 +879,7 @@ function localRuntimeId() {
|
|
|
879
879
|
})();
|
|
880
880
|
if (configured?.startsWith('cli_'))
|
|
881
881
|
return configured;
|
|
882
|
-
return (0,
|
|
882
|
+
return (0, config_1.machineCliId)();
|
|
883
883
|
}
|
|
884
884
|
function errorCodeOf(err) {
|
|
885
885
|
const code = err?.code;
|