@skrr-ai/cli 0.1.79 → 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 (114) hide show
  1. package/dist/base-command.d.ts +4 -0
  2. package/dist/base-command.js +40 -4
  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/inbox/index.js +4 -2
  8. package/dist/commands/list-tokens.d.ts +0 -1
  9. package/dist/commands/list-tokens.js +1 -1
  10. package/dist/commands/login.d.ts +8 -3
  11. package/dist/commands/login.js +14 -4
  12. package/dist/commands/machines/dedicated/ssh-proxy.js +1 -0
  13. package/dist/commands/revoke-token.d.ts +0 -1
  14. package/dist/commands/revoke-token.js +1 -1
  15. package/dist/commands/spaces/workflows/events.js +9 -2
  16. package/dist/commands/store/follow.d.ts +1 -0
  17. package/dist/commands/store/follow.js +7 -1
  18. package/dist/commands/store/follows/list.d.ts +11 -0
  19. package/dist/commands/store/follows/list.js +29 -0
  20. package/dist/commands/store/follows/remove.d.ts +16 -0
  21. package/dist/commands/store/follows/remove.js +37 -0
  22. package/dist/commands/store/publishers/profile/draft.d.ts +17 -0
  23. package/dist/commands/store/publishers/profile/draft.js +54 -0
  24. package/dist/commands/store/publishers/profile/handle.d.ts +14 -0
  25. package/dist/commands/store/publishers/profile/handle.js +39 -0
  26. package/dist/commands/store/publishers/profile/hide.d.ts +12 -0
  27. package/dist/commands/store/publishers/profile/hide.js +39 -0
  28. package/dist/commands/store/publishers/profile/preview.d.ts +11 -0
  29. package/dist/commands/store/publishers/profile/preview.js +39 -0
  30. package/dist/commands/store/publishers/profile/publish.d.ts +12 -0
  31. package/dist/commands/store/publishers/profile/publish.js +41 -0
  32. package/dist/commands/store/publishers/profile/restore.d.ts +15 -0
  33. package/dist/commands/store/publishers/profile/restore.js +49 -0
  34. package/dist/commands/store/publishers/profile/revisions.d.ts +11 -0
  35. package/dist/commands/store/publishers/profile/revisions.js +30 -0
  36. package/dist/commands/store/publishers/profile/show.d.ts +11 -0
  37. package/dist/commands/store/publishers/profile/show.js +39 -0
  38. package/dist/commands/store/review.d.ts +26 -0
  39. package/dist/commands/store/review.js +93 -0
  40. package/dist/commands/store/reviews.d.ts +29 -0
  41. package/dist/commands/store/reviews.js +170 -0
  42. package/dist/commands/store/tip.d.ts +20 -0
  43. package/dist/commands/store/tip.js +57 -0
  44. package/dist/commands/tokens/create.d.ts +28 -0
  45. package/dist/commands/tokens/create.js +136 -0
  46. package/dist/commands/tokens/list.d.ts +11 -0
  47. package/dist/commands/tokens/list.js +53 -0
  48. package/dist/commands/tokens/revoke.d.ts +14 -0
  49. package/dist/commands/tokens/revoke.js +42 -0
  50. package/dist/lib/access-tokens.d.ts +59 -0
  51. package/dist/lib/access-tokens.js +50 -0
  52. package/dist/lib/agentic-stream.js +13 -4
  53. package/dist/lib/api-fetch.js +6 -2
  54. package/dist/lib/auth-core-init.d.ts +6 -0
  55. package/dist/lib/auth-core-init.js +28 -6
  56. package/dist/lib/brokered-dpop.d.ts +34 -0
  57. package/dist/lib/brokered-dpop.js +89 -0
  58. package/dist/lib/credential-resolver.d.ts +1 -1
  59. package/dist/lib/credential-resolver.js +8 -2
  60. package/dist/lib/daemonBroker.d.ts +84 -4
  61. package/dist/lib/daemonBroker.js +209 -40
  62. package/dist/lib/daemonBrokerRefusal.d.ts +26 -0
  63. package/dist/lib/daemonBrokerRefusal.js +83 -4
  64. package/dist/lib/daemonRestartWait.d.ts +39 -0
  65. package/dist/lib/daemonRestartWait.js +181 -0
  66. package/dist/lib/dedicated-service.js +10 -5
  67. package/dist/lib/dedicated-ssh.d.ts +9 -0
  68. package/dist/lib/dedicated-ssh.js +235 -63
  69. package/dist/lib/dedicated-terminal.js +6 -3
  70. package/dist/lib/dpop-auth.d.ts +35 -0
  71. package/dist/lib/dpop-auth.js +57 -0
  72. package/dist/lib/login.d.ts +6 -0
  73. package/dist/lib/login.js +13 -4
  74. package/dist/lib/node-adapter.js +6 -2
  75. package/dist/lib/publisher-profile-flags.d.ts +19 -0
  76. package/dist/lib/publisher-profile-flags.js +48 -0
  77. package/dist/lib/refresh.d.ts +1 -1
  78. package/dist/lib/refresh.js +6 -1
  79. package/dist/lib/ssh-ordered-bytes.d.ts +8 -0
  80. package/dist/lib/ssh-ordered-bytes.js +31 -0
  81. package/dist/lib/usd-micros.d.ts +9 -0
  82. package/dist/lib/usd-micros.js +21 -0
  83. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +42 -1
  84. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +35 -3
  85. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.d.ts +22 -3
  86. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialResolver.js +29 -5
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.d.ts +49 -1
  88. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.js +47 -9
  89. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +7 -1
  90. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +7 -1
  91. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceKey.d.ts +56 -0
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceKey.js +42 -20
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dpop.d.ts +101 -0
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dpop.js +179 -0
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +23 -5
  97. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +42 -1
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +32 -2
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.d.ts +22 -3
  100. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialResolver.js +27 -4
  101. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.d.ts +49 -1
  102. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.js +47 -9
  103. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +7 -1
  104. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +7 -1
  105. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceKey.d.ts +56 -0
  106. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceKey.js +39 -20
  107. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dpop.d.ts +101 -0
  108. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dpop.js +138 -0
  109. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
  110. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +5 -1
  111. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  112. package/dist/node_modules/@skrr-ai/data-provider/index.js +3895 -3882
  113. package/oclif.manifest.json +34985 -33643
  114. package/package.json +28 -4
