@skrr-ai/cli 0.1.80 → 0.1.81

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.
Files changed (80) hide show
  1. package/dist/base-command.d.ts +4 -0
  2. package/dist/base-command.js +31 -2
  3. package/dist/commands/agents/visibility.d.ts +14 -0
  4. package/dist/commands/agents/visibility.js +52 -0
  5. package/dist/commands/create-token.d.ts +0 -1
  6. package/dist/commands/create-token.js +3 -1
  7. package/dist/commands/list-tokens.d.ts +0 -1
  8. package/dist/commands/list-tokens.js +1 -1
  9. package/dist/commands/machines/dedicated/ssh-proxy.js +1 -0
  10. package/dist/commands/revoke-token.d.ts +0 -1
  11. package/dist/commands/revoke-token.js +1 -1
  12. package/dist/commands/store/follow.d.ts +1 -0
  13. package/dist/commands/store/follow.js +7 -1
  14. package/dist/commands/store/follows/list.d.ts +11 -0
  15. package/dist/commands/store/follows/list.js +29 -0
  16. package/dist/commands/store/follows/remove.d.ts +16 -0
  17. package/dist/commands/store/follows/remove.js +37 -0
  18. package/dist/commands/store/publishers/profile/draft.d.ts +17 -0
  19. package/dist/commands/store/publishers/profile/draft.js +54 -0
  20. package/dist/commands/store/publishers/profile/handle.d.ts +14 -0
  21. package/dist/commands/store/publishers/profile/handle.js +39 -0
  22. package/dist/commands/store/publishers/profile/hide.d.ts +12 -0
  23. package/dist/commands/store/publishers/profile/hide.js +39 -0
  24. package/dist/commands/store/publishers/profile/preview.d.ts +11 -0
  25. package/dist/commands/store/publishers/profile/preview.js +39 -0
  26. package/dist/commands/store/publishers/profile/publish.d.ts +12 -0
  27. package/dist/commands/store/publishers/profile/publish.js +41 -0
  28. package/dist/commands/store/publishers/profile/restore.d.ts +15 -0
  29. package/dist/commands/store/publishers/profile/restore.js +49 -0
  30. package/dist/commands/store/publishers/profile/revisions.d.ts +11 -0
  31. package/dist/commands/store/publishers/profile/revisions.js +30 -0
  32. package/dist/commands/store/publishers/profile/show.d.ts +11 -0
  33. package/dist/commands/store/publishers/profile/show.js +39 -0
  34. package/dist/commands/store/review.d.ts +26 -0
  35. package/dist/commands/store/review.js +93 -0
  36. package/dist/commands/store/reviews.d.ts +29 -0
  37. package/dist/commands/store/reviews.js +170 -0
  38. package/dist/commands/store/tip.d.ts +20 -0
  39. package/dist/commands/store/tip.js +57 -0
  40. package/dist/commands/tokens/create.d.ts +28 -0
  41. package/dist/commands/tokens/create.js +136 -0
  42. package/dist/commands/tokens/list.d.ts +11 -0
  43. package/dist/commands/tokens/list.js +53 -0
  44. package/dist/commands/tokens/revoke.d.ts +14 -0
  45. package/dist/commands/tokens/revoke.js +42 -0
  46. package/dist/lib/access-tokens.d.ts +59 -0
  47. package/dist/lib/access-tokens.js +50 -0
  48. package/dist/lib/auth-core-init.d.ts +6 -0
  49. package/dist/lib/auth-core-init.js +28 -6
  50. package/dist/lib/credential-resolver.d.ts +1 -1
  51. package/dist/lib/credential-resolver.js +8 -2
  52. package/dist/lib/daemonBroker.d.ts +13 -2
  53. package/dist/lib/daemonBroker.js +6 -4
  54. package/dist/lib/dedicated-ssh.d.ts +9 -0
  55. package/dist/lib/dedicated-ssh.js +232 -63
  56. package/dist/lib/node-adapter.js +2 -2
  57. package/dist/lib/publisher-profile-flags.d.ts +19 -0
  58. package/dist/lib/publisher-profile-flags.js +48 -0
  59. package/dist/lib/refresh.d.ts +1 -1
  60. package/dist/lib/refresh.js +6 -1
  61. package/dist/lib/ssh-ordered-bytes.d.ts +8 -0
  62. package/dist/lib/ssh-ordered-bytes.js +31 -0
  63. package/dist/lib/usd-micros.d.ts +9 -0
  64. package/dist/lib/usd-micros.js +21 -0
  65. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.d.ts +22 -3
  66. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.js +29 -5
  67. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +7 -1
  68. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +7 -1
  69. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  70. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +7 -5
  71. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.d.ts +22 -3
  72. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.js +27 -4
  73. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +7 -1
  74. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +7 -1
  75. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  76. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  77. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  78. package/dist/node_modules/@skrr-ai/data-provider/index.js +3857 -3859
  79. package/oclif.manifest.json +38008 -36666
  80. package/package.json +28 -4
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../base-command");
6
+ const access_tokens_1 = require("../../lib/access-tokens");
7
+ const api_fetch_1 = require("../../lib/api-fetch");
8
+ const prompt_1 = require("../../lib/prompt");
9
+ /**
10
+ * `skrr tokens create` — mint a personal access token (`osk_pat_*`, OSK-13208)
11
+ * for CI. The secret is printed EXACTLY ONCE.
12
+ *
13
+ * Creating one needs a sign-in session (a token cannot mint a token) and, for
14
+ * an account with two-factor authentication, a fresh authenticator code from
15
+ * that session. `--otp` / `--backup-code` supply it up front; at a terminal the
16
+ * command asks for it when the server does.
17
+ */
18
+ class TokensCreate extends base_command_1.BaseCommand {
19
+ static description = 'Create a personal access token for CI (use it as SKRR_TOKEN). The secret is shown once and never again.';
20
+ static examples = [
21
+ '<%= config.bin %> tokens create github-actions',
22
+ '<%= config.bin %> tokens create deploy --scope write --expires-in-days 30',
23
+ '<%= config.bin %> tokens create ci --scope write --otp 123456 --json',
24
+ ];
25
+ static args = {
26
+ name: core_1.Args.string({
27
+ description: 'Label for the token, e.g. the CI secret it will live in (also --name)',
28
+ ignoreStdin: true,
29
+ }),
30
+ };
31
+ static flags = {
32
+ name: core_1.Flags.string({ description: 'Label for the token (1-64 chars)' }),
33
+ scope: core_1.Flags.string({
34
+ description: 'read: GET requests only. write: everything the CLI can do as you, except sign-in, credential, account-deletion, admin and payment actions',
35
+ options: ['read', 'write'],
36
+ default: 'read',
37
+ }),
38
+ 'expires-in-days': core_1.Flags.integer({
39
+ description: 'Days until the token expires (1-365)',
40
+ min: 1,
41
+ max: 365,
42
+ default: 90,
43
+ }),
44
+ otp: core_1.Flags.string({
45
+ description: 'Current code from your authenticator app, if your account uses two-factor',
46
+ exclusive: ['backup-code'],
47
+ }),
48
+ 'backup-code': core_1.Flags.string({
49
+ description: 'One of your unused backup codes (it is spent)',
50
+ exclusive: ['otp'],
51
+ }),
52
+ json: core_1.Flags.boolean({ description: 'Output as JSON (includes the secret)' }),
53
+ };
54
+ async run() {
55
+ this.requireAuth();
56
+ const { args, flags } = await this.parse(TokensCreate);
57
+ const name = flags.name || args.name;
58
+ if (!name) {
59
+ this.error('A name is required — pass it positionally or with --name.', { exit: 1 });
60
+ }
61
+ const input = {
62
+ name,
63
+ scopes: [flags.scope],
64
+ expiresInDays: flags['expires-in-days'],
65
+ };
66
+ let code = null;
67
+ if (flags.otp)
68
+ code = { token: flags.otp.trim() };
69
+ else if (flags['backup-code'])
70
+ code = { backupCode: flags['backup-code'].trim() };
71
+ if (code)
72
+ await this.verify(code);
73
+ let created;
74
+ try {
75
+ created = await (0, access_tokens_1.createAccessToken)(input);
76
+ }
77
+ catch (err) {
78
+ const challenge = stepUpChallenge(err);
79
+ if (!challenge)
80
+ return this.handleApiError(err);
81
+ if (code || challenge.reason !== 'no_recent_step_up') {
82
+ return this.refuseStepUp(challenge);
83
+ }
84
+ if (!process.stdin.isTTY) {
85
+ return this.refuseStepUp(challenge);
86
+ }
87
+ const entered = (await (0, prompt_1.prompt)('Code from your authenticator app: ')).trim();
88
+ code = { token: entered };
89
+ await this.verify(code);
90
+ try {
91
+ created = await (0, access_tokens_1.createAccessToken)(input);
92
+ }
93
+ catch (retryErr) {
94
+ return this.handleApiError(retryErr);
95
+ }
96
+ }
97
+ if (flags.json) {
98
+ this.log(JSON.stringify(created, null, 2));
99
+ return;
100
+ }
101
+ const t = created.accessToken;
102
+ this.log('');
103
+ this.log(` ${created.token}`);
104
+ this.log('');
105
+ this.log('This is the only time the token will be displayed. Store it as a CI secret now.');
106
+ this.log(`Use it as: SKRR_TOKEN=<token> ${this.config.bin} <command>`);
107
+ this.log('');
108
+ this.log(` id ${t.id}`);
109
+ this.log(` name ${t.name}`);
110
+ this.log(` scopes ${t.scopes.join(', ')}`);
111
+ this.log(` expires ${t.expiresAt ?? '(unknown)'}`);
112
+ }
113
+ async verify(code) {
114
+ try {
115
+ await data_provider_1.dataService.verifyStepUp({ purpose: data_provider_1.ACCESS_TOKEN_CREATE_STEP_UP_PURPOSE, ...code });
116
+ }
117
+ catch (err) {
118
+ this.handleApiError(err);
119
+ }
120
+ }
121
+ refuseStepUp(challenge) {
122
+ const bin = this.config.bin;
123
+ const messages = {
124
+ credential_not_session_bound: `Creating an access token needs a sign-in session, and this credential is not one (an access token cannot create another). Run \`${bin} login\` and try again. Nothing changed.`,
125
+ two_factor_not_enabled: 'Creating an access token needs two-factor authentication on this account. Turn it on in the web app, then try again. Nothing changed.',
126
+ no_recent_step_up: 'Creating an access token needs a fresh code from your authenticator app. Run this command again with --otp <code> (or --backup-code <code>). Nothing changed.',
127
+ };
128
+ this.error(messages[challenge.reason], { code: 'STEP_UP_REQUIRED', exit: 2 });
129
+ }
130
+ }
131
+ exports.default = TokensCreate;
132
+ function stepUpChallenge(err) {
133
+ if (!(err instanceof api_fetch_1.ApiFetchError) || err.status !== 403)
134
+ return null;
135
+ return (0, data_provider_1.stepUpChallengeFromError)(err);
136
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /** `skrr tokens list` — your personal access tokens (never their secrets). */
3
+ export default class TokensList extends BaseCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../base-command");
5
+ const access_tokens_1 = require("../../lib/access-tokens");
6
+ /** `skrr tokens list` — your personal access tokens (never their secrets). */
7
+ class TokensList extends base_command_1.BaseCommand {
8
+ static description = 'List your personal access tokens (secrets are never shown again)';
9
+ static examples = [
10
+ '<%= config.bin %> tokens list',
11
+ '<%= config.bin %> tokens list --all',
12
+ '<%= config.bin %> tokens list --json',
13
+ ];
14
+ static flags = {
15
+ all: core_1.Flags.boolean({ description: 'Include revoked and expired tokens' }),
16
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
17
+ };
18
+ async run() {
19
+ this.requireAuth();
20
+ const { flags } = await this.parse(TokensList);
21
+ let tokens;
22
+ try {
23
+ tokens = await (0, access_tokens_1.listAccessTokens)();
24
+ }
25
+ catch (err) {
26
+ this.handleApiError(err);
27
+ return;
28
+ }
29
+ const shown = flags.all ? tokens : tokens.filter((t) => t.state === 'active');
30
+ if (flags.json) {
31
+ this.log(JSON.stringify({ accessTokens: shown }, null, 2));
32
+ return;
33
+ }
34
+ if (shown.length === 0) {
35
+ this.log(flags.all || tokens.length === 0
36
+ ? `No access tokens. Create one with \`${this.config.bin} tokens create <name>\`.`
37
+ : `No active access tokens (${tokens.length} revoked or expired; --all shows them).`);
38
+ return;
39
+ }
40
+ for (const t of shown) {
41
+ this.log([
42
+ t.id,
43
+ t.name,
44
+ `…${t.lastFour}`,
45
+ t.scopes.join('+'),
46
+ (0, access_tokens_1.describeAccessTokenState)(t),
47
+ `expires ${t.expiresAt ?? '?'}`,
48
+ `last used ${t.lastUsedAt ?? 'never'}`,
49
+ ].join(' '));
50
+ }
51
+ }
52
+ }
53
+ exports.default = TokensList;
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /** `skrr tokens revoke <id>` — irreversible; a revoked token is replaced, never re-enabled. */
3
+ export default class TokensRevoke extends BaseCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static args: {
7
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ compromised: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../base-command");
5
+ const access_tokens_1 = require("../../lib/access-tokens");
6
+ /** `skrr tokens revoke <id>` — irreversible; a revoked token is replaced, never re-enabled. */
7
+ class TokensRevoke extends base_command_1.BaseCommand {
8
+ static description = 'Revoke a personal access token. Anything still presenting it is refused on its next request.';
9
+ static examples = [
10
+ '<%= config.bin %> tokens revoke <token-id>',
11
+ '<%= config.bin %> tokens revoke <token-id> --compromised',
12
+ ];
13
+ static args = {
14
+ id: core_1.Args.string({
15
+ description: 'Token id (from `tokens list`)',
16
+ required: true,
17
+ ignoreStdin: true,
18
+ }),
19
+ };
20
+ static flags = {
21
+ compromised: core_1.Flags.boolean({ description: 'Record that the token leaked' }),
22
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
23
+ };
24
+ async run() {
25
+ this.requireAuth();
26
+ const { args, flags } = await this.parse(TokensRevoke);
27
+ try {
28
+ const res = await (0, access_tokens_1.revokeAccessToken)(args.id, { compromised: flags.compromised });
29
+ if (flags.json) {
30
+ this.log(JSON.stringify(res, null, 2));
31
+ return;
32
+ }
33
+ this.log(res.alreadyRevoked
34
+ ? `${args.id} was already revoked.`
35
+ : `Revoked ${args.id}. Anything still presenting it will now be refused.`);
36
+ }
37
+ catch (err) {
38
+ this.handleApiError(err);
39
+ }
40
+ }
41
+ }
42
+ exports.default = TokensRevoke;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * access-tokens.ts — request client for `/api/user/access-tokens`, the
3
+ * personal access tokens (`osk_pat_*`, OSK-13208) behind `skrr tokens`.
4
+ *
5
+ * A personal access token is how CI runs `skrr` AS a person: set it as
6
+ * `SKRR_TOKEN` and every command authenticates with it, never refreshing,
7
+ * until it expires or is revoked. It is not `skrr create-token` (an `osk_ci_`
8
+ * DAEMON bootstrap token), not `skrr token mint` (a rotating refresh bundle),
9
+ * and not `skrr api-keys` (Space-scoped Managed-Agents keys).
10
+ *
11
+ * Path literals are inline on purpose: `domain-api-parity` resolves a command
12
+ * to an endpoint by reading these strings.
13
+ */
14
+ /** A token as the API returns it. The secret is never part of this shape. */
15
+ export interface AccessToken {
16
+ id: string;
17
+ name: string;
18
+ lastFour: string;
19
+ scopes: string[];
20
+ /** Derived server-side: natural expiry is not stored as a revocation. */
21
+ state: 'active' | 'expired' | 'revoked';
22
+ createdAt: string | null;
23
+ expiresAt: string | null;
24
+ lastUsedAt: string | null;
25
+ revokedAt: string | null;
26
+ revokedReason: string | null;
27
+ createdFromIp: string | null;
28
+ createdFromUserAgent: string | null;
29
+ }
30
+ /** The create response. `token` is shown exactly once and never again. */
31
+ export interface AccessTokenCreated {
32
+ token: string;
33
+ accessToken: AccessToken;
34
+ }
35
+ export interface CreateAccessTokenInput {
36
+ name: string;
37
+ scopes?: string[];
38
+ expiresInDays?: number;
39
+ }
40
+ /**
41
+ * The words for each stored revocation reason. Every member of the schema's
42
+ * `revokedReason` enum appears here, so the list shows WHY a token died —
43
+ * "revoked" alone cannot tell a deliberate revoke from a sign-out sweep.
44
+ */
45
+ export declare const ACCESS_TOKEN_REVOKED_REASON_LABELS: Record<string, string>;
46
+ export declare function createAccessToken(input: CreateAccessTokenInput): Promise<AccessTokenCreated>;
47
+ /** Every token the caller owns, newest first, including revoked and expired rows. */
48
+ export declare function listAccessTokens(): Promise<AccessToken[]>;
49
+ export interface RevokeAccessTokenResult {
50
+ revoked: boolean;
51
+ alreadyRevoked: boolean;
52
+ accessToken?: AccessToken;
53
+ }
54
+ /** Revoke one token. Irreversible: a revoked token is replaced, never re-enabled. */
55
+ export declare function revokeAccessToken(id: string, opts?: {
56
+ compromised?: boolean;
57
+ }): Promise<RevokeAccessTokenResult>;
58
+ /** One-line status for a listed token. */
59
+ export declare function describeAccessTokenState(token: AccessToken): string;
@@ -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, cannot open it, and mints a new keypair. It
137
- // logs that through its own `[deviceIdentity]` prefix, which reached the
138
- // terminal as a raw `[auth-core warn]` line right under the notice. The
139
- // notice covers it; without a key change it is a real fault and prints.
140
- if (keyChanged && message === DEVICE_KEY_DECRYPT_FAILED)
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
  }
@@ -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='ci' detected)
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='ci' detected)
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>
@@ -78,6 +78,9 @@ async function resolveCredential(opts = {}) {
78
78
  flagToken: opts.flagToken,
79
79
  bareMode,
80
80
  storeMode,
81
+ // SKRR_TOKEN is the name the CI recipe uses (OSK-13208); OVERSKY_TOKEN still
82
+ // wins when both are set, as in the config overlay (config.ts).
83
+ envTokenNames: ['OVERSKY_TOKEN', 'SKRR_TOKEN'],
81
84
  // CLI never has an interactive slot — users run `skrr login` explicitly.
82
85
  isHeadless: () => true,
83
86
  // Stored credentials are only usable against the origin that issued
@@ -160,7 +163,10 @@ function formatCredentialSource(source) {
160
163
  case 'flag':
161
164
  return 'CLI --token flag';
162
165
  case 'env-token':
163
- return 'OVERSKY_TOKEN';
166
+ // Name the variable that actually supplied it (OVERSKY_TOKEN wins).
167
+ return !process.env.OVERSKY_TOKEN?.trim() && process.env.SKRR_TOKEN?.trim()
168
+ ? 'SKRR_TOKEN'
169
+ : 'OVERSKY_TOKEN';
164
170
  case 'auth-helper':
165
171
  return 'OVERSKY_AUTH_HELPER';
166
172
  case 'fd-handoff':
@@ -1,5 +1,5 @@
1
1
  /**
2
- * daemonBroker.ts — Sky CLI client for the local `oversky` daemon's
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 Sky CLI's currently-configured baseURL (e.g. `https://oversky.ai`).
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
@@ -461,6 +461,17 @@ export interface MaybeAutoBrokerOptions {
461
461
  * everywhere else. Production callers omit it and behave exactly as before.
462
462
  */
463
463
  bootstrapPathOverride?: string;
464
+ /**
465
+ * The profile whose daemon may be borrowed. Defaults to the active profile.
466
+ *
467
+ * A silent hand-off must never cross profiles (OSK-13257). The `default`
468
+ * candidate list falls through to any running daemon, and every daemon is
469
+ * signed in as whoever started it — so `--profile buyer`, with its own
470
+ * session refused, used to run as the machine owner after one stderr line.
471
+ * A named profile now borrows only a daemon started under that same
472
+ * profile, and otherwise reports NOT_SIGNED_IN for the profile it was given.
473
+ */
474
+ profile?: string;
464
475
  }
465
476
  export interface MaybeAutoBrokerResult {
466
477
  /**
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * daemonBroker.ts — Sky CLI client for the local `oversky` daemon's
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,
@@ -101,6 +101,7 @@ const auth_storage_1 = require("./auth-storage");
101
101
  const config_1 = require("./config");
102
102
  const cli_identity_1 = require("./cli-identity");
103
103
  const daemonRestartWait_1 = require("./daemonRestartWait");
104
+ const keychain_1 = require("./keychain");
104
105
  const REQUEST_TIMEOUT_MS = 20_000;
105
106
  /**
106
107
  * Normalize a base URL for equality comparison. Mirrors the daemon-side
@@ -247,9 +248,9 @@ exports.HOSTED_MACHINE_CLI_HANDOFF_DESCRIPTOR = '/run/skrr-hosted-machine/cli-ha
247
248
  /** Where the cli-handoff broker looks, in order. */
248
249
  function resolveCliHandoffCandidatePaths(profile = 'default') {
249
250
  const candidates = resolveBootstrapCandidatePaths(profile);
250
- // Any profile: a guest runs one daemon, and the auto-broker resolves the
251
- // default profile regardless of `--profile`, so a narrower condition here would
252
- // only describe behaviour production never reaches.
251
+ // Any profile: a guest runs one daemon holding the one account the guest
252
+ // serves, so every profile on it is that account. On a laptop a named profile
253
+ // reaches only its own island file above (OSK-13257).
253
254
  if (process.platform === 'linux') {
254
255
  candidates.push(exports.DEDICATED_RUNTIME_CLI_HANDOFF_DESCRIPTOR, exports.HOSTED_MACHINE_CLI_HANDOFF_DESCRIPTOR);
255
256
  }
@@ -1252,6 +1253,7 @@ async function maybeAutoBroker(opts) {
1252
1253
  // family mint remains the fallback for v1 descriptors and the redeem
1253
1254
  // path for explicit `skrr login`.
1254
1255
  preferHandoffMode: 'access_token',
1256
+ profile: opts.profile ?? (0, keychain_1.getActiveProfile)(),
1255
1257
  ...(opts.bootstrapPathOverride ? { bootstrapPathOverride: opts.bootstrapPathOverride } : {}),
1256
1258
  });
1257
1259
  return {
@@ -104,6 +104,10 @@ export declare function installSshConfigBlock(home?: string): {
104
104
  changed: boolean;
105
105
  };
106
106
  export interface SshStartAck {
107
+ failed?: boolean;
108
+ closing?: boolean;
109
+ closed?: boolean;
110
+ retryable?: boolean;
107
111
  channelId?: string;
108
112
  ok?: boolean;
109
113
  error?: string;
@@ -113,6 +117,9 @@ export interface SshStartAck {
113
117
  key: string;
114
118
  }[];
115
119
  receiverAckV1?: boolean;
120
+ resumeV2?: boolean;
121
+ epoch?: string;
122
+ generation?: number;
116
123
  }
117
124
  export interface SshProxyOptions {
118
125
  baseURL: string;
@@ -123,6 +130,8 @@ export interface SshProxyOptions {
123
130
  /** Diagnostics; never stdout — ssh reads stdout as protocol bytes. */
124
131
  errorOutput: NodeJS.WritableStream;
125
132
  ttlSeconds?: number;
133
+ getToken?: () => Promise<string>;
134
+ recoveryMs?: number;
126
135
  /** Injected for tests. */
127
136
  connect?: (url: string, opts: Parameters<typeof io>[1]) => Socket;
128
137
  /** Injected for tests. */