@skrr-ai/cli 0.1.80 → 0.1.82
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 +20 -0
- package/dist/base-command.js +151 -7
- package/dist/commands/agents/visibility.d.ts +14 -0
- package/dist/commands/agents/visibility.js +52 -0
- package/dist/commands/create-token.d.ts +0 -1
- package/dist/commands/create-token.js +3 -1
- package/dist/commands/followups/checkin.js +1 -1
- package/dist/commands/list-tokens.d.ts +0 -1
- package/dist/commands/list-tokens.js +1 -1
- package/dist/commands/login.js +10 -0
- package/dist/commands/logout.js +4 -0
- package/dist/commands/machines/dedicated/ssh-proxy.js +1 -0
- package/dist/commands/revoke-token.d.ts +0 -1
- package/dist/commands/revoke-token.js +1 -1
- package/dist/commands/store/follow.d.ts +1 -0
- package/dist/commands/store/follow.js +7 -1
- package/dist/commands/store/follows/list.d.ts +11 -0
- package/dist/commands/store/follows/list.js +29 -0
- package/dist/commands/store/follows/remove.d.ts +16 -0
- package/dist/commands/store/follows/remove.js +37 -0
- package/dist/commands/store/installs.js +6 -1
- package/dist/commands/store/publishers/profile/draft.d.ts +17 -0
- package/dist/commands/store/publishers/profile/draft.js +54 -0
- package/dist/commands/store/publishers/profile/handle.d.ts +14 -0
- package/dist/commands/store/publishers/profile/handle.js +39 -0
- package/dist/commands/store/publishers/profile/hide.d.ts +12 -0
- package/dist/commands/store/publishers/profile/hide.js +39 -0
- package/dist/commands/store/publishers/profile/preview.d.ts +11 -0
- package/dist/commands/store/publishers/profile/preview.js +39 -0
- package/dist/commands/store/publishers/profile/publish.d.ts +12 -0
- package/dist/commands/store/publishers/profile/publish.js +41 -0
- package/dist/commands/store/publishers/profile/restore.d.ts +15 -0
- package/dist/commands/store/publishers/profile/restore.js +49 -0
- package/dist/commands/store/publishers/profile/revisions.d.ts +11 -0
- package/dist/commands/store/publishers/profile/revisions.js +30 -0
- package/dist/commands/store/publishers/profile/show.d.ts +11 -0
- package/dist/commands/store/publishers/profile/show.js +39 -0
- package/dist/commands/store/review.d.ts +26 -0
- package/dist/commands/store/review.js +93 -0
- package/dist/commands/store/reviews.d.ts +29 -0
- package/dist/commands/store/reviews.js +170 -0
- package/dist/commands/store/tip.d.ts +20 -0
- package/dist/commands/store/tip.js +57 -0
- package/dist/commands/tokens/create.d.ts +28 -0
- package/dist/commands/tokens/create.js +136 -0
- package/dist/commands/tokens/list.d.ts +11 -0
- package/dist/commands/tokens/list.js +53 -0
- package/dist/commands/tokens/revoke.d.ts +14 -0
- package/dist/commands/tokens/revoke.js +42 -0
- package/dist/lib/access-tokens.d.ts +59 -0
- package/dist/lib/access-tokens.js +50 -0
- package/dist/lib/auth-core-init.d.ts +6 -0
- package/dist/lib/auth-core-init.js +28 -6
- 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.d.ts +1 -1
- package/dist/lib/credential-resolver.js +15 -3
- package/dist/lib/daemonBroker.d.ts +48 -2
- package/dist/lib/daemonBroker.js +193 -15
- package/dist/lib/daemonBrokerRefusal.d.ts +14 -0
- package/dist/lib/daemonBrokerRefusal.js +18 -1
- package/dist/lib/dedicated-ssh.d.ts +9 -0
- package/dist/lib/dedicated-ssh.js +232 -63
- 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/node-adapter.js +2 -2
- package/dist/lib/publisher-profile-flags.d.ts +19 -0
- package/dist/lib/publisher-profile-flags.js +48 -0
- package/dist/lib/refresh.d.ts +1 -1
- package/dist/lib/refresh.js +10 -1
- package/dist/lib/ssh-ordered-bytes.d.ts +8 -0
- package/dist/lib/ssh-ordered-bytes.js +31 -0
- package/dist/lib/usd-micros.d.ts +9 -0
- package/dist/lib/usd-micros.js +21 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.d.ts +22 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.js +29 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +7 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +7 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +9 -5
- 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/credentialResolver.d.ts +22 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.js +27 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +7 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +7 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -2
- 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 +4302 -4249
- package/oclif.manifest.json +41314 -39972
- package/package.json +28 -4
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACCESS_TOKEN_REVOKED_REASON_LABELS = void 0;
|
|
4
|
+
exports.createAccessToken = createAccessToken;
|
|
5
|
+
exports.listAccessTokens = listAccessTokens;
|
|
6
|
+
exports.revokeAccessToken = revokeAccessToken;
|
|
7
|
+
exports.describeAccessTokenState = describeAccessTokenState;
|
|
8
|
+
const api_fetch_1 = require("./api-fetch");
|
|
9
|
+
/**
|
|
10
|
+
* The words for each stored revocation reason. Every member of the schema's
|
|
11
|
+
* `revokedReason` enum appears here, so the list shows WHY a token died —
|
|
12
|
+
* "revoked" alone cannot tell a deliberate revoke from a sign-out sweep.
|
|
13
|
+
*/
|
|
14
|
+
exports.ACCESS_TOKEN_REVOKED_REASON_LABELS = {
|
|
15
|
+
user: 'revoked by you',
|
|
16
|
+
sign_out_everywhere: 'revoked by sign-out everywhere / password or 2FA change',
|
|
17
|
+
admin: 'revoked by an administrator',
|
|
18
|
+
compromised: 'revoked as compromised',
|
|
19
|
+
};
|
|
20
|
+
async function createAccessToken(input) {
|
|
21
|
+
return (0, api_fetch_1.apiFetch)('/api/user/access-tokens', {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
body: {
|
|
24
|
+
name: input.name,
|
|
25
|
+
...(input.scopes ? { scopes: input.scopes } : {}),
|
|
26
|
+
...(input.expiresInDays !== undefined ? { expiresInDays: input.expiresInDays } : {}),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Every token the caller owns, newest first, including revoked and expired rows. */
|
|
31
|
+
async function listAccessTokens() {
|
|
32
|
+
const res = await (0, api_fetch_1.apiFetch)('/api/user/access-tokens');
|
|
33
|
+
return Array.isArray(res?.accessTokens) ? res.accessTokens : [];
|
|
34
|
+
}
|
|
35
|
+
/** Revoke one token. Irreversible: a revoked token is replaced, never re-enabled. */
|
|
36
|
+
async function revokeAccessToken(id, opts = {}) {
|
|
37
|
+
return (0, api_fetch_1.apiFetch)(`/api/user/access-tokens/${encodeURIComponent(id)}`, {
|
|
38
|
+
method: 'DELETE',
|
|
39
|
+
...(opts.compromised ? { body: { reason: 'compromised' } } : {}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** One-line status for a listed token. */
|
|
43
|
+
function describeAccessTokenState(token) {
|
|
44
|
+
if (token.state === 'revoked') {
|
|
45
|
+
return exports.ACCESS_TOKEN_REVOKED_REASON_LABELS[token.revokedReason ?? ''] ?? 'revoked';
|
|
46
|
+
}
|
|
47
|
+
if (token.state === 'expired')
|
|
48
|
+
return 'expired';
|
|
49
|
+
return 'active';
|
|
50
|
+
}
|
|
@@ -36,6 +36,12 @@ export declare function plainCredentialEnvelopeWarning(args: readonly unknown[])
|
|
|
36
36
|
* own when a local daemon can hand a credential over.
|
|
37
37
|
*/
|
|
38
38
|
export declare const CREDENTIAL_KEY_CHANGED_NOTICE: string;
|
|
39
|
+
/**
|
|
40
|
+
* The one line an unreadable device key prints when no credential-key change
|
|
41
|
+
* explains it. The CLI never enrolls or requires a device key (OSK-12063), so
|
|
42
|
+
* this degrades a proof, not access — say exactly that.
|
|
43
|
+
*/
|
|
44
|
+
export declare const DEVICE_KEY_RECOVERY_REQUIRED_NOTICE: string;
|
|
39
45
|
export declare function createCredentialEnvelopeWarningPresenter(): (args: readonly unknown[]) => string | null;
|
|
40
46
|
type DedicatedGenerationPreparationOptions = {
|
|
41
47
|
machineUuid?: string;
|
|
@@ -3,7 +3,7 @@ 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 = exports.CREDENTIAL_KEY_CHANGED_NOTICE = void 0;
|
|
6
|
+
exports.CliCredentialInitializationError = exports.DEVICE_KEY_RECOVERY_REQUIRED_NOTICE = exports.CREDENTIAL_KEY_CHANGED_NOTICE = void 0;
|
|
7
7
|
exports.plainCredentialEnvelopeWarning = plainCredentialEnvelopeWarning;
|
|
8
8
|
exports.createCredentialEnvelopeWarningPresenter = createCredentialEnvelopeWarningPresenter;
|
|
9
9
|
exports.prepareDedicatedRuntimeAuthGeneration = prepareDedicatedRuntimeAuthGeneration;
|
|
@@ -108,6 +108,20 @@ exports.CREDENTIAL_KEY_CHANGED_NOTICE = "note: this machine's credential key cha
|
|
|
108
108
|
*/
|
|
109
109
|
/** auth-core's device-identity warning for a private key it could not open. */
|
|
110
110
|
const DEVICE_KEY_DECRYPT_FAILED = '[deviceIdentity] private_key_decrypt_failed';
|
|
111
|
+
/**
|
|
112
|
+
* auth-core's device-identity warning for a stored key it will NOT replace on
|
|
113
|
+
* ordinary boot (`b5e683280b`): minting a new key here would make the next
|
|
114
|
+
* refresh sign with a key the server never pinned. Only an explicit login with
|
|
115
|
+
* `recoverUnreadable` replaces it.
|
|
116
|
+
*/
|
|
117
|
+
const DEVICE_KEY_RECOVERY_REQUIRED = '[deviceIdentity] recovery_required';
|
|
118
|
+
/**
|
|
119
|
+
* The one line an unreadable device key prints when no credential-key change
|
|
120
|
+
* explains it. The CLI never enrolls or requires a device key (OSK-12063), so
|
|
121
|
+
* this degrades a proof, not access — say exactly that.
|
|
122
|
+
*/
|
|
123
|
+
exports.DEVICE_KEY_RECOVERY_REQUIRED_NOTICE = "warning: this machine's stored device key could not be read, so skrr sends requests " +
|
|
124
|
+
'without a device proof until the local daemon signs in again. (DEBUG=1 shows the underlying error.)';
|
|
111
125
|
function createCredentialEnvelopeWarningPresenter() {
|
|
112
126
|
let keyChanged = false;
|
|
113
127
|
return (args) => {
|
|
@@ -133,12 +147,20 @@ function createCredentialEnvelopeWarningPresenter() {
|
|
|
133
147
|
if (keyChanged && event === 'cred_envelope.read.decrypt_failed')
|
|
134
148
|
return '';
|
|
135
149
|
// The device private key is one of those values: auth-core's device
|
|
136
|
-
// identity loads it next
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
150
|
+
// identity loads it next and cannot open it. Since `b5e683280b` it does
|
|
151
|
+
// NOT mint a replacement on ordinary boot — it parks in
|
|
152
|
+
// `recovery_required` until an explicit login recovers it. Both warnings
|
|
153
|
+
// come through the `[deviceIdentity]` prefix and reached the terminal as
|
|
154
|
+
// raw `[auth-core warn]` lines right under the notice, which already says
|
|
155
|
+
// the old key is gone and what replaces it.
|
|
156
|
+
if (keyChanged &&
|
|
157
|
+
(message === DEVICE_KEY_DECRYPT_FAILED || message === DEVICE_KEY_RECOVERY_REQUIRED)) {
|
|
141
158
|
return '';
|
|
159
|
+
}
|
|
160
|
+
// Without a key change an unreadable device key is a real fault: one
|
|
161
|
+
// plain line instead of an object dump.
|
|
162
|
+
if (message === DEVICE_KEY_RECOVERY_REQUIRED)
|
|
163
|
+
return exports.DEVICE_KEY_RECOVERY_REQUIRED_NOTICE;
|
|
142
164
|
return plainCredentialEnvelopeWarning(args);
|
|
143
165
|
};
|
|
144
166
|
}
|
|
@@ -124,6 +124,11 @@ export declare function describeForeignCredential(result: ReadResult): string |
|
|
|
124
124
|
* a specific backend (bare mode, store-mode overrides) should call the
|
|
125
125
|
* lower-level write helpers directly — not exported here to keep the
|
|
126
126
|
* default path honest.
|
|
127
|
+
*
|
|
128
|
+
* Refuses in a process the daemon started for an agent or tool (OSK-13254):
|
|
129
|
+
* the person's stored session is theirs, and a borrower never writes it. This
|
|
130
|
+
* is the one choke point every persisting path goes through, so the policy
|
|
131
|
+
* cannot be missed by a new caller.
|
|
127
132
|
*/
|
|
128
133
|
export declare function writeToBackend(bundle: AuthBundle, options?: WriteOptions): AuthBackend;
|
|
129
134
|
/**
|
package/dist/lib/auth-storage.js
CHANGED
|
@@ -102,6 +102,7 @@ const keychain = __importStar(require("./keychain"));
|
|
|
102
102
|
const cred_envelope_1 = require("./cred-envelope");
|
|
103
103
|
const config_1 = require("./config");
|
|
104
104
|
const cli_id_1 = require("./cli-id");
|
|
105
|
+
const harness_context_1 = require("./harness-context");
|
|
105
106
|
/** Filename for the non-darwin file backend. Parallel to the daemon's
|
|
106
107
|
* `auth.json`, deliberately distinct so the two binaries never clobber
|
|
107
108
|
* each other's tokens (CLI holds scope=cli, daemon holds scope=daemon). */
|
|
@@ -759,8 +760,15 @@ function describeForeignCredential(result) {
|
|
|
759
760
|
* a specific backend (bare mode, store-mode overrides) should call the
|
|
760
761
|
* lower-level write helpers directly — not exported here to keep the
|
|
761
762
|
* default path honest.
|
|
763
|
+
*
|
|
764
|
+
* Refuses in a process the daemon started for an agent or tool (OSK-13254):
|
|
765
|
+
* the person's stored session is theirs, and a borrower never writes it. This
|
|
766
|
+
* is the one choke point every persisting path goes through, so the policy
|
|
767
|
+
* cannot be missed by a new caller.
|
|
762
768
|
*/
|
|
763
769
|
function writeToBackend(bundle, options = {}) {
|
|
770
|
+
if ((0, harness_context_1.isHarnessCliContext)())
|
|
771
|
+
throw new harness_context_1.HarnessCredentialPersistRefused();
|
|
764
772
|
const requestedOrigin = options.serverOrigin ?? bundle.serverOrigin;
|
|
765
773
|
let persistedBundle = bundle;
|
|
766
774
|
if (requestedOrigin !== undefined) {
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -46,6 +46,14 @@ export interface CliConfig {
|
|
|
46
46
|
* `resolveCliId()` (`cli-id.ts`), the one resolver every caller goes through.
|
|
47
47
|
*/
|
|
48
48
|
profileCliId?: string;
|
|
49
|
+
/**
|
|
50
|
+
* OSK-13254 — `false` once `skrr logout` has signed this profile out: from
|
|
51
|
+
* then on a command that finds no stored session BORROWS a short-lived token
|
|
52
|
+
* from the local daemon for that process and does not adopt a durable CLI
|
|
53
|
+
* session of its own. Absent means adoption is allowed. `skrr login` clears
|
|
54
|
+
* it. Read through `cliSessionAdoptionBlocker` (`daemonBroker.ts`).
|
|
55
|
+
*/
|
|
56
|
+
adoptDaemonSession?: boolean;
|
|
49
57
|
/**
|
|
50
58
|
* OSK-3892 — the agent a locally-hosted `skrr code` session is attributed to.
|
|
51
59
|
*
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* CLI-appropriate backends. The resolver walks the published chain:
|
|
6
6
|
*
|
|
7
7
|
* 1. flag — --token <jwt> (debugging; BaseCommand parses)
|
|
8
|
-
* 2. env-token — OVERSKY_TOKEN (CI / static; kind
|
|
8
|
+
* 2. env-token — OVERSKY_TOKEN, then SKRR_TOKEN (CI / static; kind 'ci' or 'pat' detected)
|
|
9
9
|
* 3. auth-helper — OVERSKY_AUTH_HELPER script output
|
|
10
10
|
* 4. fd-handoff — N/A for CLI (skipped; no parent fd)
|
|
11
11
|
* 5. keychain — ai.skrr.cli.<profile>
|
|
@@ -12,7 +12,7 @@ exports.formatCredentialSource = formatCredentialSource;
|
|
|
12
12
|
* CLI-appropriate backends. The resolver walks the published chain:
|
|
13
13
|
*
|
|
14
14
|
* 1. flag — --token <jwt> (debugging; BaseCommand parses)
|
|
15
|
-
* 2. env-token — OVERSKY_TOKEN (CI / static; kind
|
|
15
|
+
* 2. env-token — OVERSKY_TOKEN, then SKRR_TOKEN (CI / static; kind 'ci' or 'pat' detected)
|
|
16
16
|
* 3. auth-helper — OVERSKY_AUTH_HELPER script output
|
|
17
17
|
* 4. fd-handoff — N/A for CLI (skipped; no parent fd)
|
|
18
18
|
* 5. keychain — ai.skrr.cli.<profile>
|
|
@@ -31,6 +31,7 @@ const auth_core_init_1 = require("./auth-core-init");
|
|
|
31
31
|
const config_1 = require("./config");
|
|
32
32
|
const auth_storage_1 = require("./auth-storage");
|
|
33
33
|
const cred_envelope_1 = require("./cred-envelope");
|
|
34
|
+
const harness_context_1 = require("./harness-context");
|
|
34
35
|
/**
|
|
35
36
|
* Return the only credential an Agent-owned CLI process is allowed to use.
|
|
36
37
|
*
|
|
@@ -68,7 +69,12 @@ async function resolveCredential(opts = {}) {
|
|
|
68
69
|
return undefined;
|
|
69
70
|
}
|
|
70
71
|
})();
|
|
71
|
-
|
|
72
|
+
// A process the daemon started for an agent or tool never reads the
|
|
73
|
+
// person's stored CLI session (OSK-13254): it borrows from the daemon, or
|
|
74
|
+
// uses a token it was explicitly given.
|
|
75
|
+
const storeMode = (0, harness_context_1.isHarnessCliContext)()
|
|
76
|
+
? 'env-only'
|
|
77
|
+
: (opts.storeModeOverride ?? cfg?.credentials?.store ?? 'auto');
|
|
72
78
|
const bareMode = opts.bareMode || ['1', 'true', 'TRUE', 'yes'].includes(process.env.OVERSKY_BARE_MODE ?? '');
|
|
73
79
|
// Clear an observation left by a prior, unrelated direct backend probe in
|
|
74
80
|
// this process. Only a blocked read made by THIS resolver should trigger the
|
|
@@ -78,6 +84,9 @@ async function resolveCredential(opts = {}) {
|
|
|
78
84
|
flagToken: opts.flagToken,
|
|
79
85
|
bareMode,
|
|
80
86
|
storeMode,
|
|
87
|
+
// SKRR_TOKEN is the name the CI recipe uses (OSK-13208); OVERSKY_TOKEN still
|
|
88
|
+
// wins when both are set, as in the config overlay (config.ts).
|
|
89
|
+
envTokenNames: ['OVERSKY_TOKEN', 'SKRR_TOKEN'],
|
|
81
90
|
// CLI never has an interactive slot — users run `skrr login` explicitly.
|
|
82
91
|
isHeadless: () => true,
|
|
83
92
|
// Stored credentials are only usable against the origin that issued
|
|
@@ -160,7 +169,10 @@ function formatCredentialSource(source) {
|
|
|
160
169
|
case 'flag':
|
|
161
170
|
return 'CLI --token flag';
|
|
162
171
|
case 'env-token':
|
|
163
|
-
|
|
172
|
+
// Name the variable that actually supplied it (OVERSKY_TOKEN wins).
|
|
173
|
+
return !process.env.OVERSKY_TOKEN?.trim() && process.env.SKRR_TOKEN?.trim()
|
|
174
|
+
? 'SKRR_TOKEN'
|
|
175
|
+
: 'OVERSKY_TOKEN';
|
|
164
176
|
case 'auth-helper':
|
|
165
177
|
return 'OVERSKY_AUTH_HELPER';
|
|
166
178
|
case 'fd-handoff':
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* daemonBroker.ts —
|
|
2
|
+
* daemonBroker.ts — skrr CLI client for the local `skrrd` daemon's
|
|
3
3
|
* cli-handoff endpoint.
|
|
4
4
|
*
|
|
5
5
|
* When the daemon is already authenticated for the user on this machine,
|
|
@@ -199,7 +199,7 @@ export interface DaemonBrokerOptions {
|
|
|
199
199
|
/** Stable per-machine cliId the CLI wants the new refresh row bound to. */
|
|
200
200
|
cliId: string;
|
|
201
201
|
/**
|
|
202
|
-
* The
|
|
202
|
+
* The skrr CLI's currently-configured baseURL (e.g. `https://skrr.ai`).
|
|
203
203
|
* Two roles:
|
|
204
204
|
* 1. Pre-flight check vs `bootstrap.serverUrl` — when they don't
|
|
205
205
|
* match, we refuse to call the daemon so we never mint a token
|
|
@@ -246,6 +246,14 @@ export interface DaemonBrokerOptions {
|
|
|
246
246
|
* regardless — its secret cannot mint a family at all.
|
|
247
247
|
*/
|
|
248
248
|
preferHandoffMode?: 'access_token' | 'refresh_family';
|
|
249
|
+
/**
|
|
250
|
+
* Never take the durable mint, whatever the descriptor offers (OSK-13254).
|
|
251
|
+
* Set for a process the daemon started for an agent or tool
|
|
252
|
+
* (`harness-context.ts`): it borrows an access token or nothing. A v1
|
|
253
|
+
* descriptor, which offers only the durable mint, is reported as unable to
|
|
254
|
+
* lend rather than minting a family nobody would hold.
|
|
255
|
+
*/
|
|
256
|
+
borrowOnly?: boolean;
|
|
249
257
|
/**
|
|
250
258
|
* Overrides for the bounded wait on a restarting local daemon (OSK-13222):
|
|
251
259
|
* budgets, the clock and the supervisor probe. Tests use it; production
|
|
@@ -461,6 +469,33 @@ export interface MaybeAutoBrokerOptions {
|
|
|
461
469
|
* everywhere else. Production callers omit it and behave exactly as before.
|
|
462
470
|
*/
|
|
463
471
|
bootstrapPathOverride?: string;
|
|
472
|
+
/**
|
|
473
|
+
* The profile whose daemon may be borrowed. Defaults to the active profile.
|
|
474
|
+
*
|
|
475
|
+
* A silent hand-off must never cross profiles (OSK-13257). The `default`
|
|
476
|
+
* candidate list falls through to any running daemon, and every daemon is
|
|
477
|
+
* signed in as whoever started it — so `--profile buyer`, with its own
|
|
478
|
+
* session refused, used to run as the machine owner after one stderr line.
|
|
479
|
+
* A named profile now borrows only a daemon started under that same
|
|
480
|
+
* profile, and otherwise reports NOT_SIGNED_IN for the profile it was given.
|
|
481
|
+
*/
|
|
482
|
+
profile?: string;
|
|
483
|
+
/**
|
|
484
|
+
* Adopt a CLI session of this profile's own instead of borrowing (OSK-13254).
|
|
485
|
+
*
|
|
486
|
+
* Set by `BaseCommand.init` ONLY when the resolver found no stored
|
|
487
|
+
* credential at all — a first sign-in. The 401-recovery callers leave it
|
|
488
|
+
* unset: a stored session that was refused may have been revoked on purpose
|
|
489
|
+
* (Active Sessions, a password change), and re-adopting a new one behind
|
|
490
|
+
* that decision would undo it. They borrow, as before.
|
|
491
|
+
*
|
|
492
|
+
* Ignored (borrow instead) in a harness context, after `skrr logout`
|
|
493
|
+
* (`cliConfig.adoptDaemonSession === false`), and under
|
|
494
|
+
* `SKRR_NO_CLI_SESSION_ADOPTION=1`. See {@link cliSessionAdoptionBlocker}.
|
|
495
|
+
*/
|
|
496
|
+
adopt?: boolean;
|
|
497
|
+
/** Test seam: lock-acquisition budget for the adoption lock. */
|
|
498
|
+
adoptionLockRetries?: number;
|
|
464
499
|
}
|
|
465
500
|
export interface MaybeAutoBrokerResult {
|
|
466
501
|
/**
|
|
@@ -478,7 +513,18 @@ export interface MaybeAutoBrokerResult {
|
|
|
478
513
|
* and end up with a divergent refresh family.
|
|
479
514
|
*/
|
|
480
515
|
updatedConfig?: CliConfig;
|
|
516
|
+
/**
|
|
517
|
+
* True when this process waited on the adoption lock and found that a
|
|
518
|
+
* sibling `skrr` had adopted a session for this profile meanwhile. Nothing
|
|
519
|
+
* was minted; the caller re-reads the stored credential (OSK-13254).
|
|
520
|
+
*/
|
|
521
|
+
adoptedBySibling?: boolean;
|
|
481
522
|
}
|
|
523
|
+
/**
|
|
524
|
+
* Why this invocation must borrow instead of adopting a CLI session of its
|
|
525
|
+
* own, or null when it may adopt (OSK-13254).
|
|
526
|
+
*/
|
|
527
|
+
export declare function cliSessionAdoptionBlocker(cliConfig: Pick<CliConfig, 'adoptDaemonSession'>, env?: NodeJS.ProcessEnv): 'harness_context' | 'signed_out' | 'opted_out' | null;
|
|
482
528
|
/**
|
|
483
529
|
* Auto-broker pre-flight for `BaseCommand.init()`. When the credential
|
|
484
530
|
* resolver returns no token, this attempts a silent daemon broker so
|
package/dist/lib/daemonBroker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* daemonBroker.ts —
|
|
3
|
+
* daemonBroker.ts — skrr CLI client for the local `skrrd` daemon's
|
|
4
4
|
* cli-handoff endpoint.
|
|
5
5
|
*
|
|
6
6
|
* When the daemon is already authenticated for the user on this machine,
|
|
@@ -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,18 +92,22 @@ 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");
|
|
110
|
+
const keychain_1 = require("./keychain");
|
|
104
111
|
const REQUEST_TIMEOUT_MS = 20_000;
|
|
105
112
|
/**
|
|
106
113
|
* Normalize a base URL for equality comparison. Mirrors the daemon-side
|
|
@@ -247,9 +254,9 @@ exports.HOSTED_MACHINE_CLI_HANDOFF_DESCRIPTOR = '/run/skrr-hosted-machine/cli-ha
|
|
|
247
254
|
/** Where the cli-handoff broker looks, in order. */
|
|
248
255
|
function resolveCliHandoffCandidatePaths(profile = 'default') {
|
|
249
256
|
const candidates = resolveBootstrapCandidatePaths(profile);
|
|
250
|
-
// Any profile: a guest runs one daemon
|
|
251
|
-
//
|
|
252
|
-
// only
|
|
257
|
+
// Any profile: a guest runs one daemon holding the one account the guest
|
|
258
|
+
// serves, so every profile on it is that account. On a laptop a named profile
|
|
259
|
+
// reaches only its own island file above (OSK-13257).
|
|
253
260
|
if (process.platform === 'linux') {
|
|
254
261
|
candidates.push(exports.DEDICATED_RUNTIME_CLI_HANDOFF_DESCRIPTOR, exports.HOSTED_MACHINE_CLI_HANDOFF_DESCRIPTOR);
|
|
255
262
|
}
|
|
@@ -561,8 +568,19 @@ async function brokerThrough(bootstrap, opts, sourcePath) {
|
|
|
561
568
|
// would either break explicit login or be bypassed by anyone who calls it
|
|
562
569
|
// the way login does. It is not a boundary, so it is not attempted. What the
|
|
563
570
|
// CLI CAN guarantee is that its own automatic path never mints and persists
|
|
564
|
-
// a refresh family: when `access_token` is advertised,
|
|
565
|
-
// the answer, and the daemon's outage is reported with its
|
|
571
|
+
// a refresh family on a borrower's behalf: when `access_token` is advertised,
|
|
572
|
+
// a failed redeem is the answer, and the daemon's outage is reported with its
|
|
573
|
+
// repair.
|
|
574
|
+
//
|
|
575
|
+
// OSK-13254 narrowed "the automatic path" to mean a BORROWER. A person's
|
|
576
|
+
// first `skrr` on a profile with nothing stored now ADOPTS a CLI session of
|
|
577
|
+
// its own (`maybeAutoBroker` with `adopt`, `preferHandoffMode:
|
|
578
|
+
// 'refresh_family'`), because a sign-in that lived only as long as the daemon
|
|
579
|
+
// read as "signed out" every time the daemon restarted. What OSK-12018 was
|
|
580
|
+
// guarding against — an agent-originated implicit sign-in persisting a family
|
|
581
|
+
// — is now refused by name: a process the daemon started for an agent or tool
|
|
582
|
+
// carries `SKRR_DAEMON_CHILD` / `OVERSKY_AUTONOMOUS_SESSION`
|
|
583
|
+
// (`harness-context.ts`), sets `borrowOnly`, and `writeToBackend` refuses it.
|
|
566
584
|
//
|
|
567
585
|
// COMPATIBILITY WINDOW: a v1 descriptor (no `handoffModes`, so no
|
|
568
586
|
// `access_token`) still takes the durable mint on the automatic path below,
|
|
@@ -577,7 +595,17 @@ async function brokerThrough(bootstrap, opts, sourcePath) {
|
|
|
577
595
|
const offersAccessToken = (0, cli_handoff_wire_1.descriptorOffersAccessToken)(bootstrap);
|
|
578
596
|
const offersRefreshFamily = modes.includes('refresh_family');
|
|
579
597
|
const preferAccessToken = opts.preferHandoffMode === 'access_token';
|
|
580
|
-
if (
|
|
598
|
+
if (opts.borrowOnly && !offersAccessToken) {
|
|
599
|
+
return {
|
|
600
|
+
ok: false,
|
|
601
|
+
reason: 'unknown',
|
|
602
|
+
refusedBy: 'daemon',
|
|
603
|
+
code: 'HANDOFF_BORROW_UNAVAILABLE',
|
|
604
|
+
detail: 'The skrr background service on this computer is too old to lend this agent process a ' +
|
|
605
|
+
'short-lived token, and a process it started for an agent never stores a sign-in. Update skrrd.',
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
if (offersAccessToken && (preferAccessToken || opts.borrowOnly || !offersRefreshFamily)) {
|
|
581
609
|
const redeem = await redeemCliHandoffToken(bootstrap, {
|
|
582
610
|
forceRefresh: false,
|
|
583
611
|
timeoutMs: opts.timeoutMs,
|
|
@@ -1149,6 +1177,16 @@ async function attemptDaemonBrokerLoginAndPersist(opts) {
|
|
|
1149
1177
|
// Callers use the token in-memory for this process only.
|
|
1150
1178
|
if (outcome.brokered)
|
|
1151
1179
|
return outcome;
|
|
1180
|
+
// Unreachable through `brokerThrough`, which never mints for a borrower;
|
|
1181
|
+
// kept so a future route cannot persist on a borrower's behalf.
|
|
1182
|
+
if (opts.borrowOnly) {
|
|
1183
|
+
return {
|
|
1184
|
+
ok: false,
|
|
1185
|
+
reason: 'unknown',
|
|
1186
|
+
code: 'HANDOFF_BORROW_UNAVAILABLE',
|
|
1187
|
+
detail: 'A borrower was handed a durable credential; it was not stored.',
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1152
1190
|
const serverOrigin = normalizeBaseUrl(opts.baseURL);
|
|
1153
1191
|
if (!serverOrigin) {
|
|
1154
1192
|
throw new Error('Daemon-broker credential persistence requires a valid CLI baseURL.');
|
|
@@ -1198,6 +1236,123 @@ const AUTH_SELF_MANAGED_COMMANDS = new Set([
|
|
|
1198
1236
|
'pair',
|
|
1199
1237
|
'generate-recovery-code',
|
|
1200
1238
|
]);
|
|
1239
|
+
/**
|
|
1240
|
+
* Why this invocation must borrow instead of adopting a CLI session of its
|
|
1241
|
+
* own, or null when it may adopt (OSK-13254).
|
|
1242
|
+
*/
|
|
1243
|
+
function cliSessionAdoptionBlocker(cliConfig, env = process.env) {
|
|
1244
|
+
if ((0, harness_context_1.isHarnessCliContext)(env))
|
|
1245
|
+
return 'harness_context';
|
|
1246
|
+
if (env.SKRR_NO_CLI_SESSION_ADOPTION === '1')
|
|
1247
|
+
return 'opted_out';
|
|
1248
|
+
// `skrr logout` said "this CLI is signed out". The daemon may still LEND
|
|
1249
|
+
// tokens (logout says so), but a durable session does not reappear behind
|
|
1250
|
+
// that statement; the next `skrr login` clears the flag.
|
|
1251
|
+
if (cliConfig.adoptDaemonSession === false)
|
|
1252
|
+
return 'signed_out';
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Failures that mean no daemon answered (or answered for another server) —
|
|
1257
|
+
* a second attempt on the token route cannot do better and would only wait
|
|
1258
|
+
* out the restart window twice.
|
|
1259
|
+
*/
|
|
1260
|
+
function daemonDidNotAnswer(failure) {
|
|
1261
|
+
switch (failure.reason) {
|
|
1262
|
+
case 'no_bootstrap':
|
|
1263
|
+
case 'stale_bootstrap':
|
|
1264
|
+
case 'bootstrap_parse':
|
|
1265
|
+
case 'base_url_mismatch':
|
|
1266
|
+
case 'network':
|
|
1267
|
+
case 'timeout':
|
|
1268
|
+
return true;
|
|
1269
|
+
default:
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
/** The adoption lock: one per config root and profile. */
|
|
1274
|
+
function adoptionLockPath(profile) {
|
|
1275
|
+
const safe = profile.replace(/[^A-Za-z0-9._-]/g, '_') || 'default';
|
|
1276
|
+
return path.join((0, config_1.configRoot)(), `cli-session-adopt.${safe}.lock`);
|
|
1277
|
+
}
|
|
1278
|
+
function storedAccessToken(baseURL) {
|
|
1279
|
+
try {
|
|
1280
|
+
return (0, auth_storage_1.readFromBackend)(baseURL).bundle?.token ?? null;
|
|
1281
|
+
}
|
|
1282
|
+
catch {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Adopt a CLI session through the local daemon (OSK-13254): the durable mint
|
|
1288
|
+
* (`/v1/auth/cli-handoff` → `POST /api/daemons/cli-handoff`), persisted like a
|
|
1289
|
+
* browser login, so that the next `skrr` reads it from the Keychain/file and
|
|
1290
|
+
* renews it itself — the daemon restarting no longer reads as signed out.
|
|
1291
|
+
*
|
|
1292
|
+
* Serialized across processes. The confirm that follows a mint retires every
|
|
1293
|
+
* OTHER live family of this cliId, so two concurrent adopters would each
|
|
1294
|
+
* revoke the other's session mid-command. Under the lock a process that
|
|
1295
|
+
* finds a session a sibling stored while it waited uses that one instead.
|
|
1296
|
+
*
|
|
1297
|
+
* Never worse than borrowing: if the lock cannot be taken in time, or the
|
|
1298
|
+
* daemon answered but would not mint (rate limit, a server refusal, a
|
|
1299
|
+
* broker-only descriptor), this borrows an access token for this process
|
|
1300
|
+
* exactly as before.
|
|
1301
|
+
*/
|
|
1302
|
+
async function adoptCliSessionThroughDaemon(base, lockRetries) {
|
|
1303
|
+
const profile = base.profile ?? 'default';
|
|
1304
|
+
const baseURL = base.baseURL ?? '';
|
|
1305
|
+
const borrow = () => attemptDaemonBrokerLoginAndPersist({
|
|
1306
|
+
...base,
|
|
1307
|
+
preferHandoffMode: 'access_token',
|
|
1308
|
+
borrowOnly: true,
|
|
1309
|
+
});
|
|
1310
|
+
// Adopt only from a live descriptor that can mint a family. One that
|
|
1311
|
+
// cannot (the Dedicated guest's access-token-only shape), or none at all (a
|
|
1312
|
+
// daemon that is down or restarting), has nothing to adopt: borrow without
|
|
1313
|
+
// taking the lock, so concurrent commands never queue behind it and the
|
|
1314
|
+
// borrow path's own restart wait and outage reporting apply unchanged.
|
|
1315
|
+
const usable = listUsableBootstraps(base);
|
|
1316
|
+
if (!usable.some(({ bootstrap }) => (0, cli_handoff_wire_1.advertisedHandoffModes)(bootstrap).includes('refresh_family'))) {
|
|
1317
|
+
return { outcome: await borrow() };
|
|
1318
|
+
}
|
|
1319
|
+
const before = storedAccessToken(baseURL);
|
|
1320
|
+
const lockPath = adoptionLockPath(profile);
|
|
1321
|
+
let release = null;
|
|
1322
|
+
try {
|
|
1323
|
+
fs.mkdirSync(path.dirname(lockPath), { recursive: true, mode: 0o700 });
|
|
1324
|
+
if (!fs.existsSync(lockPath))
|
|
1325
|
+
fs.writeFileSync(lockPath, '', { mode: 0o600 });
|
|
1326
|
+
release = await proper_lockfile_1.default.lock(lockPath, {
|
|
1327
|
+
// Longer than one mint (20s loopback + 10s confirm), so a holder that
|
|
1328
|
+
// is still working is never judged stale.
|
|
1329
|
+
stale: 60_000,
|
|
1330
|
+
retries: { retries: lockRetries, minTimeout: 250, maxTimeout: 1_000, factor: 1.5 },
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
catch {
|
|
1334
|
+
// Could not serialize: do not race a sibling's mint. Borrow.
|
|
1335
|
+
return { outcome: await borrow() };
|
|
1336
|
+
}
|
|
1337
|
+
let durable;
|
|
1338
|
+
try {
|
|
1339
|
+
const now = storedAccessToken(baseURL);
|
|
1340
|
+
if (now && now !== before)
|
|
1341
|
+
return { adoptedBySibling: true };
|
|
1342
|
+
durable = await attemptDaemonBrokerLoginAndPersist({
|
|
1343
|
+
...base,
|
|
1344
|
+
preferHandoffMode: 'refresh_family',
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
finally {
|
|
1348
|
+
await release().catch(() => undefined);
|
|
1349
|
+
}
|
|
1350
|
+
if (durable.ok || daemonDidNotAnswer(durable))
|
|
1351
|
+
return { outcome: durable };
|
|
1352
|
+
// The daemon answered but would not mint a family. The token route is a
|
|
1353
|
+
// different door with different limits; take it for this process.
|
|
1354
|
+
return { outcome: await borrow() };
|
|
1355
|
+
}
|
|
1201
1356
|
/**
|
|
1202
1357
|
* Auto-broker pre-flight for `BaseCommand.init()`. When the credential
|
|
1203
1358
|
* resolver returns no token, this attempts a silent daemon broker so
|
|
@@ -1243,20 +1398,43 @@ async function maybeAutoBroker(opts) {
|
|
|
1243
1398
|
// confirm cannot retire another profile's session. Persisted by the caller on
|
|
1244
1399
|
// success so the rotation row stays bound across processes.
|
|
1245
1400
|
const { cliId, updatedConfig } = (0, cli_identity_1.planLoginCliId)(opts.cliConfig);
|
|
1246
|
-
const
|
|
1401
|
+
const base = {
|
|
1247
1402
|
cliId,
|
|
1248
1403
|
baseURL: opts.cliConfig.baseURL,
|
|
1249
|
-
|
|
1250
|
-
// advertises it: nothing credential-shaped is persisted, and the next
|
|
1251
|
-
// `skrr` process simply re-redeems over loopback. The legacy refresh-
|
|
1252
|
-
// family mint remains the fallback for v1 descriptors and the redeem
|
|
1253
|
-
// path for explicit `skrr login`.
|
|
1254
|
-
preferHandoffMode: 'access_token',
|
|
1404
|
+
profile: opts.profile ?? (0, keychain_1.getActiveProfile)(),
|
|
1255
1405
|
...(opts.bootstrapPathOverride ? { bootstrapPathOverride: opts.bootstrapPathOverride } : {}),
|
|
1406
|
+
};
|
|
1407
|
+
const harness = (0, harness_context_1.isHarnessCliContext)();
|
|
1408
|
+
// OSK-13254 — a person's first `skrr` on a machine whose daemon is signed in
|
|
1409
|
+
// ADOPTS a CLI session of its own: a separate cli-scope family, minted by the
|
|
1410
|
+
// daemon's hand-off and stored like a browser login, so later commands do
|
|
1411
|
+
// not depend on the daemon being up. Everything else borrows a short-lived
|
|
1412
|
+
// token for this process, which is what the automatic path always did
|
|
1413
|
+
// (OSK-12018): a process the daemon started for an agent or tool, a 401
|
|
1414
|
+
// recovery, a profile signed out with `skrr logout`, and the opt-out.
|
|
1415
|
+
if (opts.adopt && !cliSessionAdoptionBlocker(opts.cliConfig)) {
|
|
1416
|
+
const adopted = await adoptCliSessionThroughDaemon(base, opts.adoptionLockRetries ?? 40);
|
|
1417
|
+
if (adopted.adoptedBySibling)
|
|
1418
|
+
return { triggered: true, adoptedBySibling: true };
|
|
1419
|
+
const outcome = adopted.outcome;
|
|
1420
|
+
return {
|
|
1421
|
+
triggered: true,
|
|
1422
|
+
outcome,
|
|
1423
|
+
...(updatedConfig && outcome.ok ? { updatedConfig } : {}),
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
const outcome = await attemptDaemonBrokerLoginAndPersist({
|
|
1427
|
+
...base,
|
|
1428
|
+
// Borrow: the access-token mode, held in memory for this process; the next
|
|
1429
|
+
// `skrr` re-redeems over loopback. A v1 descriptor still takes the durable
|
|
1430
|
+
// mint here (the compatibility window in `brokerThrough`), except for a
|
|
1431
|
+
// harness-context process, which never stores a sign-in.
|
|
1432
|
+
preferHandoffMode: 'access_token',
|
|
1433
|
+
...(harness ? { borrowOnly: true } : {}),
|
|
1256
1434
|
});
|
|
1257
1435
|
return {
|
|
1258
1436
|
triggered: true,
|
|
1259
1437
|
outcome,
|
|
1260
|
-
...(updatedConfig && outcome.ok ? { updatedConfig } : {}),
|
|
1438
|
+
...(updatedConfig && outcome.ok && !harness ? { updatedConfig } : {}),
|
|
1261
1439
|
};
|
|
1262
1440
|
}
|
|
@@ -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;
|