@@ -52,6 +52,10 @@ export declare abstract class BaseCommand extends Command {
52
52
  * from here.
53
53
  */
54
54
  protected resolvedCredential: ResolvedCredential;
55
+ /** Re-authorize a long-lived transport using this command's credential
56
+ * source. A daemon handoff is redeemed again; its refresh family is never
57
+ * borrowed or written into the CLI's own login. */
58
+ protected refreshTransportToken(): Promise<string>;
55
59
  /**
56
60
  * Where `resolvedCredential` really came from when its `source` slot cannot
57
61
  * say — an in-memory daemon hand-off or Agent session token is recorded as
@@ -155,6 +155,25 @@ class BaseCommand extends core_1.Command {
155
155
  * from here.
156
156
  */
157
157
  resolvedCredential;
158
+ /** Re-authorize a long-lived transport using this command's credential
159
+ * source. A daemon handoff is redeemed again; its refresh family is never
160
+ * borrowed or written into the CLI's own login. */
161
+ async refreshTransportToken() {
162
+ if (this.credentialProvenance?.kind === 'daemon-handoff') {
163
+ const result = await (0, daemonBroker_1.redeemBrokeredHandoffToken)(true);
164
+ if (!result?.accessToken)
165
+ throw new Error('The local daemon could not renew CLI authentication.');
166
+ this.resolvedCredential = { token: result.accessToken, source: 'env-token',
167
+ kind: (0, auth_core_1.classifyTokenKind)(result.accessToken) };
168
+ }
169
+ else {
170
+ this.resolvedCredential = (await (0, refresh_1.ensureFreshCliCredential)({ credential: this.resolvedCredential,
171
+ bareMode: this.bareMode })).credential;
172
+ }
173
+ if (!this.resolvedCredential.token)
174
+ throw new Error('CLI authentication is unavailable.');
175
+ return this.resolvedCredential.token;
176
+ }
158
177
  /**
159
178
  * Where `resolvedCredential` really came from when its `source` slot cannot
160
179
  * say — an in-memory daemon hand-off or Agent session token is recorded as
@@ -399,7 +418,10 @@ class BaseCommand extends core_1.Command {
399
418
  }
400
419
  else if ((this.brokerRefusal =
401
420
  (0, daemonBrokerRefusal_1.describeBrokerRefusal)(autoResult.outcome, this.config.bin) ??
402
- describeAbsentGuestDaemon(autoResult.outcome, this.config.bin))) {
421
+ describeAbsentGuestDaemon(autoResult.outcome, this.config.bin) ??
422
+ // A laptop daemon that is down or mid-restart is not the person
423
+ // being signed out (OSK-13222).
424
+ (0, daemonBrokerRefusal_1.describeLocalDaemonUnavailable)(autoResult.outcome, this.config.bin))) {
403
425
  // Reported by requireAuth(); nothing to print here.
404
426
  }
405
427
  else if (autoResult.outcome.reason === 'rate_limited') {
@@ -760,7 +782,11 @@ class BaseCommand extends core_1.Command {
760
782
  // Same code as the plain case — scripts branch on NOT_SIGNED_IN — with
761
783
  // the daemon's answer in the message and in `details.broker`.
762
784
  this.failWithCliError({
763
- message: `Not signed in. ${refusal.headline}\n→ ${refusal.remedy}`,
785
+ // A daemon that is down or restarting holds the person's sign-in; it
786
+ // has not been lost, so the sentence must not say it has (OSK-13222).
787
+ message: refusal.daemonUnavailable
788
+ ? `No credential from the skrr background service. ${refusal.headline}\n→ ${refusal.remedy}`
789
+ : `Not signed in. ${refusal.headline}\n→ ${refusal.remedy}`,
764
790
  code: 'NOT_SIGNED_IN',
765
791
  exit: 2,
766
792
  retryable: refusal.transient,
@@ -1169,11 +1195,15 @@ class BaseCommand extends core_1.Command {
1169
1195
  // `login` sends a CI job to do the one thing it cannot, and hides the
1170
1196
  // real mistake (dogfood OSK-4693). Name the credential instead.
1171
1197
  const presentedCiToken = Boolean(this.resolvedCredential?.token?.startsWith('osk_ci_'));
1198
+ // A personal access token (OSK-13208) has no session behind it to renew
1199
+ // and no daemon to ask: the server's own reason (expired, revoked) IS
1200
+ // the answer, and the repair is a new token, not `login`.
1201
+ const presentedPat = Boolean(this.resolvedCredential?.token?.startsWith('osk_pat_'));
1172
1202
  // OSK-12116 — the 401 is usually the SECOND failure: the token expired
1173
1203
  // (routine) and renewing it was refused (the fault). The server's code
1174
1204
  // for the retry is `TOKEN_EXPIRED`, which names the routine half; the
1175
1205
  // latched refresh refusal names the half the reader can act on.
1176
- const refreshRefusal = presentedCiToken
1206
+ const refreshRefusal = presentedCiToken || presentedPat
1177
1207
  ? null
1178
1208
  : (0, auth_failure_1.describeRefreshRefusal)(readRefreshRefusal(), {
1179
1209
  bin: this.config.bin,
@@ -1183,7 +1213,7 @@ class BaseCommand extends core_1.Command {
1183
1213
  // refused refresh, and the daemon refused too. Its reason decides the
1184
1214
  // repair (OSK-12149, OSK-12151), and on a Dedicated guest there is no
1185
1215
  // login to run, so its remedy replaces the generic "Run login".
1186
- const brokerRefusal = presentedCiToken || !e.brokerFailure
1216
+ const brokerRefusal = presentedCiToken || presentedPat || !e.brokerFailure
1187
1217
  ? null
1188
1218
  : (0, daemonBrokerRefusal_1.describeBrokerRefusal)(e.brokerFailure, this.config.bin);
1189
1219
  const renewal = refreshRefusal
@@ -1202,6 +1232,12 @@ class BaseCommand extends core_1.Command {
1202
1232
  `daemon (\`OVERSKY_TOKEN=<token> skrrd start\`). ${this.config.bin} commands do ` +
1203
1233
  `not accept it — sign in with \`${this.config.bin} login\` instead.`;
1204
1234
  }
1235
+ else if (presentedPat) {
1236
+ authMessage =
1237
+ `Authentication failed: ${bodyMessage || 'the personal access token was refused.'} ` +
1238
+ `Create a new token with \`${this.config.bin} tokens create\` from a signed-in terminal ` +
1239
+ `and update the secret that sets SKRR_TOKEN.`;
1240
+ }
1205
1241
  else if (brokerRefusal) {
1206
1242
  authMessage = `Authentication failed. ${renewal} ${brokerRefusal.headline}\n→ ${brokerRefusal.remedy}`;
1207
1243
  }
@@ -1,3 +1,4 @@
1
+ import type { StoreReleaseLicense } from '@skrr-ai/data-provider';
1
2
  import { BaseCommand } from '../../base-command';
2
3
  /**
3
4
  * `--capability-rationale <permission-key>=<note>` → the publisher notes a
@@ -18,6 +19,16 @@ export declare function parseReleaseDependencies(entries: readonly string[] | un
18
19
  }>;
19
20
  error?: string;
20
21
  };
22
+ /**
23
+ * `--license` / `--remix` → the `license` declaration a public release carries
24
+ * (SUP11). Values are already constrained by the flags' `options`; this only
25
+ * enforces the pairing the flags cannot express: a remix grant is part of a
26
+ * license declaration, so it is meaningless without one.
27
+ */
28
+ export declare function buildReleaseLicense(licenseId: string | undefined, remix: string | undefined): {
29
+ license?: StoreReleaseLicense;
30
+ error?: string;
31
+ };
21
32
  export default class AgentsVisibility extends BaseCommand {
22
33
  static description: string;
23
34
  static examples: string[];
@@ -36,6 +47,9 @@ export default class AgentsVisibility extends BaseCommand {
36
47
  'capability-rationale': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
37
48
  language: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
38
49
  dependency: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
50
+ license: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
51
+ remix: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
52
+ 'source-visibility': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
39
53
  };
40
54
  run(): Promise<void>;
41
55
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseCapabilityRationales = parseCapabilityRationales;
4
4
  exports.parseReleaseDependencies = parseReleaseDependencies;
5
+ exports.buildReleaseLicense = buildReleaseLicense;
5
6
  const core_1 = require("@oclif/core");
6
7
  const data_provider_1 = require("@skrr-ai/data-provider");
7
8
  const base_command_1 = require("../../base-command");
@@ -48,6 +49,25 @@ function parseReleaseDependencies(entries) {
48
49
  }
49
50
  return { dependencies };
50
51
  }
52
+ /**
53
+ * `--license` / `--remix` → the `license` declaration a public release carries
54
+ * (SUP11). Values are already constrained by the flags' `options`; this only
55
+ * enforces the pairing the flags cannot express: a remix grant is part of a
56
+ * license declaration, so it is meaningless without one.
57
+ */
58
+ function buildReleaseLicense(licenseId, remix) {
59
+ if (!licenseId) {
60
+ return remix
61
+ ? { error: '--remix is part of a license declaration; pass it with --license <id>.' }
62
+ : {};
63
+ }
64
+ return {
65
+ license: {
66
+ id: licenseId,
67
+ ...(remix ? { remix: remix } : {}),
68
+ },
69
+ };
70
+ }
51
71
  class AgentsVisibility extends base_command_1.BaseCommand {
52
72
  static description = 'Set the visibility of an agent';
53
73
  static examples = [
@@ -57,6 +77,8 @@ class AgentsVisibility extends base_command_1.BaseCommand {
57
77
  '<%= config.bin %> agents visibility <agent-id> --public --capability-rationale "network:*=Looks up flight prices on the web"',
58
78
  '<%= config.bin %> agents visibility <agent-id> --public --language en --language zh-Hans',
59
79
  '<%= config.bin %> agents visibility <agent-id> --public --dependency skill:<skill-id>@<store-release-id>',
80
+ '<%= config.bin %> agents visibility <agent-id> --public --license mit --remix attributed --source-visibility public',
81
+ '<%= config.bin %> agents visibility <agent-id> --public --license proprietary --source-visibility sealed',
60
82
  ];
61
83
  static args = {
62
84
  id: core_1.Args.string({ description: 'Agent ID', required: true, ignoreStdin: true }),
@@ -96,6 +118,18 @@ class AgentsVisibility extends base_command_1.BaseCommand {
96
118
  description: 'Pin an attached Skill or Action to its immutable Store release: skill:<id>@<store-release-id> (repeatable)',
97
119
  multiple: true,
98
120
  }),
121
+ license: core_1.Flags.string({
122
+ description: 'The license the public release is offered under. Recorded on the release and shown on the listing; proprietary grants no reuse',
123
+ options: [...data_provider_1.STORE_RELEASE_LICENSE_IDS],
124
+ }),
125
+ remix: core_1.Flags.string({
126
+ description: "Whether a buyer's installed copy may be published as a derived listing: attributed (the fork credits this release) or none. Needs --license; defaults to none",
127
+ options: [...data_provider_1.STORE_RELEASE_REMIX_GRANTS],
128
+ }),
129
+ 'source-visibility': core_1.Flags.string({
130
+ description: "Who may read the release's source, independent of price: licensed (license holders), public (anyone who sees the listing) or sealed (you only). Omit to keep the price-derived rule",
131
+ options: [...data_provider_1.STORE_RELEASE_SOURCE_VISIBILITIES],
132
+ }),
99
133
  };
100
134
  async run() {
101
135
  this.requireAuth();
@@ -128,6 +162,15 @@ class AgentsVisibility extends base_command_1.BaseCommand {
128
162
  exit: 1,
129
163
  });
130
164
  }
165
+ const { license, error: licenseError } = buildReleaseLicense(flags.license, flags.remix);
166
+ if (licenseError)
167
+ this.error(licenseError, { exit: 1 });
168
+ const sourceVisibility = flags['source-visibility'];
169
+ // The server refuses both declarations with isPublic: false — they are
170
+ // terms of a public release, and an unpublish creates none.
171
+ if ((license || sourceVisibility) && !isPublic) {
172
+ this.error('--license, --remix and --source-visibility are terms of a public release; pass them with --public.', { exit: 1 });
173
+ }
131
174
  let result;
132
175
  try {
133
176
  result = await data_provider_1.dataService.setAgentVisibility({
@@ -141,6 +184,8 @@ class AgentsVisibility extends base_command_1.BaseCommand {
141
184
  capabilityRationales,
142
185
  supportedLanguages: flags.language,
143
186
  dependencies,
187
+ license,
188
+ sourceVisibility,
144
189
  });
145
190
  }
146
191
  catch (err) {
@@ -152,6 +197,13 @@ class AgentsVisibility extends base_command_1.BaseCommand {
152
197
  }
153
198
  const label = isPublic ? 'public' : 'private';
154
199
  this.log(`Set agent ${args.id} visibility: ${label}`);
200
+ const recorded = result;
201
+ if (recorded?.license) {
202
+ this.log(`License: ${recorded.license.id} (remix: ${recorded.license.remix})`);
203
+ }
204
+ if (recorded?.sourceVisibility) {
205
+ this.log(`Source visibility: ${recorded.sourceVisibility}`);
206
+ }
155
207
  }
156
208
  }
157
209
  exports.default = AgentsVisibility;
@@ -43,7 +43,6 @@ import { BaseCommand } from '../base-command';
43
43
  */
44
44
  export default class CreateToken extends BaseCommand {
45
45
  static description: string;
46
- static aliases: string[];
47
46
  static examples: string[];
48
47
  static flags: {
49
48
  name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -49,7 +49,9 @@ const api_fetch_1 = require("../lib/api-fetch");
49
49
  class CreateToken extends base_command_1.BaseCommand {
50
50
  static description = 'Create a long-lived token for bootstrapping a daemon on a CI/headless machine. ' +
51
51
  'Printed exactly once. Not accepted by `skrr` commands themselves.';
52
- static aliases = ['tokens:create'];
52
+ // `tokens:create` used to alias this command. It now names personal access
53
+ // tokens (OSK-13208), the credential `skrr` itself accepts; this one only
54
+ // bootstraps a daemon, and sharing the name is what sent CI here.
53
55
  static examples = [
54
56
  '<%= config.bin %> create-token --name "github-actions-prod" --ttl 1y',
55
57
  '<%= config.bin %> create-token --name "local-ci" --ttl 30d',
@@ -8,6 +8,7 @@ const base_command_1 = require("../../base-command");
8
8
  const inbox_1 = require("../../lib/inbox");
9
9
  const format_1 = require("../../lib/format");
10
10
  const agentic_stream_1 = require("../../lib/agentic-stream");
11
+ const dpop_auth_1 = require("../../lib/dpop-auth");
11
12
  /**
12
13
  * Stop `--follow` on Ctrl-C or SIGTERM, and end with the status a shell
13
14
  * reports for that signal (130 / 143).
@@ -241,11 +242,12 @@ class Inbox extends base_command_1.BaseCommand {
241
242
  const wsURL = (0, agentic_stream_1.toWebSocketOrigin)(this.cliConfig.baseURL);
242
243
  const socket = (0, socket_io_client_1.io)(wsURL, {
243
244
  path: '/ws/agentic',
244
- auth: {
245
+ // OSK-12020 — a DPoP-bound token proves possession on every handshake.
246
+ auth: (0, dpop_auth_1.socketAuthWithDpop)({
245
247
  token: this.resolvedCredential.token,
246
248
  ...(0, data_provider_1.getClientIdentityAuth)(),
247
249
  clientPlatform: 'cli',
248
- },
250
+ }, wsURL),
249
251
  extraHeaders: process.env.OVERSKY_WORKSPACE_ID
250
252
  ? { 'X-Workspace-Id': process.env.OVERSKY_WORKSPACE_ID }
251
253
  : undefined,
@@ -8,7 +8,6 @@ import { BaseCommand } from '../base-command';
8
8
  */
9
9
  export default class ListTokens extends BaseCommand {
10
10
  static description: string;
11
- static aliases: string[];
12
11
  static flags: {
13
12
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
13
  all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -13,7 +13,7 @@ const api_fetch_1 = require("../lib/api-fetch");
13
13
  */
14
14
  class ListTokens extends base_command_1.BaseCommand {
15
15
  static description = 'List your long-lived CI tokens';
16
- static aliases = ['tokens:list'];
16
+ // No `tokens:list` alias: `skrr tokens` is personal access tokens (OSK-13208).
17
17
  static flags = {
18
18
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
19
19
  all: core_1.Flags.boolean({
@@ -12,9 +12,14 @@ import { BaseCommand } from '../base-command';
12
12
  * Refresh, rotation, family revoke, and the cross-process auth.lock are
13
13
  * all the same substrate the daemon uses (@skrr-ai/auth-core).
14
14
  *
15
- * CI callers should NOT use this command — use
16
- * skrr create-token --name <name> --ttl 365
17
- * and set `OVERSKY_TOKEN=osk_ci_...` in the CI environment.
15
+ * CI. There is no long-lived token that `skrr` commands accept yet (OSK-13208).
16
+ * `skrr create-token` mints an `osk_ci_*` token, and that token only
17
+ * bootstraps a DAEMON (`OVERSKY_TOKEN=osk_ci_... skrrd start`): every `skrr`
18
+ * command rejects it with 401 (see create-token.ts and base-command.ts). What
19
+ * works today is a session minted for the machine with
20
+ * `skrr token mint --for <id>`, restored by the `OVERSKY_REFRESH_TOKEN` path
21
+ * below — whose refresh token rotates on use — or `OVERSKY_AUTH_HELPER`.
22
+ * docs/cli/SKRR_AUTH.md ("CI today") states the caveats.
18
23
  *
19
24
  * L13 — non-interactive restore from a refresh token. When
20
25
  * `OVERSKY_REFRESH_TOKEN` is set in the environment, the browser /
@@ -27,9 +27,14 @@ const prompt_1 = require("../lib/prompt");
27
27
  * Refresh, rotation, family revoke, and the cross-process auth.lock are
28
28
  * all the same substrate the daemon uses (@skrr-ai/auth-core).
29
29
  *
30
- * CI callers should NOT use this command — use
31
- * skrr create-token --name <name> --ttl 365
32
- * and set `OVERSKY_TOKEN=osk_ci_...` in the CI environment.
30
+ * CI. There is no long-lived token that `skrr` commands accept yet (OSK-13208).
31
+ * `skrr create-token` mints an `osk_ci_*` token, and that token only
32
+ * bootstraps a DAEMON (`OVERSKY_TOKEN=osk_ci_... skrrd start`): every `skrr`
33
+ * command rejects it with 401 (see create-token.ts and base-command.ts). What
34
+ * works today is a session minted for the machine with
35
+ * `skrr token mint --for <id>`, restored by the `OVERSKY_REFRESH_TOKEN` path
36
+ * below — whose refresh token rotates on use — or `OVERSKY_AUTH_HELPER`.
37
+ * docs/cli/SKRR_AUTH.md ("CI today") states the caveats.
33
38
  *
34
39
  * L13 — non-interactive restore from a refresh token. When
35
40
  * `OVERSKY_REFRESH_TOKEN` is set in the environment, the browser /
@@ -60,7 +65,12 @@ class Login extends base_command_1.BaseCommand {
60
65
  'Set OVERSKY_SKIP_DAEMON_BROKER=1 to skip step 1 (--device-code skips it too). After signing ' +
61
66
  'in, skrr also signs the background service in, and on an interactive first login on a ' +
62
67
  'computer without it, offers to install it. Set SKRR_SKIP_DAEMON_HANDOFF=1 to skip that ' +
63
- 'optional setup; it does not skip step 1.';
68
+ 'optional setup; it does not skip step 1.\n\n' +
69
+ 'With no person to approve a sign-in (CI, a provisioning script): mint a session for that ' +
70
+ 'machine with `skrr token mint --for <id>` and run this command there with ' +
71
+ 'OVERSKY_REFRESH_TOKEN and OVERSKY_CLI_ID set. The refresh token rotates when used, so one ' +
72
+ 'minted session serves one machine. Tokens from `skrr create-token` only bring up a ' +
73
+ 'background service (`skrrd`); skrr commands reject them.';
64
74
  static examples = [
65
75
  '<%= config.bin %> login',
66
76
  '<%= config.bin %> login --env prod',
@@ -44,6 +44,7 @@ class DedicatedMachinesSshProxy extends dedicated_lease_command_1.DedicatedLease
44
44
  code = await (0, dedicated_ssh_1.runSshProxy)({
45
45
  baseURL: this.cliConfig.baseURL,
46
46
  token: this.resolvedCredential.token,
47
+ getToken: () => this.refreshTransportToken(),
47
48
  leaseId: lease,
48
49
  input: process.stdin,
49
50
  output: process.stdout,
@@ -11,7 +11,6 @@ import { BaseCommand } from '../base-command';
11
11
  */
12
12
  export default class RevokeToken extends BaseCommand {
13
13
  static description: string;
14
- static aliases: string[];
15
14
  static args: {
16
15
  id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
16
  };
@@ -15,7 +15,7 @@ const api_fetch_1 = require("../lib/api-fetch");
15
15
  */
16
16
  class RevokeToken extends base_command_1.BaseCommand {
17
17
  static description = 'Revoke a long-lived CI token by ID or unique prefix';
18
- static aliases = ['tokens:revoke'];
18
+ // No `tokens:revoke` alias: `skrr tokens` is personal access tokens (OSK-13208).
19
19
  static args = {
20
20
  id: core_1.Args.string({
21
21
  name: 'id',
@@ -4,6 +4,7 @@ exports.workflowEventStreamHeaders = workflowEventStreamHeaders;
4
4
  const core_1 = require("@oclif/core");
5
5
  const base_command_1 = require("../../../base-command");
6
6
  const workspace_header_1 = require("../../../lib/workspace-header");
7
+ const dpop_auth_1 = require("../../../lib/dpop-auth");
7
8
  function workflowEventStreamHeaders(token, env = process.env) {
8
9
  return (0, workspace_header_1.withWorkspaceHeader)({
9
10
  Accept: 'text/event-stream',
@@ -31,8 +32,14 @@ class SpacesWorkflowsEvents extends base_command_1.BaseCommand {
31
32
  this.error('--after must be an ISO-8601 timestamp', { exit: 2 });
32
33
  }
33
34
  const path = `/api/spaces/${encodeURIComponent(args.spaceId)}/workflows/runs/${encodeURIComponent(args.runId)}/events/stream${flags.after ? `?after=${encodeURIComponent(flags.after)}` : ''}`;
34
- const response = await fetch(`${this.cliConfig.baseURL.replace(/\/+$/, '')}${path}`, {
35
- headers: workflowEventStreamHeaders(this.resolvedCredential.token),
35
+ const streamUrl = `${this.cliConfig.baseURL.replace(/\/+$/, '')}${path}`;
36
+ const token = this.resolvedCredential.token;
37
+ const response = await fetch(streamUrl, {
38
+ headers: {
39
+ ...workflowEventStreamHeaders(token),
40
+ // OSK-12020 — a DPoP-bound hand-off token proves possession here too.
41
+ ...(await (0, dpop_auth_1.dpopHeadersFor)(token, 'GET', streamUrl)),
42
+ },
36
43
  });
37
44
  if (!response.ok || !response.body) {
38
45
  const body = await response.text();
@@ -8,6 +8,7 @@ export default class StoreFollow extends BaseCommand {
8
8
  };
9
9
  static flags: {
10
10
  undo: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ status: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
12
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
13
  };
13
14
  run(): Promise<void>;
@@ -18,13 +18,19 @@ class StoreFollow extends base_command_1.BaseCommand {
18
18
  }),
19
19
  };
20
20
  static flags = {
21
- undo: core_1.Flags.boolean({ description: 'Unfollow the publisher' }),
21
+ undo: core_1.Flags.boolean({ description: 'Unfollow the publisher', exclusive: ['status'] }),
22
+ status: core_1.Flags.boolean({ description: 'Show whether you follow the publisher; change nothing' }),
22
23
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
23
24
  };
24
25
  async run() {
25
26
  this.requireAuth();
26
27
  const { args, flags } = await this.parse(StoreFollow);
27
28
  try {
29
+ if (flags.status) {
30
+ const overlay = await data_provider_1.dataService.getStorePublisherFollowOverlay(args.handle);
31
+ this.log(JSON.stringify(overlay, null, 2));
32
+ return;
33
+ }
28
34
  const result = flags.undo
29
35
  ? await data_provider_1.dataService.unfollowStorePublisherByHandle(args.handle)
30
36
  : await data_provider_1.dataService.followStorePublisherByHandle(args.handle);
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /** The publishers you follow, newest first, one page at a time. */
3
+ export default class StoreFollowsList extends BaseCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ cursor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,29 @@
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
+ /** The publishers you follow, newest first, one page at a time. */
7
+ class StoreFollowsList extends base_command_1.BaseCommand {
8
+ static description = 'List the store publishers you follow';
9
+ static examples = [
10
+ '<%= config.bin %> store follows list',
11
+ '<%= config.bin %> store follows list --cursor <next>',
12
+ ];
13
+ static flags = {
14
+ cursor: core_1.Flags.string({ description: 'Continue from a previous page' }),
15
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
16
+ };
17
+ async run() {
18
+ this.requireAuth();
19
+ const { flags } = await this.parse(StoreFollowsList);
20
+ try {
21
+ const result = await data_provider_1.dataService.getStorePublisherFollows(flags.cursor);
22
+ this.log(JSON.stringify(result, null, 2));
23
+ }
24
+ catch (err) {
25
+ this.handleApiError(err);
26
+ }
27
+ }
28
+ }
29
+ exports.default = StoreFollowsList;
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * Remove a follow by its follow id — the one way to drop a follow whose
4
+ * publisher has since renamed, hidden or lost its handle.
5
+ */
6
+ export default class StoreFollowsRemove extends BaseCommand {
7
+ static description: string;
8
+ static examples: string[];
9
+ static args: {
10
+ followId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
11
+ };
12
+ static flags: {
13
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,37 @@
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
+ /**
7
+ * Remove a follow by its follow id — the one way to drop a follow whose
8
+ * publisher has since renamed, hidden or lost its handle.
9
+ */
10
+ class StoreFollowsRemove extends base_command_1.BaseCommand {
11
+ static description = 'Remove a follow by id (works even when the publisher no longer has a public handle)';
12
+ static examples = ['<%= config.bin %> store follows remove pf_abc123'];
13
+ static args = {
14
+ followId: core_1.Args.string({
15
+ description: 'Follow id (from `store follows list`)',
16
+ required: true,
17
+ ignoreStdin: true,
18
+ }),
19
+ };
20
+ static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
21
+ async run() {
22
+ this.requireAuth();
23
+ const { args, flags } = await this.parse(StoreFollowsRemove);
24
+ try {
25
+ const result = await data_provider_1.dataService.unfollowStorePublisherById(args.followId);
26
+ if (flags.json) {
27
+ this.log(JSON.stringify(result, null, 2));
28
+ return;
29
+ }
30
+ this.log(result.removed ? `Removed ${args.followId}.` : `${args.followId} was not followed.`);
31
+ }
32
+ catch (err) {
33
+ this.handleApiError(err);
34
+ }
35
+ }
36
+ }
37
+ exports.default = StoreFollowsRemove;
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../../../../base-command';
2
+ /**
3
+ * Replace the profile draft. `--revision` is required: a draft is a whole
4
+ * document, and writing one without naming the revision you edited would
5
+ * silently overwrite a change made meanwhile in the browser.
6
+ */
7
+ export default class StorePublishersProfileDraft extends BaseCommand {
8
+ static description: string;
9
+ static examples: string[];
10
+ static flags: {
11
+ 'publish-as': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ file: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ revision: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
14
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ }
@@ -0,0 +1,54 @@
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 publisher_profile_flags_1 = require("../../../../lib/publisher-profile-flags");
7
+ /**
8
+ * Replace the profile draft. `--revision` is required: a draft is a whole
9
+ * document, and writing one without naming the revision you edited would
10
+ * silently overwrite a change made meanwhile in the browser.
11
+ */
12
+ class StorePublishersProfileDraft extends base_command_1.BaseCommand {
13
+ static description = 'Replace your publisher profile draft from a JSON file (compare-and-set on --revision)';
14
+ static examples = [
15
+ '<%= config.bin %> store publishers profile draft --file draft.json --revision 7',
16
+ ];
17
+ static flags = {
18
+ 'publish-as': publisher_profile_flags_1.publishAsFlag,
19
+ file: core_1.Flags.string({ description: 'JSON file holding the draft', required: true }),
20
+ revision: core_1.Flags.integer({
21
+ description: 'The revision you edited (from `profile show`); a stale one is refused',
22
+ required: true,
23
+ min: 0,
24
+ }),
25
+ json: publisher_profile_flags_1.jsonFlag,
26
+ };
27
+ async run() {
28
+ this.requireAuth();
29
+ const { flags } = await this.parse(StorePublishersProfileDraft);
30
+ let draft;
31
+ try {
32
+ draft = (0, publisher_profile_flags_1.readDraftFile)(flags.file);
33
+ }
34
+ catch (err) {
35
+ this.error(err.message);
36
+ }
37
+ try {
38
+ const result = await data_provider_1.dataService.replaceStorePublisherProfileDraft({
39
+ publishAs: flags['publish-as'],
40
+ expectedRevision: flags.revision,
41
+ draft,
42
+ });
43
+ if (flags.json) {
44
+ this.log(JSON.stringify(result, null, 2));
45
+ return;
46
+ }
47
+ this.log(`Draft saved at revision ${result.profile.revision}. Nothing is public until you publish.`);
48
+ }
49
+ catch (err) {
50
+ this.handleApiError(err);
51
+ }
52
+ }
53
+ }
54
+ exports.default = StorePublishersProfileDraft;
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../../base-command';
2
+ /** Rename your public handle. The server refuses a lookalike of an existing one. */
3
+ export default class StorePublishersProfileHandle extends BaseCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static args: {
7
+ handle: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ 'publish-as': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }