@skrr-ai/cli 0.1.42 → 0.1.44

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 (120) hide show
  1. package/dist/base-command.d.ts +2 -0
  2. package/dist/base-command.js +1 -0
  3. package/dist/commands/balance/index.js +1 -1
  4. package/dist/commands/balance/overage.js +4 -3
  5. package/dist/commands/balance/plan.js +10 -6
  6. package/dist/commands/balance/show.d.ts +32 -0
  7. package/dist/commands/balance/show.js +74 -3
  8. package/dist/commands/balance/usage/events.js +3 -2
  9. package/dist/commands/balance/usage.js +2 -1
  10. package/dist/commands/browser/install.js +3 -2
  11. package/dist/commands/browser/uninstall.js +3 -2
  12. package/dist/commands/code/handover.d.ts +1 -0
  13. package/dist/commands/code/handover.js +4 -0
  14. package/dist/commands/code/install.js +3 -2
  15. package/dist/commands/code/jobs/run.d.ts +1 -0
  16. package/dist/commands/code/jobs/run.js +4 -0
  17. package/dist/commands/daemon/install.js +7 -0
  18. package/dist/commands/harnesses/install.d.ts +11 -0
  19. package/dist/commands/harnesses/install.js +33 -14
  20. package/dist/commands/harnesses/installers.d.ts +10 -0
  21. package/dist/commands/harnesses/installers.js +31 -0
  22. package/dist/commands/harnesses/leases/show.js +14 -0
  23. package/dist/commands/harnesses/list.js +3 -2
  24. package/dist/commands/instructions/install.d.ts +22 -0
  25. package/dist/commands/instructions/install.js +83 -7
  26. package/dist/commands/instructions/list.js +5 -0
  27. package/dist/commands/instructions/show.d.ts +6 -0
  28. package/dist/commands/instructions/show.js +34 -1
  29. package/dist/commands/instructions/status.js +17 -1
  30. package/dist/commands/payments/wallet.js +2 -2
  31. package/dist/commands/tasks/attachments/set-role.d.ts +24 -0
  32. package/dist/commands/tasks/attachments/set-role.js +60 -0
  33. package/dist/commands/tasks/attachments/upload.d.ts +1 -0
  34. package/dist/commands/tasks/attachments/upload.js +18 -52
  35. package/dist/commands/tasks/comments/add.d.ts +1 -0
  36. package/dist/commands/tasks/comments/add.js +67 -2
  37. package/dist/commands/tasks/complete.d.ts +1 -0
  38. package/dist/commands/tasks/complete.js +56 -1
  39. package/dist/commands/tasks/create.d.ts +6 -0
  40. package/dist/commands/tasks/create.js +26 -3
  41. package/dist/commands/tasks/labels/attach.js +4 -0
  42. package/dist/commands/tasks/list.d.ts +1 -0
  43. package/dist/commands/tasks/list.js +9 -0
  44. package/dist/commands/tasks/result/submit.d.ts +3 -0
  45. package/dist/commands/tasks/result/submit.js +76 -8
  46. package/dist/commands/tasks/show.d.ts +23 -0
  47. package/dist/commands/tasks/show.js +61 -1
  48. package/dist/commands/tasks/update.js +16 -2
  49. package/dist/commands/tasks/updates/add.d.ts +1 -0
  50. package/dist/commands/tasks/updates/add.js +63 -0
  51. package/dist/commands/views/create.js +12 -2
  52. package/dist/commands/views/list.js +3 -2
  53. package/dist/commands/views/show.js +2 -0
  54. package/dist/lib/agentic-stream.d.ts +10 -4
  55. package/dist/lib/agentic-stream.js +25 -11
  56. package/dist/lib/api-fetch.js +7 -2
  57. package/dist/lib/cli-installers.d.ts +23 -4
  58. package/dist/lib/cli-installers.js +56 -8
  59. package/dist/lib/daemon-setup.d.ts +18 -1
  60. package/dist/lib/daemon-setup.js +33 -1
  61. package/dist/lib/dedicated-machines.js +4 -2
  62. package/dist/lib/file-mime.js +1 -1
  63. package/dist/lib/first-party-harness-agent.d.ts +16 -1
  64. package/dist/lib/first-party-harness-agent.js +47 -17
  65. package/dist/lib/first-party-harness-doctor.js +49 -48
  66. package/dist/lib/first-party-harness-managed.d.ts +9 -4
  67. package/dist/lib/first-party-harness-managed.js +13 -10
  68. package/dist/lib/first-party-harness.d.ts +32 -21
  69. package/dist/lib/first-party-harness.js +49 -31
  70. package/dist/lib/harness-provider-input.d.ts +18 -12
  71. package/dist/lib/harness-provider-input.js +18 -12
  72. package/dist/lib/harness-tiers.d.ts +4 -3
  73. package/dist/lib/harness-tiers.js +4 -3
  74. package/dist/lib/harnesses.d.ts +6 -0
  75. package/dist/lib/html-text.js +25 -0
  76. package/dist/lib/instruction-input.d.ts +21 -0
  77. package/dist/lib/instruction-input.js +30 -0
  78. package/dist/lib/instruction-provenance.d.ts +54 -0
  79. package/dist/lib/instruction-provenance.js +84 -0
  80. package/dist/lib/session-task-endpoints.d.ts +1 -1
  81. package/dist/lib/session-task-endpoints.js +3 -0
  82. package/dist/lib/task-asset-upload.d.ts +84 -0
  83. package/dist/lib/task-asset-upload.js +374 -0
  84. package/dist/lib/task-closure.d.ts +15 -0
  85. package/dist/lib/task-closure.js +20 -0
  86. package/dist/lib/task-view-render.d.ts +14 -0
  87. package/dist/lib/task-view-render.js +55 -0
  88. package/dist/lib/tasks.d.ts +26 -0
  89. package/dist/lib/tasks.js +38 -1
  90. package/dist/lib/views/vocabulary.d.ts +1 -1
  91. package/dist/lib/views/vocabulary.js +3 -1
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +183 -58
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +286 -100
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +21 -33
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +24 -50
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +19 -7
  97. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +26 -10
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +9 -5
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +9 -5
  100. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  101. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +1 -13
  102. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +21 -1
  103. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +75 -19
  104. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +183 -58
  105. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +279 -99
  106. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +21 -33
  107. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +24 -49
  108. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +19 -7
  109. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +27 -11
  110. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +9 -5
  111. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +9 -5
  112. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  113. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -4
  114. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +21 -1
  115. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +74 -19
  116. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  117. package/dist/node_modules/@skrr-ai/data-provider/index.js +4588 -4509
  118. package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
  119. package/oclif.manifest.json +33980 -33832
  120. package/package.json +3 -3
