@skrr-ai/cli 0.1.81 → 0.1.83
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 +16 -0
- package/dist/base-command.js +124 -9
- package/dist/commands/followups/checkin.js +1 -1
- package/dist/commands/login.js +10 -0
- package/dist/commands/logout.js +4 -0
- package/dist/commands/store/installs.js +6 -1
- package/dist/lib/auth-storage.d.ts +5 -0
- package/dist/lib/auth-storage.js +8 -0
- package/dist/lib/config.d.ts +8 -0
- package/dist/lib/credential-resolver.js +7 -1
- package/dist/lib/daemonBroker.d.ts +35 -0
- package/dist/lib/daemonBroker.js +191 -11
- package/dist/lib/daemonBrokerRefusal.d.ts +14 -0
- package/dist/lib/daemonBrokerRefusal.js +18 -1
- package/dist/lib/first-party-harness-broker.d.ts +2 -0
- package/dist/lib/first-party-harness-broker.js +77 -3
- package/dist/lib/harness-context.d.ts +8 -0
- package/dist/lib/harness-context.js +55 -0
- package/dist/lib/login.js +4 -1
- package/dist/lib/refresh.js +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +25 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +35 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +25 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +33 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3856 -3801
- package/oclif.manifest.json +19574 -19574
- package/package.json +2 -2
package/dist/lib/daemonBroker.js
CHANGED
|
@@ -70,6 +70,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
70
70
|
return result;
|
|
71
71
|
};
|
|
72
72
|
})();
|
|
73
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
74
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
75
|
+
};
|
|
73
76
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
74
77
|
exports.HOSTED_MACHINE_CLI_HANDOFF_DESCRIPTOR = exports.DEDICATED_RUNTIME_CLI_HANDOFF_DESCRIPTOR = void 0;
|
|
75
78
|
exports.normalizeBaseUrl = normalizeBaseUrl;
|
|
@@ -89,16 +92,19 @@ exports.findBrokeredHandoffDescriptor = findBrokeredHandoffDescriptor;
|
|
|
89
92
|
exports.maybeRetireStoredCliAuthFile = maybeRetireStoredCliAuthFile;
|
|
90
93
|
exports.shouldBrokerBeforeStoredCredential = shouldBrokerBeforeStoredCredential;
|
|
91
94
|
exports.attemptDaemonBrokerLoginAndPersist = attemptDaemonBrokerLoginAndPersist;
|
|
95
|
+
exports.cliSessionAdoptionBlocker = cliSessionAdoptionBlocker;
|
|
92
96
|
exports.maybeAutoBroker = maybeAutoBroker;
|
|
93
97
|
const fs = __importStar(require("node:fs"));
|
|
94
98
|
const path = __importStar(require("node:path"));
|
|
95
99
|
const os = __importStar(require("node:os"));
|
|
100
|
+
const proper_lockfile_1 = __importDefault(require("proper-lockfile"));
|
|
96
101
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
97
102
|
const loopback_http_1 = require("@skrr-ai/auth-core/loopback-http");
|
|
98
103
|
const cli_handoff_wire_1 = require("@skrr-ai/auth-core/cli-handoff-wire");
|
|
99
104
|
const brokered_dpop_1 = require("./brokered-dpop");
|
|
100
105
|
const auth_storage_1 = require("./auth-storage");
|
|
101
106
|
const config_1 = require("./config");
|
|
107
|
+
const harness_context_1 = require("./harness-context");
|
|
102
108
|
const cli_identity_1 = require("./cli-identity");
|
|
103
109
|
const daemonRestartWait_1 = require("./daemonRestartWait");
|
|
104
110
|
const keychain_1 = require("./keychain");
|
|
@@ -368,6 +374,10 @@ async function attemptDaemonBrokerLogin(opts) {
|
|
|
368
374
|
const first = await attemptDaemonBrokerLoginOnce(opts);
|
|
369
375
|
if (first.ok || !isRestartShapedFailure(first))
|
|
370
376
|
return first;
|
|
377
|
+
// The guest descriptor is owned by the runtime supervisor, not this CLI's
|
|
378
|
+
// local daemon service. Waiting on a laptop launchd restart cannot repair it.
|
|
379
|
+
if (opts.bootstrapPathOverride === exports.DEDICATED_RUNTIME_CLI_HANDOFF_DESCRIPTOR)
|
|
380
|
+
return first;
|
|
371
381
|
// The local service may be restarting: a graceful restart removes the
|
|
372
382
|
// descriptor and republishes it only once the new daemon is listening
|
|
373
383
|
// (measured ~14s), and a crash leaves it naming a dead pid until launchd
|
|
@@ -562,8 +572,19 @@ async function brokerThrough(bootstrap, opts, sourcePath) {
|
|
|
562
572
|
// would either break explicit login or be bypassed by anyone who calls it
|
|
563
573
|
// the way login does. It is not a boundary, so it is not attempted. What the
|
|
564
574
|
// CLI CAN guarantee is that its own automatic path never mints and persists
|
|
565
|
-
// a refresh family: when `access_token` is advertised,
|
|
566
|
-
// the answer, and the daemon's outage is reported with its
|
|
575
|
+
// a refresh family on a borrower's behalf: when `access_token` is advertised,
|
|
576
|
+
// a failed redeem is the answer, and the daemon's outage is reported with its
|
|
577
|
+
// repair.
|
|
578
|
+
//
|
|
579
|
+
// OSK-13254 narrowed "the automatic path" to mean a BORROWER. A person's
|
|
580
|
+
// first `skrr` on a profile with nothing stored now ADOPTS a CLI session of
|
|
581
|
+
// its own (`maybeAutoBroker` with `adopt`, `preferHandoffMode:
|
|
582
|
+
// 'refresh_family'`), because a sign-in that lived only as long as the daemon
|
|
583
|
+
// read as "signed out" every time the daemon restarted. What OSK-12018 was
|
|
584
|
+
// guarding against — an agent-originated implicit sign-in persisting a family
|
|
585
|
+
// — is now refused by name: a process the daemon started for an agent or tool
|
|
586
|
+
// carries `SKRR_DAEMON_CHILD` / `OVERSKY_AUTONOMOUS_SESSION`
|
|
587
|
+
// (`harness-context.ts`), sets `borrowOnly`, and `writeToBackend` refuses it.
|
|
567
588
|
//
|
|
568
589
|
// COMPATIBILITY WINDOW: a v1 descriptor (no `handoffModes`, so no
|
|
569
590
|
// `access_token`) still takes the durable mint on the automatic path below,
|
|
@@ -578,7 +599,17 @@ async function brokerThrough(bootstrap, opts, sourcePath) {
|
|
|
578
599
|
const offersAccessToken = (0, cli_handoff_wire_1.descriptorOffersAccessToken)(bootstrap);
|
|
579
600
|
const offersRefreshFamily = modes.includes('refresh_family');
|
|
580
601
|
const preferAccessToken = opts.preferHandoffMode === 'access_token';
|
|
581
|
-
if (
|
|
602
|
+
if (opts.borrowOnly && !offersAccessToken) {
|
|
603
|
+
return {
|
|
604
|
+
ok: false,
|
|
605
|
+
reason: 'unknown',
|
|
606
|
+
refusedBy: 'daemon',
|
|
607
|
+
code: 'HANDOFF_BORROW_UNAVAILABLE',
|
|
608
|
+
detail: 'The skrr background service on this computer is too old to lend this agent process a ' +
|
|
609
|
+
'short-lived token, and a process it started for an agent never stores a sign-in. Update skrrd.',
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (offersAccessToken && (preferAccessToken || opts.borrowOnly || !offersRefreshFamily)) {
|
|
582
613
|
const redeem = await redeemCliHandoffToken(bootstrap, {
|
|
583
614
|
forceRefresh: false,
|
|
584
615
|
timeoutMs: opts.timeoutMs,
|
|
@@ -1150,6 +1181,16 @@ async function attemptDaemonBrokerLoginAndPersist(opts) {
|
|
|
1150
1181
|
// Callers use the token in-memory for this process only.
|
|
1151
1182
|
if (outcome.brokered)
|
|
1152
1183
|
return outcome;
|
|
1184
|
+
// Unreachable through `brokerThrough`, which never mints for a borrower;
|
|
1185
|
+
// kept so a future route cannot persist on a borrower's behalf.
|
|
1186
|
+
if (opts.borrowOnly) {
|
|
1187
|
+
return {
|
|
1188
|
+
ok: false,
|
|
1189
|
+
reason: 'unknown',
|
|
1190
|
+
code: 'HANDOFF_BORROW_UNAVAILABLE',
|
|
1191
|
+
detail: 'A borrower was handed a durable credential; it was not stored.',
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1153
1194
|
const serverOrigin = normalizeBaseUrl(opts.baseURL);
|
|
1154
1195
|
if (!serverOrigin) {
|
|
1155
1196
|
throw new Error('Daemon-broker credential persistence requires a valid CLI baseURL.');
|
|
@@ -1199,6 +1240,123 @@ const AUTH_SELF_MANAGED_COMMANDS = new Set([
|
|
|
1199
1240
|
'pair',
|
|
1200
1241
|
'generate-recovery-code',
|
|
1201
1242
|
]);
|
|
1243
|
+
/**
|
|
1244
|
+
* Why this invocation must borrow instead of adopting a CLI session of its
|
|
1245
|
+
* own, or null when it may adopt (OSK-13254).
|
|
1246
|
+
*/
|
|
1247
|
+
function cliSessionAdoptionBlocker(cliConfig, env = process.env) {
|
|
1248
|
+
if ((0, harness_context_1.isHarnessCliContext)(env))
|
|
1249
|
+
return 'harness_context';
|
|
1250
|
+
if (env.SKRR_NO_CLI_SESSION_ADOPTION === '1')
|
|
1251
|
+
return 'opted_out';
|
|
1252
|
+
// `skrr logout` said "this CLI is signed out". The daemon may still LEND
|
|
1253
|
+
// tokens (logout says so), but a durable session does not reappear behind
|
|
1254
|
+
// that statement; the next `skrr login` clears the flag.
|
|
1255
|
+
if (cliConfig.adoptDaemonSession === false)
|
|
1256
|
+
return 'signed_out';
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Failures that mean no daemon answered (or answered for another server) —
|
|
1261
|
+
* a second attempt on the token route cannot do better and would only wait
|
|
1262
|
+
* out the restart window twice.
|
|
1263
|
+
*/
|
|
1264
|
+
function daemonDidNotAnswer(failure) {
|
|
1265
|
+
switch (failure.reason) {
|
|
1266
|
+
case 'no_bootstrap':
|
|
1267
|
+
case 'stale_bootstrap':
|
|
1268
|
+
case 'bootstrap_parse':
|
|
1269
|
+
case 'base_url_mismatch':
|
|
1270
|
+
case 'network':
|
|
1271
|
+
case 'timeout':
|
|
1272
|
+
return true;
|
|
1273
|
+
default:
|
|
1274
|
+
return false;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
/** The adoption lock: one per config root and profile. */
|
|
1278
|
+
function adoptionLockPath(profile) {
|
|
1279
|
+
const safe = profile.replace(/[^A-Za-z0-9._-]/g, '_') || 'default';
|
|
1280
|
+
return path.join((0, config_1.configRoot)(), `cli-session-adopt.${safe}.lock`);
|
|
1281
|
+
}
|
|
1282
|
+
function storedAccessToken(baseURL) {
|
|
1283
|
+
try {
|
|
1284
|
+
return (0, auth_storage_1.readFromBackend)(baseURL).bundle?.token ?? null;
|
|
1285
|
+
}
|
|
1286
|
+
catch {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Adopt a CLI session through the local daemon (OSK-13254): the durable mint
|
|
1292
|
+
* (`/v1/auth/cli-handoff` → `POST /api/daemons/cli-handoff`), persisted like a
|
|
1293
|
+
* browser login, so that the next `skrr` reads it from the Keychain/file and
|
|
1294
|
+
* renews it itself — the daemon restarting no longer reads as signed out.
|
|
1295
|
+
*
|
|
1296
|
+
* Serialized across processes. The confirm that follows a mint retires every
|
|
1297
|
+
* OTHER live family of this cliId, so two concurrent adopters would each
|
|
1298
|
+
* revoke the other's session mid-command. Under the lock a process that
|
|
1299
|
+
* finds a session a sibling stored while it waited uses that one instead.
|
|
1300
|
+
*
|
|
1301
|
+
* Never worse than borrowing: if the lock cannot be taken in time, or the
|
|
1302
|
+
* daemon answered but would not mint (rate limit, a server refusal, a
|
|
1303
|
+
* broker-only descriptor), this borrows an access token for this process
|
|
1304
|
+
* exactly as before.
|
|
1305
|
+
*/
|
|
1306
|
+
async function adoptCliSessionThroughDaemon(base, lockRetries) {
|
|
1307
|
+
const profile = base.profile ?? 'default';
|
|
1308
|
+
const baseURL = base.baseURL ?? '';
|
|
1309
|
+
const borrow = () => attemptDaemonBrokerLoginAndPersist({
|
|
1310
|
+
...base,
|
|
1311
|
+
preferHandoffMode: 'access_token',
|
|
1312
|
+
borrowOnly: true,
|
|
1313
|
+
});
|
|
1314
|
+
// Adopt only from a live descriptor that can mint a family. One that
|
|
1315
|
+
// cannot (the Dedicated guest's access-token-only shape), or none at all (a
|
|
1316
|
+
// daemon that is down or restarting), has nothing to adopt: borrow without
|
|
1317
|
+
// taking the lock, so concurrent commands never queue behind it and the
|
|
1318
|
+
// borrow path's own restart wait and outage reporting apply unchanged.
|
|
1319
|
+
const usable = listUsableBootstraps(base);
|
|
1320
|
+
if (!usable.some(({ bootstrap }) => (0, cli_handoff_wire_1.advertisedHandoffModes)(bootstrap).includes('refresh_family'))) {
|
|
1321
|
+
return { outcome: await borrow() };
|
|
1322
|
+
}
|
|
1323
|
+
const before = storedAccessToken(baseURL);
|
|
1324
|
+
const lockPath = adoptionLockPath(profile);
|
|
1325
|
+
let release = null;
|
|
1326
|
+
try {
|
|
1327
|
+
fs.mkdirSync(path.dirname(lockPath), { recursive: true, mode: 0o700 });
|
|
1328
|
+
if (!fs.existsSync(lockPath))
|
|
1329
|
+
fs.writeFileSync(lockPath, '', { mode: 0o600 });
|
|
1330
|
+
release = await proper_lockfile_1.default.lock(lockPath, {
|
|
1331
|
+
// Longer than one mint (20s loopback + 10s confirm), so a holder that
|
|
1332
|
+
// is still working is never judged stale.
|
|
1333
|
+
stale: 60_000,
|
|
1334
|
+
retries: { retries: lockRetries, minTimeout: 250, maxTimeout: 1_000, factor: 1.5 },
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
catch {
|
|
1338
|
+
// Could not serialize: do not race a sibling's mint. Borrow.
|
|
1339
|
+
return { outcome: await borrow() };
|
|
1340
|
+
}
|
|
1341
|
+
let durable;
|
|
1342
|
+
try {
|
|
1343
|
+
const now = storedAccessToken(baseURL);
|
|
1344
|
+
if (now && now !== before)
|
|
1345
|
+
return { adoptedBySibling: true };
|
|
1346
|
+
durable = await attemptDaemonBrokerLoginAndPersist({
|
|
1347
|
+
...base,
|
|
1348
|
+
preferHandoffMode: 'refresh_family',
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
finally {
|
|
1352
|
+
await release().catch(() => undefined);
|
|
1353
|
+
}
|
|
1354
|
+
if (durable.ok || daemonDidNotAnswer(durable))
|
|
1355
|
+
return { outcome: durable };
|
|
1356
|
+
// The daemon answered but would not mint a family. The token route is a
|
|
1357
|
+
// different door with different limits; take it for this process.
|
|
1358
|
+
return { outcome: await borrow() };
|
|
1359
|
+
}
|
|
1202
1360
|
/**
|
|
1203
1361
|
* Auto-broker pre-flight for `BaseCommand.init()`. When the credential
|
|
1204
1362
|
* resolver returns no token, this attempts a silent daemon broker so
|
|
@@ -1244,21 +1402,43 @@ async function maybeAutoBroker(opts) {
|
|
|
1244
1402
|
// confirm cannot retire another profile's session. Persisted by the caller on
|
|
1245
1403
|
// success so the rotation row stays bound across processes.
|
|
1246
1404
|
const { cliId, updatedConfig } = (0, cli_identity_1.planLoginCliId)(opts.cliConfig);
|
|
1247
|
-
const
|
|
1405
|
+
const base = {
|
|
1248
1406
|
cliId,
|
|
1249
1407
|
baseURL: opts.cliConfig.baseURL,
|
|
1250
|
-
// Auto-broker prefers the brokered access-token mode when the descriptor
|
|
1251
|
-
// advertises it: nothing credential-shaped is persisted, and the next
|
|
1252
|
-
// `skrr` process simply re-redeems over loopback. The legacy refresh-
|
|
1253
|
-
// family mint remains the fallback for v1 descriptors and the redeem
|
|
1254
|
-
// path for explicit `skrr login`.
|
|
1255
|
-
preferHandoffMode: 'access_token',
|
|
1256
1408
|
profile: opts.profile ?? (0, keychain_1.getActiveProfile)(),
|
|
1257
1409
|
...(opts.bootstrapPathOverride ? { bootstrapPathOverride: opts.bootstrapPathOverride } : {}),
|
|
1410
|
+
};
|
|
1411
|
+
const harness = (0, harness_context_1.isHarnessCliContext)();
|
|
1412
|
+
// OSK-13254 — a person's first `skrr` on a machine whose daemon is signed in
|
|
1413
|
+
// ADOPTS a CLI session of its own: a separate cli-scope family, minted by the
|
|
1414
|
+
// daemon's hand-off and stored like a browser login, so later commands do
|
|
1415
|
+
// not depend on the daemon being up. Everything else borrows a short-lived
|
|
1416
|
+
// token for this process, which is what the automatic path always did
|
|
1417
|
+
// (OSK-12018): a process the daemon started for an agent or tool, a 401
|
|
1418
|
+
// recovery, a profile signed out with `skrr logout`, and the opt-out.
|
|
1419
|
+
if (opts.adopt && !cliSessionAdoptionBlocker(opts.cliConfig)) {
|
|
1420
|
+
const adopted = await adoptCliSessionThroughDaemon(base, opts.adoptionLockRetries ?? 40);
|
|
1421
|
+
if (adopted.adoptedBySibling)
|
|
1422
|
+
return { triggered: true, adoptedBySibling: true };
|
|
1423
|
+
const outcome = adopted.outcome;
|
|
1424
|
+
return {
|
|
1425
|
+
triggered: true,
|
|
1426
|
+
outcome,
|
|
1427
|
+
...(updatedConfig && outcome.ok ? { updatedConfig } : {}),
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
const outcome = await attemptDaemonBrokerLoginAndPersist({
|
|
1431
|
+
...base,
|
|
1432
|
+
// Borrow: the access-token mode, held in memory for this process; the next
|
|
1433
|
+
// `skrr` re-redeems over loopback. A v1 descriptor still takes the durable
|
|
1434
|
+
// mint here (the compatibility window in `brokerThrough`), except for a
|
|
1435
|
+
// harness-context process, which never stores a sign-in.
|
|
1436
|
+
preferHandoffMode: 'access_token',
|
|
1437
|
+
...(harness ? { borrowOnly: true } : {}),
|
|
1258
1438
|
});
|
|
1259
1439
|
return {
|
|
1260
1440
|
triggered: true,
|
|
1261
1441
|
outcome,
|
|
1262
|
-
...(updatedConfig && outcome.ok ? { updatedConfig } : {}),
|
|
1442
|
+
...(updatedConfig && outcome.ok && !harness ? { updatedConfig } : {}),
|
|
1263
1443
|
};
|
|
1264
1444
|
}
|
|
@@ -96,3 +96,17 @@ export declare function describeGuestDaemonUnavailable(failure: DaemonBrokerFail
|
|
|
96
96
|
* nothing waited for): "Not signed in" is then the true sentence.
|
|
97
97
|
*/
|
|
98
98
|
export declare function describeLocalDaemonUnavailable(failure: DaemonBrokerFailure, bin?: string): BrokerRefusalDescription | null;
|
|
99
|
+
/**
|
|
100
|
+
* The one description of a laptop or guest broker failure for every surface
|
|
101
|
+
* that has no separate guest probe of its own: a daemon that answered and
|
|
102
|
+
* refused (`describeBrokerRefusal`), else a daemon whose descriptor is on disk
|
|
103
|
+
* but which did not answer — down, crashed or mid-restart
|
|
104
|
+
* (`describeLocalDaemonUnavailable`).
|
|
105
|
+
*
|
|
106
|
+
* `requireAuth` composed these two by hand; `skrr login`'s progress line and
|
|
107
|
+
* headless refusal, and the mid-command 401 paths, consulted only the first,
|
|
108
|
+
* so a daemon that was restarting read there as "no daemon at all" — the
|
|
109
|
+
* generic browser refusal, or a bare "Run `skrr login`" (OSK-13099). Returns
|
|
110
|
+
* null only when there is genuinely nothing to say about a daemon.
|
|
111
|
+
*/
|
|
112
|
+
export declare function describeBrokerFailure(failure: DaemonBrokerFailure, bin?: string): BrokerRefusalDescription | null;
|
|
@@ -4,6 +4,7 @@ exports.isAnsweredBrokerRefusal = isAnsweredBrokerRefusal;
|
|
|
4
4
|
exports.describeBrokerRefusal = describeBrokerRefusal;
|
|
5
5
|
exports.describeGuestDaemonUnavailable = describeGuestDaemonUnavailable;
|
|
6
6
|
exports.describeLocalDaemonUnavailable = describeLocalDaemonUnavailable;
|
|
7
|
+
exports.describeBrokerFailure = describeBrokerFailure;
|
|
7
8
|
/**
|
|
8
9
|
* True when a daemon ANSWERED the hand-off and refused it — as opposed to no
|
|
9
10
|
* daemon being there to ask (`no_bootstrap`, a stale descriptor nobody listens
|
|
@@ -261,7 +262,7 @@ function describeLocalDaemonUnavailable(failure, bin = 'skrr') {
|
|
|
261
262
|
if (!headline)
|
|
262
263
|
return null;
|
|
263
264
|
const remedy = 'You are not signed out: your sign-in is held by that service, which normally restarts on its own. ' +
|
|
264
|
-
`Retry in a
|
|
265
|
+
`Retry in a few seconds, or start it with \`${bin} daemon start\` if it stays down. \`${bin} daemon status\` and \`${bin} daemon logs\` show the service's side, ` +
|
|
265
266
|
`and \`${bin} login\` signs this CLI in without it.`;
|
|
266
267
|
return {
|
|
267
268
|
headline: `${headline}${waited}`,
|
|
@@ -289,3 +290,19 @@ function describeLocalDaemonUnavailable(failure, bin = 'skrr') {
|
|
|
289
290
|
},
|
|
290
291
|
};
|
|
291
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* The one description of a laptop or guest broker failure for every surface
|
|
295
|
+
* that has no separate guest probe of its own: a daemon that answered and
|
|
296
|
+
* refused (`describeBrokerRefusal`), else a daemon whose descriptor is on disk
|
|
297
|
+
* but which did not answer — down, crashed or mid-restart
|
|
298
|
+
* (`describeLocalDaemonUnavailable`).
|
|
299
|
+
*
|
|
300
|
+
* `requireAuth` composed these two by hand; `skrr login`'s progress line and
|
|
301
|
+
* headless refusal, and the mid-command 401 paths, consulted only the first,
|
|
302
|
+
* so a daemon that was restarting read there as "no daemon at all" — the
|
|
303
|
+
* generic browser refusal, or a bare "Run `skrr login`" (OSK-13099). Returns
|
|
304
|
+
* null only when there is genuinely nothing to say about a daemon.
|
|
305
|
+
*/
|
|
306
|
+
function describeBrokerFailure(failure, bin = 'skrr') {
|
|
307
|
+
return describeBrokerRefusal(failure, bin) ?? describeLocalDaemonUnavailable(failure, bin);
|
|
308
|
+
}
|
|
@@ -146,6 +146,8 @@ export interface AcquireManagedSessionInput {
|
|
|
146
146
|
export interface AcquireManagedSessionDeps {
|
|
147
147
|
prefetchAuth?: () => Promise<void>;
|
|
148
148
|
hasCredential?: () => boolean;
|
|
149
|
+
/** null means this is not a standalone Dedicated guest invocation. */
|
|
150
|
+
borrowGuestCredential?: () => Promise<boolean | null>;
|
|
149
151
|
/** `signal` is the acquisition-wide deadline; an override that ignores it is unbounded. */
|
|
150
152
|
resolveAgent?: (ref?: string, signal?: AbortSignal) => Promise<FirstPartyHarnessAgent>;
|
|
151
153
|
/** The user-JWT call. Defaults to `apiFetch`. */
|
|
@@ -75,6 +75,13 @@ 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 dedicated_guest_1 = require("./dedicated-guest");
|
|
79
|
+
const daemonBroker_1 = require("./daemonBroker");
|
|
80
|
+
const daemonBrokerRefusal_1 = require("./daemonBrokerRefusal");
|
|
81
|
+
const cli_identity_1 = require("./cli-identity");
|
|
82
|
+
const credential_resolver_1 = require("./credential-resolver");
|
|
83
|
+
const harness_context_1 = require("./harness-context");
|
|
84
|
+
const node_adapter_1 = require("./node-adapter");
|
|
78
85
|
const first_party_harness_1 = require("./first-party-harness");
|
|
79
86
|
const first_party_harness_agent_1 = require("./first-party-harness-agent");
|
|
80
87
|
const first_party_harness_managed_1 = require("./first-party-harness-managed");
|
|
@@ -162,6 +169,47 @@ class ManagedAcquisitionError extends Error {
|
|
|
162
169
|
}
|
|
163
170
|
}
|
|
164
171
|
exports.ManagedAcquisitionError = ManagedAcquisitionError;
|
|
172
|
+
async function borrowDedicatedGuestCredential() {
|
|
173
|
+
if (!(0, dedicated_guest_1.runningOnDedicatedRuntimeGuest)() ||
|
|
174
|
+
(0, harness_context_1.isHarnessCliContext)() ||
|
|
175
|
+
(0, credential_resolver_1.resolveTrustedAutonomousCredential)()) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
// `skrr code run` does not extend BaseCommand, so its local credential check
|
|
179
|
+
// used to stop before the guest's daemon hand-off could be redeemed. Borrow
|
|
180
|
+
// only the fixed root-owned guest descriptor. No refresh family or credential
|
|
181
|
+
// is persisted, and apiFetch can re-redeem this DPoP-bound token on a 401.
|
|
182
|
+
try {
|
|
183
|
+
const cfg = (0, config_1.loadConfig)();
|
|
184
|
+
const outcome = await (0, daemonBroker_1.attemptDaemonBrokerLogin)({
|
|
185
|
+
cliId: (0, cli_identity_1.planLoginCliId)(cfg).cliId,
|
|
186
|
+
baseURL: cfg.baseURL,
|
|
187
|
+
bootstrapPathOverride: daemonBroker_1.DEDICATED_RUNTIME_CLI_HANDOFF_DESCRIPTOR,
|
|
188
|
+
preferHandoffMode: 'access_token',
|
|
189
|
+
borrowOnly: true,
|
|
190
|
+
timeoutMs: 10_000,
|
|
191
|
+
});
|
|
192
|
+
if (!outcome.ok) {
|
|
193
|
+
const description = (0, daemonBrokerRefusal_1.describeBrokerFailure)(outcome);
|
|
194
|
+
throw new ManagedAcquisitionError(outcome.code ?? 'guest-handoff-unavailable', description
|
|
195
|
+
? `${description.headline} ${description.remedy}`
|
|
196
|
+
: `the Dedicated Runtime could not provide its managed skrr session (${outcome.reason})`);
|
|
197
|
+
}
|
|
198
|
+
if (!outcome.brokered)
|
|
199
|
+
return false;
|
|
200
|
+
(0, node_adapter_1.setAdapterCredentialOverride)({
|
|
201
|
+
token: outcome.accessToken,
|
|
202
|
+
source: 'env-token',
|
|
203
|
+
kind: (0, auth_core_1.classifyTokenKind)(outcome.accessToken),
|
|
204
|
+
});
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (error instanceof ManagedAcquisitionError)
|
|
209
|
+
throw error;
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
165
213
|
/**
|
|
166
214
|
* Acquire a managed session, or throw a typed `ManagedAcquisitionError`.
|
|
167
215
|
*
|
|
@@ -183,11 +231,28 @@ async function acquireManagedSession(input = {}, deps = {}) {
|
|
|
183
231
|
// Checked locally, BEFORE any network. A fresh machine that has never run
|
|
184
232
|
// `skrr login` must fall back to BYOK without a round trip and without a 401 that
|
|
185
233
|
// would drag `apiFetch`'s refresh machinery in behind it.
|
|
186
|
-
let localCredentialAvailable;
|
|
187
|
-
|
|
234
|
+
let localCredentialAvailable = false;
|
|
235
|
+
// Match BaseCommand's narrowly scoped retirement path: a stale guest refresh
|
|
236
|
+
// family must not prevent the live machine descriptor from lending a token.
|
|
237
|
+
const brokerBeforeStored = !deps.hasCredential &&
|
|
238
|
+
!(0, config_1.isEnvAuthOverride)() &&
|
|
239
|
+
!(0, harness_context_1.isHarnessCliContext)() &&
|
|
240
|
+
!(0, credential_resolver_1.resolveTrustedAutonomousCredential)() &&
|
|
241
|
+
(0, daemonBroker_1.shouldBrokerBeforeStoredCredential)();
|
|
242
|
+
if (brokerBeforeStored) {
|
|
243
|
+
try {
|
|
244
|
+
const guestHandoff = await (deps.borrowGuestCredential ?? borrowDedicatedGuestCredential)();
|
|
245
|
+
if (guestHandoff === true)
|
|
246
|
+
localCredentialAvailable = true;
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// A usable stored credential remains the fallback during a hand-off outage.
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (!localCredentialAvailable && deps.hasCredential) {
|
|
188
253
|
localCredentialAvailable = deps.hasCredential();
|
|
189
254
|
}
|
|
190
|
-
else {
|
|
255
|
+
else if (!localCredentialAvailable) {
|
|
191
256
|
(0, cred_envelope_1.consumeEncryptedCredentialReadWhileInactive)();
|
|
192
257
|
localCredentialAvailable = hasLocalCredential();
|
|
193
258
|
if (!localCredentialAvailable && (0, cred_envelope_1.consumeEncryptedCredentialReadWhileInactive)()) {
|
|
@@ -195,6 +260,15 @@ async function acquireManagedSession(input = {}, deps = {}) {
|
|
|
195
260
|
localCredentialAvailable = hasLocalCredential();
|
|
196
261
|
}
|
|
197
262
|
}
|
|
263
|
+
if (!localCredentialAvailable) {
|
|
264
|
+
const guestHandoff = await (deps.borrowGuestCredential ?? borrowDedicatedGuestCredential)();
|
|
265
|
+
if (guestHandoff === true) {
|
|
266
|
+
localCredentialAvailable = true;
|
|
267
|
+
}
|
|
268
|
+
else if (guestHandoff === false) {
|
|
269
|
+
throw new ManagedAcquisitionError('guest-handoff-unavailable', 'the Dedicated Runtime could not provide its managed skrr session');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
198
272
|
if (!localCredentialAvailable) {
|
|
199
273
|
throw new ManagedAcquisitionError('not-signed-in', 'no skrr session on this machine — run `skrr login`');
|
|
200
274
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function isHarnessCliContext(env?: Readonly<Record<string, string | undefined>>): boolean;
|
|
2
|
+
/** Thrown by `writeToBackend` when a harness-context process tries to persist. */
|
|
3
|
+
export declare class HarnessCredentialPersistRefused extends Error {
|
|
4
|
+
readonly code = "HARNESS_CONTEXT_PERSIST_REFUSED";
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
/** The commands a harness-context process may not run (they own the stored session). */
|
|
8
|
+
export declare const HARNESS_REFUSED_AUTH_COMMANDS: ReadonlySet<string>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HARNESS_REFUSED_AUTH_COMMANDS = exports.HarnessCredentialPersistRefused = void 0;
|
|
4
|
+
exports.isHarnessCliContext = isHarnessCliContext;
|
|
5
|
+
/**
|
|
6
|
+
* harness-context.ts — is this `skrr` running for an agent the daemon started?
|
|
7
|
+
* (OSK-13254)
|
|
8
|
+
*
|
|
9
|
+
* A person's `skrr` may adopt a CLI refresh session of its own through the
|
|
10
|
+
* local daemon's hand-off, so that signing in survives the daemon restarting
|
|
11
|
+
* (`daemonBroker.ts`, `maybeAutoBroker`). A `skrr` the daemon spawned for an
|
|
12
|
+
* agent or a tool must not: it borrows short-lived tokens from the daemon for
|
|
13
|
+
* each process, and never reads, writes, rotates or revokes the person's
|
|
14
|
+
* stored CLI session.
|
|
15
|
+
*
|
|
16
|
+
* Two daemon-set markers say so, and either is enough:
|
|
17
|
+
*
|
|
18
|
+
* - `OVERSKY_AUTONOMOUS_SESSION=1` — a harness session (`session-wrapper.ts`,
|
|
19
|
+
* `api-local-session-client.ts`). Such a process already never reaches the
|
|
20
|
+
* stored-credential chain (`BaseCommand.init` returns early on it); it is
|
|
21
|
+
* listed here so every guard below answers the same question.
|
|
22
|
+
* - `SKRR_DAEMON_CHILD` — every other agent/tool child: one-shot Bash,
|
|
23
|
+
* background Bash, agent terminals, harness backends
|
|
24
|
+
* (`daemon/src/env-sanitize.ts`, `agent-terminal-env.ts`). It survives the
|
|
25
|
+
* shared sanitizer, so grandchildren keep it.
|
|
26
|
+
*
|
|
27
|
+
* What this is and is not, stated so nobody leans on it harder than it holds:
|
|
28
|
+
* on a laptop the daemon, the agent and the person share one uid, so this is
|
|
29
|
+
* POLICY — a child that deletes the marker can do what the person's shell can.
|
|
30
|
+
* Where a uid boundary exists (Dedicated Runtime, identity-split Hosted
|
|
31
|
+
* Machines) the guest descriptor offers only the access-token mode, so a
|
|
32
|
+
* durable family cannot be minted there whatever the environment says. This
|
|
33
|
+
* is the same scope invariant I1 has today
|
|
34
|
+
* (`docs/architecture/managed-inference-execution-shapes-2026-08-12.md` §15).
|
|
35
|
+
*/
|
|
36
|
+
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
37
|
+
function isHarnessCliContext(env = process.env) {
|
|
38
|
+
return env.OVERSKY_AUTONOMOUS_SESSION === '1' || (0, auth_core_1.isDaemonChildEnv)(env);
|
|
39
|
+
}
|
|
40
|
+
/** Thrown by `writeToBackend` when a harness-context process tries to persist. */
|
|
41
|
+
class HarnessCredentialPersistRefused extends Error {
|
|
42
|
+
code = 'HARNESS_CONTEXT_PERSIST_REFUSED';
|
|
43
|
+
constructor() {
|
|
44
|
+
super('Refusing to store a skrr sign-in from a process the skrr background service started for an ' +
|
|
45
|
+
'agent or tool. skrr here borrows short-lived tokens from the service; sign in from your own terminal.');
|
|
46
|
+
this.name = 'HarnessCredentialPersistRefused';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.HarnessCredentialPersistRefused = HarnessCredentialPersistRefused;
|
|
50
|
+
/** The commands a harness-context process may not run (they own the stored session). */
|
|
51
|
+
exports.HARNESS_REFUSED_AUTH_COMMANDS = new Set([
|
|
52
|
+
'login',
|
|
53
|
+
'logout',
|
|
54
|
+
'pair',
|
|
55
|
+
]);
|
package/dist/lib/login.js
CHANGED
|
@@ -202,7 +202,10 @@ async function cliLogin(opts = {}) {
|
|
|
202
202
|
// The daemon answered and refused. Say so before whatever comes next —
|
|
203
203
|
// a browser that opens with no stated cause, or a refusal about terminals
|
|
204
204
|
// that says nothing about the daemon, both hid the real reason.
|
|
205
|
-
|
|
205
|
+
// A daemon whose descriptor is on disk but which did not answer (down or
|
|
206
|
+
// mid-restart) is described too: without it a headless `skrr login` during
|
|
207
|
+
// a restart said only that this shell has no terminal (OSK-13099).
|
|
208
|
+
brokerRefusal = (0, daemonBrokerRefusal_1.describeBrokerFailure)(brokerResult);
|
|
206
209
|
if (brokerRefusal && !(0, ssh_detect_1.isNonInteractive)()) {
|
|
207
210
|
console.log('');
|
|
208
211
|
console.log(` Asked the local skrr service for a session first. ${brokerRefusal.headline}`);
|
package/dist/lib/refresh.js
CHANGED
|
@@ -30,6 +30,7 @@ const config_1 = require("./config");
|
|
|
30
30
|
const cli_id_1 = require("./cli-id");
|
|
31
31
|
const credential_resolver_1 = require("./credential-resolver");
|
|
32
32
|
const device_identity_1 = require("./device-identity");
|
|
33
|
+
const harness_context_1 = require("./harness-context");
|
|
33
34
|
exports.CLI_REFRESH_EARLY_MS = 5 * 60 * 1000;
|
|
34
35
|
/**
|
|
35
36
|
* Stable origin header so the server's benign-grace matcher can identify
|
|
@@ -96,6 +97,9 @@ function canMutatePersistedCredential(credential, bareMode) {
|
|
|
96
97
|
return false;
|
|
97
98
|
if ((0, auth_core_1.isStaticTokenKind)(credential.kind))
|
|
98
99
|
return false;
|
|
100
|
+
// A borrower never rotates the person's stored family (OSK-13254).
|
|
101
|
+
if ((0, harness_context_1.isHarnessCliContext)())
|
|
102
|
+
return false;
|
|
99
103
|
// These sources are operator-owned or generated on demand. Never rotate
|
|
100
104
|
// them into the local keychain/file store behind the operator's back.
|
|
101
105
|
return credential.source === 'keychain' || credential.source === 'file';
|
|
@@ -35,7 +35,7 @@ export { ensureMachineDaemonId, legacyDaemonUuids, __resetCachedDaemonIdForTest,
|
|
|
35
35
|
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';
|
|
36
36
|
export { HARNESS_TIERS, getHarnessTier, credentialPolicyForTier, tierPermitsConfiguredAutoMode, type HarnessTrustTier, type CredentialDeliveryPolicy, type CredentialMode, } from './harnessTrust.js';
|
|
37
37
|
export { HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT, claudeSkillsLayoutEvidence, harnessLoadsClaudeSkillsLayout, } from './harnessSkillLayout.js';
|
|
38
|
-
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 { sanitizeSpawnEnv, SENSITIVE_ENV_VARS, SENSITIVE_ENV_PREFIXES, SENSITIVE_ENV_SUFFIXES, SENSITIVE_ENV_NAME_TOKENS, SENSITIVE_ENV_PREFIX_EXCEPTIONS, DAEMON_CHILD_ENV, isDaemonChildEnv, LOOPBACK_NO_PROXY_HOSTS, envHasProxy, withLoopbackNoProxy, } from './spawnEnv.js';
|
|
39
39
|
export { loopbackFetch, assertLoopbackHttpUrl, isLoopbackHostname, isLoopbackAddress, LoopbackTargetRefusedError, LOOPBACK_DEFAULT_MAX_BODY_BYTES, type LoopbackFetch, type LoopbackFetchInit, } from './loopbackHttp.js';
|
|
40
40
|
export { CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAMES, NATIVE_ID_PREFIX, LEGACY_NATIVE_ID_PREFIXES, DEFAULT_BINARY_NAME, } from './localIdentity.js';
|
|
41
41
|
export { findLegacyLocalState, describeLegacyState, type LegacyStateFinding, type LegacyStateReport, } from './legacyStatePreflight.js';
|
|
@@ -25,8 +25,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
25
25
|
exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__resetAuthHelperFailureReasonForTest = exports.getLastAuthHelperFailureReason = exports.getDefaultHelperTtlMs = exports.invokeAuthHelper = exports.AuthHelperUnavailableError = exports.AuthHelperUntrustedError = exports.isStaticTokenKind = exports.PERSONAL_ACCESS_TOKEN_PREFIX = 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.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 = exports.generateVerifier = exports.invalidateHeadersHelperCache = void 0;
|
|
27
27
|
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.describeDeviceKeySplit = 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.DEFAULT_UNREADABLE_CREDENTIAL_HINT = exports.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.splitKeyConvergencePolicy = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDpopProof = exports.normalizeDpopHtu = exports.isJwkThumbprint = exports.isAth = exports.createDpopKey = exports.accessTokenHash = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_TYP = exports.DPOP_PROOF_REQUIRED = exports.DPOP_PROOF_INVALID = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.verifyDeviceProof = exports.tokenHash = exports.publicJwkFromX = exports.normalizeHtu = void 0;
|
|
28
|
-
exports.
|
|
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 = 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 = void 0;
|
|
28
|
+
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.isDaemonChildEnv = exports.DAEMON_CHILD_ENV = 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 = 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 = 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 = 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 = 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
|
|
@@ -346,6 +346,8 @@ Object.defineProperty(exports, "SENSITIVE_ENV_PREFIXES", { enumerable: true, get
|
|
|
346
346
|
Object.defineProperty(exports, "SENSITIVE_ENV_SUFFIXES", { enumerable: true, get: function () { return spawnEnv_js_1.SENSITIVE_ENV_SUFFIXES; } });
|
|
347
347
|
Object.defineProperty(exports, "SENSITIVE_ENV_NAME_TOKENS", { enumerable: true, get: function () { return spawnEnv_js_1.SENSITIVE_ENV_NAME_TOKENS; } });
|
|
348
348
|
Object.defineProperty(exports, "SENSITIVE_ENV_PREFIX_EXCEPTIONS", { enumerable: true, get: function () { return spawnEnv_js_1.SENSITIVE_ENV_PREFIX_EXCEPTIONS; } });
|
|
349
|
+
Object.defineProperty(exports, "DAEMON_CHILD_ENV", { enumerable: true, get: function () { return spawnEnv_js_1.DAEMON_CHILD_ENV; } });
|
|
350
|
+
Object.defineProperty(exports, "isDaemonChildEnv", { enumerable: true, get: function () { return spawnEnv_js_1.isDaemonChildEnv; } });
|
|
349
351
|
Object.defineProperty(exports, "LOOPBACK_NO_PROXY_HOSTS", { enumerable: true, get: function () { return spawnEnv_js_1.LOOPBACK_NO_PROXY_HOSTS; } });
|
|
350
352
|
Object.defineProperty(exports, "envHasProxy", { enumerable: true, get: function () { return spawnEnv_js_1.envHasProxy; } });
|
|
351
353
|
Object.defineProperty(exports, "withLoopbackNoProxy", { enumerable: true, get: function () { return spawnEnv_js_1.withLoopbackNoProxy; } });
|
|
@@ -77,6 +77,31 @@ export declare const SENSITIVE_ENV_SUFFIXES: readonly string[];
|
|
|
77
77
|
export declare const SENSITIVE_ENV_NAME_TOKENS: readonly string[];
|
|
78
78
|
/** Specific names to keep — the prefix sweeps are broad on purpose. */
|
|
79
79
|
export declare const SENSITIVE_ENV_PREFIX_EXCEPTIONS: ReadonlySet<string>;
|
|
80
|
+
/**
|
|
81
|
+
* OSK-13254 — the marker the daemon stamps on every agent- or tool-driven child
|
|
82
|
+
* (`daemon/src/env-sanitize.ts`, `daemon/src/terminal/agent-terminal-env.ts`).
|
|
83
|
+
*
|
|
84
|
+
* A `skrr` that sees it is a borrower: it redeems short-lived tokens from the
|
|
85
|
+
* daemon's loopback broker, never adopts a CLI refresh session of its own, and
|
|
86
|
+
* never reads the person's stored one (`cli/src/lib/harness-context.ts`). A
|
|
87
|
+
* person's own terminal — including the web terminal, which is a human surface
|
|
88
|
+
* (`humanTerminalEnv`) — does not carry it.
|
|
89
|
+
*
|
|
90
|
+
* The marker can only take authority away, so a process that forges it hurts
|
|
91
|
+
* nobody but itself. On a laptop it is POLICY, not a boundary: a same-uid child
|
|
92
|
+
* that deletes it can do whatever the person's own shell can. Where a uid
|
|
93
|
+
* boundary exists (Dedicated Runtime, identity-split Hosted Machines), the
|
|
94
|
+
* guest descriptor already offers only the access-token mode, so a durable CLI
|
|
95
|
+
* family cannot be minted there whatever the environment says.
|
|
96
|
+
*/
|
|
97
|
+
export declare const DAEMON_CHILD_ENV = "SKRR_DAEMON_CHILD";
|
|
98
|
+
/**
|
|
99
|
+
* True when `env` carries the daemon-child marker with ANY non-empty value.
|
|
100
|
+
* Deliberately no "0 means off": the marker only narrows authority, so the
|
|
101
|
+
* one value that could switch it off is the one a request-carried environment
|
|
102
|
+
* would forge.
|
|
103
|
+
*/
|
|
104
|
+
export declare function isDaemonChildEnv(env: Readonly<Record<string, string | undefined>>): boolean;
|
|
80
105
|
/**
|
|
81
106
|
* Build a sanitized snapshot of the parent's own env for a spawned
|
|
82
107
|
* child, stripping credentials and library-injection vectors while
|