@@ -346,6 +346,8 @@ export declare function parseGlobalFlags(argv: readonly string[]): ParsedGlobals
346
346
  export declare function apiRefusal(err: unknown): {
347
347
  code?: string;
348
348
  message?: string;
349
+ /** The parsed JSON body, for a refusal that carries structured context. */
350
+ body?: Record<string, unknown>;
349
351
  };
350
352
  /**
351
353
  * Prefix a failure message with the resource it is about.
@@ -1368,6 +1368,7 @@ function apiRefusal(err) {
1368
1368
  return {
1369
1369
  code: stringProp(body, 'code') || stringProp(body, 'errorType'),
1370
1370
  message: stringProp(body, 'message') || stringProp(body, 'error'),
1371
+ ...(body ? { body } : {}),
1371
1372
  };
1372
1373
  }
1373
1374
  /**
@@ -18,7 +18,7 @@ const show_1 = __importDefault(require("./show"));
18
18
  * lists every subcommand, which is where a menu belongs.
19
19
  */
20
20
  class Balance extends show_1.default {
21
- static description = 'Show your hosted usage balance — the ceiling that refuses a turn (not the x402 wallet). ' +
21
+ static description = 'Show your hosted usage balance and plan limits — the ceilings that refuse a turn (not the x402 wallet). ' +
22
22
  'See `balance usage`, `balance overage`, `balance statement` and `balance history` for the detail behind it.';
23
23
  static examples = [
24
24
  '<%= config.bin %> balance',
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
+ const auth_core_1 = require("@skrr-ai/auth-core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const balance_1 = require("../../lib/balance");
6
7
  class BalanceOverage extends base_command_1.BaseCommand {
7
- static description = 'Enable or disable personal Sky Code overage after included allowance is exhausted';
8
+ static description = `Enable or disable personal ${auth_core_1.FIRST_PARTY_HARNESS.displayName} overage after included allowance is exhausted`;
8
9
  static examples = [
9
10
  '<%= config.bin %> balance overage enabled',
10
11
  '<%= config.bin %> balance overage disabled',
@@ -12,7 +13,7 @@ class BalanceOverage extends base_command_1.BaseCommand {
12
13
  ];
13
14
  static args = {
14
15
  state: core_1.Args.string({
15
- description: 'Whether personal stored balance may pay for Sky Code overage',
16
+ description: `Whether personal stored balance may pay for ${auth_core_1.FIRST_PARTY_HARNESS.displayName} overage`,
16
17
  required: true,
17
18
  options: ['enabled', 'disabled'],
18
19
  ignoreStdin: true,
@@ -34,7 +35,7 @@ class BalanceOverage extends base_command_1.BaseCommand {
34
35
  this.log(JSON.stringify({ ownerType: 'user', overageEnabled: result?.enabled === true }, null, 2));
35
36
  return;
36
37
  }
37
- this.log(`Sky Code overage ${result?.enabled ? 'enabled' : 'disabled'} for your personal billing account.`);
38
+ this.log(`${auth_core_1.FIRST_PARTY_HARNESS.displayName} overage ${result?.enabled ? 'enabled' : 'disabled'} for your personal billing account.`);
38
39
  this.log(result?.enabled
39
40
  ? 'New admissions may use stored balance after included allowance is exhausted.'
40
41
  : 'New admissions will stop when included allowance is exhausted.');
@@ -2,12 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderSkyCodePlan = renderSkyCodePlan;
4
4
  const core_1 = require("@oclif/core");
5
+ const auth_core_1 = require("@skrr-ai/auth-core");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
6
7
  const base_command_1 = require("../../base-command");
7
8
  const balance_1 = require("../../lib/balance");
8
9
  const web_url_1 = require("../../lib/web-url");
9
10
  class BalancePlan extends base_command_1.BaseCommand {
10
- static description = 'Show your Sky Code plan, renewal state, available tiers, and the matching lifecycle page';
11
+ static description = `Show your ${auth_core_1.FIRST_PARTY_HARNESS.displayName} plan, renewal state, available tiers, and the matching lifecycle page`;
11
12
  static examples = [
12
13
  '<%= config.bin %> balance plan',
13
14
  '<%= config.bin %> balance plan --json',
@@ -33,7 +34,7 @@ class BalancePlan extends base_command_1.BaseCommand {
33
34
  }
34
35
  exports.default = BalancePlan;
35
36
  function renderSkyCodePlan(subscription, lifecycleUrl, log) {
36
- log(`Sky Code plan — ${subscription.tierName}`);
37
+ log(`${auth_core_1.FIRST_PARTY_HARNESS.displayName} plan — ${subscription.tierName}`);
37
38
  log(` Status ${subscription.status}`);
38
39
  log(` Price $${subscription.monthlyPriceUsd}/month`);
39
40
  log(` Current period ${subscription.currentPeriodStart ?? 'not started'}`);
@@ -43,10 +44,13 @@ function renderSkyCodePlan(subscription, lifecycleUrl, log) {
43
44
  log(` Concurrent sessions ${subscription.concurrentSessions ?? 'not reported'}`);
44
45
  log(` Requests per minute ${subscription.rateLimitPerMin}`);
45
46
  log('');
46
- log('Available Sky Code plans:');
47
- for (const key of data_provider_1.SKY_CODE_PLAN_KEYS.filter((plan) => plan !== 'free')) {
48
- const plan = data_provider_1.SKY_CODE_PLAN_CATALOG[key];
49
- log(` ${plan.displayName.padEnd(18)} $${plan.monthlyPriceUsd}/month · ` +
47
+ log(`Available ${auth_core_1.FIRST_PARTY_HARNESS.displayName} plans:`);
48
+ const paidPlans = data_provider_1.SKY_CODE_PLAN_KEYS.filter((plan) => plan !== 'free').map((key) => data_provider_1.SKY_CODE_PLAN_CATALOG[key]);
49
+ // Padded to the longest name rather than a fixed width: plan names embed the
50
+ // product's display name, whose length is not this renderer's to assume.
51
+ const nameWidth = Math.max(...paidPlans.map((plan) => plan.displayName.length)) + 1;
52
+ for (const plan of paidPlans) {
53
+ log(` ${plan.displayName.padEnd(nameWidth)} $${plan.monthlyPriceUsd}/month · ` +
50
54
  `${(0, data_provider_1.usageMicrosToDisplayCredits)(plan.includedUsageMicros).toLocaleString()} monthly credits · ` +
51
55
  `${(0, data_provider_1.usageMicrosToDisplayCredits)(plan.fiveHourUsageMicros).toLocaleString()} per 5 hours · ` +
52
56
  `${plan.concurrentSessions} sessions · ${plan.requestsPerMinute} RPM`);
@@ -1,3 +1,4 @@
1
+ import type { TSkyCodeUsageSnapshot } from '@skrr-ai/data-provider';
1
2
  import { BaseCommand } from '../../base-command';
2
3
  import { type RuntimeMonthlySpend } from '../../lib/harnesses';
3
4
  /**
@@ -7,6 +8,30 @@ import { type RuntimeMonthlySpend } from '../../lib/harnesses';
7
8
  * the wording is part of the observable contract and is pinned directly.
8
9
  */
9
10
  export declare function renderRuntimeSpend(spend: RuntimeMonthlySpend[], log: (line: string) => void): void;
11
+ /**
12
+ * Your plan's ceilings, as lines.
13
+ *
14
+ * This screen showed the credits and the spend cap under a footer that called
15
+ * them "the ceiling that refuses a turn" — while the plan's monthly allowance,
16
+ * its five-hour window and its concurrent-session limit, each of which refuses a
17
+ * turn with its own message ("plan allowance is used up", "five-hour usage
18
+ * window is used up", "concurrent-session limit is reached"), were only on
19
+ * `balance usage` (OSK-4638). A refusal that names a ceiling should find that
20
+ * ceiling here, in the same words.
21
+ *
22
+ * The allowance is spent before the credits above; past it, overage consent
23
+ * decides whether the credits pay or the turn is refused. The five-hour window
24
+ * and the session limit are enforced together or not at all (admission needs
25
+ * both to be set), and what is left of the window is what admission computes:
26
+ * limit less used less reserved.
27
+ *
28
+ * Personal only. The workspace read does not report the workspace's overage
29
+ * terms, so rendering this for a workspace would state a refusal rule it cannot
30
+ * see.
31
+ *
32
+ * Pure so the wording is pinned directly, like `renderRuntimeSpend`.
33
+ */
34
+ export declare function renderPlanCeilings(usage: TSkyCodeUsageSnapshot, log: (line: string) => void, bin?: string): void;
10
35
  /**
11
36
  * YOUR hosted usage balance — the personal account, and only that one.
12
37
  *
@@ -41,6 +66,13 @@ export default class BalanceShow extends BaseCommand {
41
66
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
42
67
  };
43
68
  run(): Promise<void>;
69
+ /**
70
+ * Your plan's ceilings, beside the balance they are spent before.
71
+ *
72
+ * Best-effort, like the runtime cap below it: a failed read prints one line
73
+ * and leaves the balance intact rather than failing the command.
74
+ */
75
+ private renderPlanCeilings;
44
76
  /**
45
77
  * The OTHER cap — the one that actually refuses a Hosted Machine.
46
78
  *
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderRuntimeSpend = renderRuntimeSpend;
4
+ exports.renderPlanCeilings = renderPlanCeilings;
4
5
  const core_1 = require("@oclif/core");
6
+ const auth_core_1 = require("@skrr-ai/auth-core");
5
7
  const base_command_1 = require("../../base-command");
6
8
  const balance_1 = require("../../lib/balance");
7
9
  const workspaces_1 = require("../../lib/workspaces");
@@ -35,6 +37,52 @@ function renderRuntimeSpend(spend, log) {
35
37
  }
36
38
  }
37
39
  }
40
+ /**
41
+ * Your plan's ceilings, as lines.
42
+ *
43
+ * This screen showed the credits and the spend cap under a footer that called
44
+ * them "the ceiling that refuses a turn" — while the plan's monthly allowance,
45
+ * its five-hour window and its concurrent-session limit, each of which refuses a
46
+ * turn with its own message ("plan allowance is used up", "five-hour usage
47
+ * window is used up", "concurrent-session limit is reached"), were only on
48
+ * `balance usage` (OSK-4638). A refusal that names a ceiling should find that
49
+ * ceiling here, in the same words.
50
+ *
51
+ * The allowance is spent before the credits above; past it, overage consent
52
+ * decides whether the credits pay or the turn is refused. The five-hour window
53
+ * and the session limit are enforced together or not at all (admission needs
54
+ * both to be set), and what is left of the window is what admission computes:
55
+ * limit less used less reserved.
56
+ *
57
+ * Personal only. The workspace read does not report the workspace's overage
58
+ * terms, so rendering this for a workspace would state a refusal rule it cannot
59
+ * see.
60
+ *
61
+ * Pure so the wording is pinned directly, like `renderRuntimeSpend`.
62
+ */
63
+ function renderPlanCeilings(usage, log, bin = 'skrr') {
64
+ const resets = (at) => (at ? ` — resets ${(0, balance_1.formatBalanceTimestamp)(at)}` : '');
65
+ const window = usage.fiveHour;
66
+ log('');
67
+ log(`${auth_core_1.FIRST_PARTY_HARNESS.displayName} ${usage.plan ?? 'current'} plan — spent before the credits, and each limit refuses a turn:`);
68
+ log(` Plan monthly: ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.remainingUsageMicros)} left${resets(usage.monthly.resetsAt)}`);
69
+ if (window.limitUsageMicros > 0 && window.maxConcurrentSessions > 0) {
70
+ const windowLeft = Math.max(0, window.limitUsageMicros - window.usedUsageMicros - window.reservedUsageMicros);
71
+ log(` Plan 5-hour: ${(0, balance_1.formatCreditsWithUsd)(windowLeft)} left of ${(0, balance_1.formatCreditsWithUsd)(window.limitUsageMicros)}${resets(window.resetsAt)}`);
72
+ log(` Plan sessions: ${window.activeSessions} of ${window.maxConcurrentSessions} running`);
73
+ }
74
+ else {
75
+ log(' Plan 5-hour: no five-hour or concurrent-session limit');
76
+ }
77
+ log(usage.overageEnabled
78
+ ? ' Past the plan: paid from the credits (overage on)'
79
+ : ` Past the plan: refused — overage is off (\`${bin} balance overage enabled\`)`);
80
+ if (usage.partialReasons?.includes('missing_entitlement_period')) {
81
+ // Catalog defaults, not this account's period: say so rather than present
82
+ // them as measured.
83
+ log(' (plan period unavailable; catalog limits shown)');
84
+ }
85
+ }
38
86
  /**
39
87
  * YOUR hosted usage balance — the personal account, and only that one.
40
88
  *
@@ -171,17 +219,40 @@ class BalanceShow extends base_command_1.BaseCommand {
171
219
  else {
172
220
  this.log(' Auto-refill: off');
173
221
  }
222
+ await this.renderPlanCeilings();
174
223
  await this.renderRuntimeSpend();
175
224
  this.log('');
176
225
  this.log('A credit is a micro-dollar: 1,000,000 credits = $1.00. The spend cap');
177
226
  this.log('is a per-period ceiling, not a second balance. `skrr balance statement`');
178
227
  this.log('itemises this same account and says what each charge was for.');
179
228
  this.log('');
180
- this.log('This is the ceiling that refuses a turn for work you own. A session');
181
- this.log("carrying a workspace is refused against the WORKSPACE's balance —");
182
- this.log('see `skrr balance show --workspace <id>`. `skrr payments wallet` is the');
229
+ // Plural, because they are: a turn you own can be refused by the credits,
230
+ // the spend cap, a plan limit or a hosted runtime's monthly cap, and the
231
+ // refusal names which (OSK-4638).
232
+ this.log('These are the ceilings that refuse a turn for work you own; a refusal');
233
+ this.log('names the one that ran out. A session carrying a workspace is refused');
234
+ this.log("against the WORKSPACE's balance instead — see");
235
+ this.log('`skrr balance show --workspace <id>`. `skrr payments wallet` is the');
183
236
  this.log('separate x402 crypto wallet, in USDC — not this balance.');
184
237
  }
238
+ /**
239
+ * Your plan's ceilings, beside the balance they are spent before.
240
+ *
241
+ * Best-effort, like the runtime cap below it: a failed read prints one line
242
+ * and leaves the balance intact rather than failing the command.
243
+ */
244
+ async renderPlanCeilings() {
245
+ let usage;
246
+ try {
247
+ usage = await (0, balance_1.getSkyCodeUsage)();
248
+ }
249
+ catch {
250
+ this.log('');
251
+ this.log(` Plan limits: unavailable (\`${this.config.bin} balance usage\` to retry)`);
252
+ return;
253
+ }
254
+ renderPlanCeilings(usage, (line) => this.log(line), this.config.bin);
255
+ }
185
256
  /**
186
257
  * The OTHER cap — the one that actually refuses a Hosted Machine.
187
258
  *
@@ -2,10 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderSkyCodeUsageEvents = renderSkyCodeUsageEvents;
4
4
  const core_1 = require("@oclif/core");
5
+ const auth_core_1 = require("@skrr-ai/auth-core");
5
6
  const base_command_1 = require("../../../base-command");
6
7
  const balance_1 = require("../../../lib/balance");
7
8
  class BalanceUsageEvents extends base_command_1.BaseCommand {
8
- static description = 'List the Sky Code charges and run/session attribution behind aggregate allowance usage';
9
+ static description = `List the ${auth_core_1.FIRST_PARTY_HARNESS.displayName} charges and run/session attribution behind aggregate allowance usage`;
9
10
  static examples = [
10
11
  '<%= config.bin %> balance usage events',
11
12
  '<%= config.bin %> balance usage events --limit 20 --json',
@@ -44,7 +45,7 @@ class BalanceUsageEvents extends base_command_1.BaseCommand {
44
45
  }
45
46
  exports.default = BalanceUsageEvents;
46
47
  function renderSkyCodeUsageEvents(events, log) {
47
- log(`Sky Code usage events · ${events.ownerType} ${events.ownerId}`);
48
+ log(`${auth_core_1.FIRST_PARTY_HARNESS.displayName} usage events · ${events.ownerType} ${events.ownerId}`);
48
49
  log(` Window ${events.from} to ${events.to}`);
49
50
  if (events.items.length === 0) {
50
51
  log(' No usage events in this window.');
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderSkyCodeUsage = renderSkyCodeUsage;
4
4
  const core_1 = require("@oclif/core");
5
+ const auth_core_1 = require("@skrr-ai/auth-core");
5
6
  const base_command_1 = require("../../base-command");
6
7
  const balance_1 = require("../../lib/balance");
7
8
  /**
@@ -40,7 +41,7 @@ class BalanceUsage extends base_command_1.BaseCommand {
40
41
  }
41
42
  exports.default = BalanceUsage;
42
43
  function renderSkyCodeUsage(usage, log) {
43
- log(`Sky Code ${usage.plan ?? 'current'} plan · ${usage.ownerType} ${usage.ownerId}`);
44
+ log(`${auth_core_1.FIRST_PARTY_HARNESS.displayName} ${usage.plan ?? 'current'} plan · ${usage.ownerType} ${usage.ownerId}`);
44
45
  log(` Monthly used ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.usedUsageMicros)}`);
45
46
  log(` Monthly reserved ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.reservedUsageMicros)}`);
46
47
  log(` Monthly remaining ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.remainingUsageMicros)}`);
@@ -23,8 +23,9 @@ class BrowserInstall extends core_1.Command {
23
23
  async run() {
24
24
  const { flags } = await this.parse(BrowserInstall);
25
25
  const args = ['install'];
26
- // Any spelling of the first-party harness is accepted here and handed to the
27
- // daemon in the spelling every installed daemon understands.
26
+ // Any ACCEPTED spelling of the first-party harness is handed to the daemon in
27
+ // the wire spelling; anything else — a retired spelling included — is forwarded
28
+ // unchanged for the daemon's own parser to accept or refuse.
28
29
  if (flags.harness)
29
30
  args.push('--harness', (0, harness_provider_input_1.daemonHarnessArgument)(flags.harness));
30
31
  if (flags.config)
@@ -23,8 +23,9 @@ class BrowserUninstall extends core_1.Command {
23
23
  async run() {
24
24
  const { flags } = await this.parse(BrowserUninstall);
25
25
  const args = ['uninstall'];
26
- // Any spelling of the first-party harness is accepted here and handed to the
27
- // daemon in the spelling every installed daemon understands.
26
+ // Any ACCEPTED spelling of the first-party harness is handed to the daemon in
27
+ // the wire spelling; anything else — a retired spelling included — is forwarded
28
+ // unchanged for the daemon's own parser to accept or refuse.
28
29
  if (flags.harness)
29
30
  args.push('--harness', (0, harness_provider_input_1.daemonHarnessArgument)(flags.harness));
30
31
  if (flags.config)
@@ -37,6 +37,7 @@ export default class CodeHandover extends BaseCommand {
37
37
  repo: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
38
38
  base: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
39
39
  'allow-dirty': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
40
+ browser: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
40
41
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
41
42
  };
42
43
  /** Run git, returning null rather than throwing — every caller has a fallback. */
@@ -61,6 +61,9 @@ class CodeHandover extends base_command_1.BaseCommand {
61
61
  description: 'Hand over anyway when the working tree is dirty or the branch is unpushed. The cloud agent will NOT see that work.',
62
62
  default: false,
63
63
  }),
64
+ browser: core_1.Flags.boolean({
65
+ description: 'Give the receiving agent a browser. Refused where the deployment has no browser runtime, never run without one.',
66
+ }),
64
67
  json: core_1.Flags.boolean({ description: 'Output the created task as JSON' }),
65
68
  };
66
69
  /** Run git, returning null rather than throwing — every caller has a fallback. */
@@ -152,6 +155,7 @@ class CodeHandover extends base_command_1.BaseCommand {
152
155
  baseBranch: branch,
153
156
  prompt,
154
157
  machineProduct: 'hosted-job-machine',
158
+ ...(flags.browser ? { browser: true } : {}),
155
159
  clientRequestId: (0, node_crypto_1.randomUUID)(),
156
160
  });
157
161
  }
@@ -201,8 +201,9 @@ class CodeInstall extends base_command_1.BaseCommand {
201
201
  async installerNote(daemonId) {
202
202
  try {
203
203
  const res = await (0, api_fetch_1.apiFetch)(`/api/daemons/${encodeURIComponent(daemonId)}/cli-installers`, { credential: this.resolvedCredential });
204
- // Any spelling: a daemon lists the provider the way it registered it, and
205
- // one that predates or postdates a rename spells it differently.
204
+ // Any accepted spelling: a daemon lists the provider the way it registered it,
205
+ // and during a rename's alias window one that predates or postdates the flip
206
+ // spells it differently.
206
207
  const row = (res.providers ?? []).find((p) => (0, auth_core_1.isFirstPartyHarnessProvider)(p.provider));
207
208
  const reason = row?.installDisabledReason?.trim();
208
209
  if (!reason)
@@ -22,6 +22,7 @@ export default class CodeJobsRun extends BaseCommand {
22
22
  prompt: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
23
  'prompt-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
24
  egress: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
25
+ browser: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
25
26
  wait: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
27
  interval: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
27
28
  timeout: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -40,6 +40,9 @@ class CodeJobsRun extends base_command_1.BaseCommand {
40
40
  description: 'Network reach. Default `isolated` — the control plane only, nothing else.',
41
41
  options: ['isolated', 'allowlist', 'open'],
42
42
  }),
43
+ browser: core_1.Flags.boolean({
44
+ description: 'Run the job with a browser. Refused where the deployment has no browser runtime, never run without one.',
45
+ }),
43
46
  wait: core_1.Flags.boolean({ description: 'Wait for the job to finish and print its result' }),
44
47
  interval: core_1.Flags.integer({ description: 'Poll interval in seconds while waiting', default: 5 }),
45
48
  timeout: core_1.Flags.integer({ description: 'Give up waiting after this many minutes', default: 30 }),
@@ -88,6 +91,7 @@ class CodeJobsRun extends base_command_1.BaseCommand {
88
91
  mode: 'bare',
89
92
  machineProduct: 'hosted-job-machine',
90
93
  ...(flags.egress ? { egressPolicy: flags.egress } : {}),
94
+ ...(flags.browser ? { browser: true } : {}),
91
95
  clientRequestId: (0, node_crypto_1.randomUUID)(),
92
96
  });
93
97
  task = res?.task;
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
5
5
  const daemon_binding_1 = require("../../lib/daemon-binding");
6
6
  const daemon_installer_1 = require("../../lib/daemon-installer");
7
+ const daemon_setup_1 = require("../../lib/daemon-setup");
7
8
  /**
8
9
  * `skrr daemon install` — install the local runtime's OS service.
9
10
  *
@@ -52,6 +53,12 @@ class DaemonInstall extends core_1.Command {
52
53
  // own comment records `skrr code run -m <model>` dying the same way. Same
53
54
  // mistake, one directory over.
54
55
  const argv = this.argv;
56
+ // Before fetching anything: a machine still carrying the previous product
57
+ // identity's state is refused by the runtime's own install, so downloading a
58
+ // release first only delays the same refusal (OSK-6895).
59
+ if ((0, daemon_setup_1.refuseOnLegacyLocalState)((line) => this.logToStderr(line))) {
60
+ this.exit(1);
61
+ }
55
62
  // Bootstrap only when there is nothing to forward to. An existing runtime —
56
63
  // brew, the script installer, a dev checkout — is left exactly alone; this
57
64
  // is a floor under the missing case, not a second update channel competing
@@ -46,6 +46,17 @@ export default class HarnessesInstall extends BaseCommand {
46
46
  * reason the daemon already words. Both are answered before anything runs.
47
47
  */
48
48
  private update;
49
+ /**
50
+ * Start an install or update, and survive a gateway that cut the request off.
51
+ *
52
+ * The relay holds the request while the daemon answers, and a gateway can end
53
+ * it first: an update returned HTTP 504 after 21.9s while the job it started
54
+ * ran to success, and the caller was told the outcome was unknown (OSK-10096
55
+ * verifier). The daemon is the only party that knows whether the request
56
+ * arrived, so on a server-side failure we ask it, and follow the job it
57
+ * started rather than reporting a result we do not have.
58
+ */
59
+ private start;
49
60
  /** Follow a job to its end, or report why it could not be followed (and return nothing). */
50
61
  private follow;
51
62
  /** What the daemon says about this provider right now, best effort. */
@@ -74,14 +74,9 @@ class HarnessesInstall extends base_command_1.BaseCommand {
74
74
  await this.update(daemonId, provider, Boolean(flags.json));
75
75
  return;
76
76
  }
77
- let start;
78
- try {
79
- start = await (0, cli_installers_1.startManagedCliInstall)(daemonId, provider, this.resolvedCredential);
80
- }
81
- catch (err) {
82
- this.handleApiError(err);
77
+ const start = await this.start(daemonId, provider, () => (0, cli_installers_1.startManagedCliInstall)(daemonId, provider, this.resolvedCredential), Boolean(flags.json));
78
+ if (!start)
83
79
  return;
84
- }
85
80
  const jobId = start.job?.id;
86
81
  if (!start.started || !jobId) {
87
82
  // The daemon declined and already knows why. Read its own reason off the
@@ -163,14 +158,9 @@ class HarnessesInstall extends base_command_1.BaseCommand {
163
158
  });
164
159
  return;
165
160
  }
166
- let start;
167
- try {
168
- start = await (0, cli_installers_1.startManagedCliUpdate)(daemonId, provider, this.resolvedCredential);
169
- }
170
- catch (err) {
171
- this.handleApiError(err);
161
+ const start = await this.start(daemonId, provider, () => (0, cli_installers_1.startManagedCliUpdate)(daemonId, provider, this.resolvedCredential), json);
162
+ if (!start)
172
163
  return;
173
- }
174
164
  const jobId = start.job?.id;
175
165
  if (!start.started || !jobId) {
176
166
  this.log(start.alreadyRunning
@@ -199,6 +189,35 @@ class HarnessesInstall extends base_command_1.BaseCommand {
199
189
  this.log((0, cli_installers_1.updateOutcomeLine)(job) ??
200
190
  `${provider} updated${job.installedVersion ? ` to ${job.installedVersion}` : ''}.`);
201
191
  }
192
+ /**
193
+ * Start an install or update, and survive a gateway that cut the request off.
194
+ *
195
+ * The relay holds the request while the daemon answers, and a gateway can end
196
+ * it first: an update returned HTTP 504 after 21.9s while the job it started
197
+ * ran to success, and the caller was told the outcome was unknown (OSK-10096
198
+ * verifier). The daemon is the only party that knows whether the request
199
+ * arrived, so on a server-side failure we ask it, and follow the job it
200
+ * started rather than reporting a result we do not have.
201
+ */
202
+ async start(daemonId, provider, begin, json) {
203
+ try {
204
+ return await begin();
205
+ }
206
+ catch (err) {
207
+ const status = err.status ?? 0;
208
+ if (status >= 500 || status === 408) {
209
+ const job = await (0, cli_installers_1.jobStartedDespiteTimeout)(daemonId, provider, this.resolvedCredential);
210
+ if (job?.id) {
211
+ if (!json) {
212
+ this.logToStderr(`The request to ${daemonId} did not come back (HTTP ${status}), but the daemon started job ${job.id}. Following it.`);
213
+ }
214
+ return { started: true, job };
215
+ }
216
+ }
217
+ this.handleApiError(err);
218
+ return undefined;
219
+ }
220
+ }
202
221
  /** Follow a job to its end, or report why it could not be followed (and return nothing). */
203
222
  async follow(daemonId, jobId, verb, json) {
204
223
  const outcome = await (0, cli_installers_1.followManagedCliInstallJob)({
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ import { type ManagedCliProviderState } from '../../lib/cli-installers';
2
3
  /**
3
4
  * What each managed provider's state is on one machine.
4
5
  *
@@ -43,3 +44,12 @@ export default class HarnessesInstallers extends BaseCommand {
43
44
  */
44
45
  private daemonIdFor;
45
46
  }
47
+ /**
48
+ * What each installed CLI's update story is, as sentences under the table.
49
+ *
50
+ * Sentences, not a column: an update reason is "the copy inside Devin.app
51
+ * updates with that app", which no column fits. Silent for a daemon that
52
+ * reports no update fields at all — it cannot say, and inventing "no" for it
53
+ * would be worse than nothing.
54
+ */
55
+ export declare function updateNotes(providers: readonly ManagedCliProviderState[], bin: string): string[];
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateNotes = updateNotes;
3
4
  const core_1 = require("@oclif/core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
@@ -126,6 +127,14 @@ class HarnessesInstallers extends base_command_1.BaseCommand {
126
127
  for (const note of notes)
127
128
  this.log(` ${note.provider}: ${note.detail}`);
128
129
  }
130
+ // The VERSION column is where a reader asks "can I get a newer one", and
131
+ // the answer lived only in `--json` (OSK-10096 verifier).
132
+ const updateLines = updateNotes(providers, this.config.bin);
133
+ if (updateLines.length > 0) {
134
+ this.log('');
135
+ for (const line of updateLines)
136
+ this.log(` ${line}`);
137
+ }
129
138
  // A provider the platform knows and this daemon did not mention. Without
130
139
  // this it simply is not on the list, which reads as dropped support rather
131
140
  // than as an older daemon — `devin` arrived in the 0.8.45 daemon release, so 0.8.35
@@ -177,3 +186,25 @@ class HarnessesInstallers extends base_command_1.BaseCommand {
177
186
  }
178
187
  }
179
188
  exports.default = HarnessesInstallers;
189
+ /**
190
+ * What each installed CLI's update story is, as sentences under the table.
191
+ *
192
+ * Sentences, not a column: an update reason is "the copy inside Devin.app
193
+ * updates with that app", which no column fits. Silent for a daemon that
194
+ * reports no update fields at all — it cannot say, and inventing "no" for it
195
+ * would be worse than nothing.
196
+ */
197
+ function updateNotes(providers, bin) {
198
+ const lines = [];
199
+ for (const provider of providers) {
200
+ if (provider.updatable === true) {
201
+ lines.push(`${provider.provider}: update it with \`${bin} harnesses install ${provider.provider} --update\``);
202
+ }
203
+ }
204
+ for (const provider of providers) {
205
+ if (provider.updatable === false && provider.installed && provider.updateDisabledReason) {
206
+ lines.push(`${provider.provider}: ${provider.updateDisabledReason}`);
207
+ }
208
+ }
209
+ return lines;
210
+ }
@@ -82,6 +82,20 @@ class HarnessesLeasesShow extends base_command_1.BaseCommand {
82
82
  line('Updated:', lease.updatedAt);
83
83
  // Money last, and only when the server sent it — an absent billing block
84
84
  // and a zero are different states.
85
+ //
86
+ // Who pays comes first. `harnesses leases billing` used to be "change who
87
+ // pays", so there was a way to move the payer (which never worked) and no
88
+ // way to read it (OSK-4628). The payer is fixed at create, and the lease
89
+ // carries it as `workspaceId`: the workspace account it bills, or `null`
90
+ // for the owner's personal account — the same scope the monthly cap below
91
+ // is counted in. A server that does not send the key says nothing here,
92
+ // because a missing field is not evidence of a personal lease.
93
+ if (Object.prototype.hasOwnProperty.call(lease, 'workspaceId')) {
94
+ const workspaceId = typeof lease.workspaceId === 'string' ? lease.workspaceId.trim() : '';
95
+ line('Billed to:', workspaceId
96
+ ? `workspace ${workspaceId} (\`${this.config.bin} balance show --workspace ${workspaceId}\`)`
97
+ : 'your personal account');
98
+ }
85
99
  line('Billing:', nested(lease.billing, 'billableState'));
86
100
  // The monthly cap, which is what actually refuses a Hosted Machine.
87
101
  //
@@ -421,8 +421,9 @@ class HarnessesList extends base_command_1.BaseCommand {
421
421
  // mode. Materialize that default before the request so JSON cannot quietly
422
422
  // return every historical offline row while the table shows seven live ones.
423
423
  const status = flags.online ? 'online' : (flags.status ?? (flags.all ? undefined : 'online'));
424
- // Any spelling of the first-party harness filters the same rows; the server is
425
- // asked for the canonical one, which it matches against every stored spelling.
424
+ // Any accepted spelling of the first-party harness filters the same rows; the
425
+ // server is asked for the canonical one, which it matches against every
426
+ // accepted stored spelling.
426
427
  const provider = flags.provider ? (0, harness_provider_input_1.canonicalHarnessInput)(flags.provider) : undefined;
427
428
  const params = new URLSearchParams();
428
429
  const serverStatus = serverStatusParam(status);
@@ -1,4 +1,26 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ /** The target a `TARGET_CONFLICT` refusal names, as the API sends it. */
3
+ export interface ConflictingTarget {
4
+ id: string;
5
+ desiredState?: string;
6
+ status?: string;
7
+ }
8
+ export declare function conflictingTargetOf(body: Record<string, unknown> | undefined): ConflictingTarget | null;
9
+ /**
10
+ * What to do about a placement that already has a target, as commands.
11
+ *
12
+ * Re-running an install whose first attempt had failed printed "An unexpected
13
+ * error occurred. Code: TARGET_CONFLICT" (OSK-4631): no target, no path, no next
14
+ * step, and no word on whether the re-run had partly applied. The API now names
15
+ * the target in the way; this turns that into the commands for its state. A live
16
+ * target is changed or removed, never installed twice; one being removed can be
17
+ * restored, or purged once its daemon confirms cleanup.
18
+ */
19
+ export declare function describeTargetConflict(target: ConflictingTarget, { bin, placement, created }: {
20
+ bin: string;
21
+ placement: string;
22
+ created: string[];
23
+ }): string;
2
24
  export default class InstructionsInstall extends BaseCommand {
3
25
  static description: string;
4
26
  static flags: {