@skrr-ai/cli 0.1.24 → 0.1.26

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 (87) hide show
  1. package/dist/base-command.d.ts +4 -4
  2. package/dist/base-command.js +11 -10
  3. package/dist/commands/code/install.js +13 -0
  4. package/dist/commands/harnesses/leases/billing.d.ts +9 -6
  5. package/dist/commands/harnesses/leases/billing.js +19 -24
  6. package/dist/commands/machines/dedicated/attach.d.ts +28 -0
  7. package/dist/commands/machines/dedicated/attach.js +75 -0
  8. package/dist/commands/machines/dedicated/catalog.d.ts +17 -0
  9. package/dist/commands/machines/dedicated/catalog.js +75 -0
  10. package/dist/commands/machines/dedicated/cp.d.ts +22 -0
  11. package/dist/commands/machines/dedicated/cp.js +76 -0
  12. package/dist/commands/machines/dedicated/create.d.ts +6 -2
  13. package/dist/commands/machines/dedicated/create.js +29 -20
  14. package/dist/commands/machines/dedicated/destroy.d.ts +6 -2
  15. package/dist/commands/machines/dedicated/destroy.js +41 -24
  16. package/dist/commands/machines/dedicated/exec.d.ts +28 -0
  17. package/dist/commands/machines/dedicated/exec.js +105 -0
  18. package/dist/commands/machines/dedicated/grow.d.ts +6 -2
  19. package/dist/commands/machines/dedicated/grow.js +14 -19
  20. package/dist/commands/machines/dedicated/health-check.d.ts +2 -2
  21. package/dist/commands/machines/dedicated/health-check.js +16 -14
  22. package/dist/commands/machines/dedicated/incident-close.d.ts +3 -2
  23. package/dist/commands/machines/dedicated/incident-close.js +11 -14
  24. package/dist/commands/machines/dedicated/index.js +37 -16
  25. package/dist/commands/machines/dedicated/list.d.ts +2 -2
  26. package/dist/commands/machines/dedicated/list.js +28 -17
  27. package/dist/commands/machines/dedicated/restart.d.ts +5 -2
  28. package/dist/commands/machines/dedicated/restart.js +8 -19
  29. package/dist/commands/machines/dedicated/show.d.ts +2 -2
  30. package/dist/commands/machines/dedicated/show.js +28 -30
  31. package/dist/commands/machines/dedicated/sign-in.d.ts +19 -0
  32. package/dist/commands/machines/dedicated/sign-in.js +49 -0
  33. package/dist/commands/machines/dedicated/snapshot.d.ts +5 -2
  34. package/dist/commands/machines/dedicated/snapshot.js +11 -19
  35. package/dist/commands/machines/dedicated/spending-limit.d.ts +22 -0
  36. package/dist/commands/machines/dedicated/spending-limit.js +50 -0
  37. package/dist/commands/machines/dedicated/start.d.ts +5 -2
  38. package/dist/commands/machines/dedicated/start.js +8 -19
  39. package/dist/commands/machines/dedicated/stop.d.ts +5 -2
  40. package/dist/commands/machines/dedicated/stop.js +8 -19
  41. package/dist/commands/machines/dedicated/terminal.d.ts +22 -0
  42. package/dist/commands/machines/dedicated/terminal.js +42 -0
  43. package/dist/commands/spaces/auto-executor/preview.d.ts +8 -0
  44. package/dist/commands/spaces/auto-executor/preview.js +41 -0
  45. package/dist/commands/spaces/task-rollups.d.ts +14 -0
  46. package/dist/commands/spaces/task-rollups.js +66 -0
  47. package/dist/commands/spaces/work-sync/telemetry.js +22 -0
  48. package/dist/commands/tasks/assign.js +1 -1
  49. package/dist/commands/tasks/create.js +10 -4
  50. package/dist/commands/tasks/list.js +27 -0
  51. package/dist/lib/api-fetch.js +9 -1
  52. package/dist/lib/assignee-resolver.d.ts +7 -6
  53. package/dist/lib/daemonBroker.d.ts +28 -0
  54. package/dist/lib/daemonBroker.js +60 -19
  55. package/dist/lib/dedicated-copy.d.ts +22 -0
  56. package/dist/lib/dedicated-copy.js +115 -0
  57. package/dist/lib/dedicated-lease-command.d.ts +50 -0
  58. package/dist/lib/dedicated-lease-command.js +158 -0
  59. package/dist/lib/dedicated-machines.d.ts +248 -12
  60. package/dist/lib/dedicated-machines.js +385 -25
  61. package/dist/lib/dedicated-terminal.d.ts +89 -0
  62. package/dist/lib/dedicated-terminal.js +275 -0
  63. package/dist/lib/dedicated-wait.d.ts +72 -0
  64. package/dist/lib/dedicated-wait.js +195 -0
  65. package/dist/lib/harnesses.d.ts +6 -0
  66. package/dist/lib/idempotency-scope.d.ts +10 -0
  67. package/dist/lib/idempotency-scope.js +16 -0
  68. package/dist/lib/node-adapter.d.ts +1 -1
  69. package/dist/lib/node-adapter.js +13 -11
  70. package/dist/lib/sky-code.d.ts +30 -1
  71. package/dist/lib/sky-code.js +44 -2
  72. package/dist/lib/triggers.js +7 -0
  73. package/dist/lib/work-sync.d.ts +35 -0
  74. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  75. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +2 -1
  76. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/linux.js +71 -21
  77. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +10 -0
  78. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +17 -1
  79. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  80. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  81. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/linux.js +71 -21
  82. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +10 -0
  83. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +16 -0
  84. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  85. package/dist/node_modules/@skrr-ai/data-provider/index.js +2657 -2596
  86. package/oclif.manifest.json +5075 -4294
  87. package/package.json +1 -1
@@ -199,10 +199,10 @@ export declare abstract class BaseCommand extends Command {
199
199
  *
200
200
  * Shared by every command that accepts an assignee reference. A failure is
201
201
  * fatal rather than a silent fall-back to a global directory search: the
202
- * server only accepts assignees who are members of the space
203
- * (`ASSIGNEE_NOT_IN_SPACE`), so widening the lookup could only ever produce
204
- * a request guaranteed to be rejected — or, worse, resolve a typo to a
205
- * stranger the caller never intended to name.
202
+ * server accepts space members, plus only the caller's own unshared agents
203
+ * (`AgentTaskConsent`; anything else is `ASSIGNEE_NOT_IN_SPACE`), so widening
204
+ * the lookup could only ever produce a request the server refuses — or,
205
+ * worse, resolve a typo to a stranger the caller never intended to name.
206
206
  */
207
207
  protected loadSpaceMembers(spaceId: string): Promise<SpaceMember[]>;
208
208
  /**
@@ -284,11 +284,10 @@ class BaseCommand extends core_1.Command {
284
284
  const accessToken = autoResult.outcome.accessToken;
285
285
  this.resolvedCredential = {
286
286
  token: accessToken,
287
- // Best-effort source label. `writeToBackend` routes to
288
- // Keychain on darwin, file elsewhere; we don't re-probe
289
- // here, but a follow-up `skrr whoami` reads the actual
290
- // backend so the label there is authoritative.
291
- source: 'keychain',
287
+ // The backend the broker actually wrote to. Hard-coding
288
+ // 'keychain' labelled a Linux credential (always the file)
289
+ // "macOS Keychain" in the same process that minted it.
290
+ source: autoResult.outcome.storedIn === 'keychain' ? 'keychain' : 'file',
292
291
  kind: (0, auth_core_1.classifyTokenKind)(accessToken),
293
292
  };
294
293
  }
@@ -645,10 +644,10 @@ class BaseCommand extends core_1.Command {
645
644
  *
646
645
  * Shared by every command that accepts an assignee reference. A failure is
647
646
  * fatal rather than a silent fall-back to a global directory search: the
648
- * server only accepts assignees who are members of the space
649
- * (`ASSIGNEE_NOT_IN_SPACE`), so widening the lookup could only ever produce
650
- * a request guaranteed to be rejected — or, worse, resolve a typo to a
651
- * stranger the caller never intended to name.
647
+ * server accepts space members, plus only the caller's own unshared agents
648
+ * (`AgentTaskConsent`; anything else is `ASSIGNEE_NOT_IN_SPACE`), so widening
649
+ * the lookup could only ever produce a request the server refuses — or,
650
+ * worse, resolve a typo to a stranger the caller never intended to name.
652
651
  */
653
652
  async loadSpaceMembers(spaceId) {
654
653
  let response;
@@ -1376,8 +1375,10 @@ function describeAssigneeNotInSpace(serverMessage, bin = 'skrr') {
1376
1375
  const base = serverMessage?.trim() || 'That assignee is not a member of this space.';
1377
1376
  if (base.includes(`${bin} spaces members add`))
1378
1377
  return base;
1378
+ // Two ways forward, because the server accepts two consents: membership, or
1379
+ // an owner assigning their own unshared agent to one task (AgentTaskConsent).
1379
1380
  return (`${base}\n → add it to the space with \`${bin} spaces members add\`, ` +
1380
- 'or assign someone already in it.');
1381
+ 'assign someone already in it, or — for your own unshared agent — assign it yourself.');
1381
1382
  }
1382
1383
  /**
1383
1384
  * Server-declared TERMINAL refusals: a state conflict the caller must resolve
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  const base_command_1 = require("../../base-command");
5
5
  const api_fetch_1 = require("../../lib/api-fetch");
6
6
  const daemon_ref_1 = require("../../lib/daemon-ref");
7
+ const sky_code_1 = require("../../lib/sky-code");
7
8
  /**
8
9
  * `skrr code install` — install the engine from the terminal.
9
10
  *
@@ -47,6 +48,18 @@ class CodeInstall extends base_command_1.BaseCommand {
47
48
  };
48
49
  async run() {
49
50
  const { flags } = await this.parse(CodeInstall);
51
+ // Refused locally, before choosing a daemon. The daemon and the API refuse
52
+ // too, but with several active daemons this command would first ask which
53
+ // one — and "pick a machine" is the wrong question to put to someone whose
54
+ // answer is "none of them can do this here".
55
+ if ((0, sky_code_1.isDedicatedRuntimeWorkload)()) {
56
+ this.failWithCliError({
57
+ message: sky_code_1.DEDICATED_RUNTIME_ENGINE_MESSAGE,
58
+ code: 'DEDICATED_RUNTIME_ENGINE_SHIPS_WITH_IMAGE',
59
+ exit: 1,
60
+ retryable: false,
61
+ });
62
+ }
50
63
  const daemonId = await this.resolveDaemon(flags.daemon);
51
64
  let started;
52
65
  try {
@@ -1,10 +1,14 @@
1
1
  import { BaseCommand } from '../../../base-command';
2
2
  /**
3
- * Retarget who pays for a lease.
3
+ * Change a lease's monthly spending cap.
4
4
  *
5
- * This moves money, so the command refuses to guess: it sends exactly the axis
6
- * you named and nothing else. `--workspace` bills a workspace you belong to;
7
- * `--personal` moves it back to you.
5
+ * `PATCH /api/machines/leases/:id/billing` accepts `spendingLimitCents` and
6
+ * NOTHING else — its own comment says lifecycle billing state, usage and cost
7
+ * fields stay server-owned. This command used to be "change who pays", sending
8
+ * `{ workspaceId }`: no API moves a lease's payer, so every call was answered
9
+ * `INVALID_RUNTIME_SPENDING_LIMIT` for a field it never sent, while the parity
10
+ * check stayed green because it tests that the URL is reachable, not that the
11
+ * body means anything to it.
8
12
  */
9
13
  export default class HarnessesLeasesBilling extends BaseCommand {
10
14
  static description: string;
@@ -14,8 +18,7 @@ export default class HarnessesLeasesBilling extends BaseCommand {
14
18
  };
15
19
  static flags: {
16
20
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
- workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
- personal: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ 'spending-limit-cents': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
19
22
  };
20
23
  run(): Promise<void>;
21
24
  }
@@ -13,50 +13,45 @@ const core_1 = require("@oclif/core");
13
13
  const base_command_1 = require("../../../base-command");
14
14
  const harnesses_1 = require("../../../lib/harnesses");
15
15
  /**
16
- * Retarget who pays for a lease.
16
+ * Change a lease's monthly spending cap.
17
17
  *
18
- * This moves money, so the command refuses to guess: it sends exactly the axis
19
- * you named and nothing else. `--workspace` bills a workspace you belong to;
20
- * `--personal` moves it back to you.
18
+ * `PATCH /api/machines/leases/:id/billing` accepts `spendingLimitCents` and
19
+ * NOTHING else — its own comment says lifecycle billing state, usage and cost
20
+ * fields stay server-owned. This command used to be "change who pays", sending
21
+ * `{ workspaceId }`: no API moves a lease's payer, so every call was answered
22
+ * `INVALID_RUNTIME_SPENDING_LIMIT` for a field it never sent, while the parity
23
+ * check stayed green because it tests that the URL is reachable, not that the
24
+ * body means anything to it.
21
25
  */
22
26
  class HarnessesLeasesBilling extends base_command_1.BaseCommand {
23
- static description = 'Change who pays for a harness lease';
27
+ static description = 'Change the monthly spending cap of a hosted lease (Hosted Machine or Dedicated Runtime)';
24
28
  static examples = [
25
- '<%= config.bin %> harnesses leases billing <lease-id> --workspace <workspace-id>',
26
- '<%= config.bin %> harnesses leases billing <lease-id> --personal',
29
+ '<%= config.bin %> harnesses leases billing <lease-id> --spending-limit-cents 30000',
30
+ '<%= config.bin %> harnesses leases billing <lease-id> --spending-limit-cents 5000 --json',
27
31
  ];
28
32
  static args = {
29
33
  lease: core_1.Args.string({ ignoreStdin: true, description: 'Lease ID', required: true }),
30
34
  };
31
35
  static flags = {
32
36
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
33
- workspace: core_1.Flags.string({
34
- description: 'Bill this workspace instead',
35
- exclusive: ['personal'],
36
- }),
37
- personal: core_1.Flags.boolean({
38
- description: 'Bill you personally instead of a workspace',
39
- exclusive: ['workspace'],
37
+ 'spending-limit-cents': core_1.Flags.integer({
38
+ description: 'New monthly cap in cents. Refused below what the month has already spent or is projected to. Who pays for a lease is set at create and cannot be changed.',
39
+ required: true,
40
+ min: 1,
40
41
  }),
41
42
  };
42
43
  async run() {
43
44
  this.requireAuth();
44
45
  const { args, flags } = await this.parse(HarnessesLeasesBilling);
45
- if (!flags.workspace && !flags.personal) {
46
- this.error('Name the new payer: --workspace <id> or --personal.', { exit: 1 });
47
- return;
48
- }
49
- // `null` is the explicit "no workspace pays this" signal, not an omission.
50
- const body = { workspaceId: flags.personal ? null : flags.workspace };
51
46
  try {
52
- const response = await (0, harnesses_1.setHarnessLeaseBilling)(args.lease, body);
47
+ const response = await (0, harnesses_1.setHarnessLeaseBilling)(args.lease, {
48
+ spendingLimitCents: flags['spending-limit-cents'],
49
+ });
53
50
  if (flags.json) {
54
51
  this.log(JSON.stringify(response, null, 2));
55
52
  return;
56
53
  }
57
- this.log(flags.personal
58
- ? `${args.lease} now bills you personally.`
59
- : `${args.lease} now bills workspace ${flags.workspace}.`);
54
+ this.log(`${args.lease} monthly spending cap set to $${(flags['spending-limit-cents'] / 100).toFixed(2)}.`);
60
55
  }
61
56
  catch (err) {
62
57
  this.handleApiError(err);
@@ -0,0 +1,28 @@
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ /**
3
+ * `skrr machines dedicated attach` — run an agent on this machine.
4
+ *
5
+ * The only way to put an agent's turns on a Dedicated Runtime is to pin it to a
6
+ * harness row the machine's daemon registered, and the lease payload names no
7
+ * daemon, so the caller had to cross-reference `harnesses list` by metadata the
8
+ * table does not print. The admission error even told users to "attach it",
9
+ * naming a command that did not exist.
10
+ *
11
+ * This resolves the row from the lease and hands off to `agents attach-harness`,
12
+ * so execution-order semantics, agent resolution and task-pin migration stay in
13
+ * the one command that owns them.
14
+ */
15
+ export default class DedicatedMachinesAttach extends DedicatedLeaseCommand {
16
+ static description: string;
17
+ static examples: string[];
18
+ static args: {
19
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
20
+ };
21
+ static flags: {
22
+ agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
+ engine: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
+ 'rebind-tasks': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
25
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
+ };
27
+ run(): Promise<void>;
28
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@oclif/core");
7
+ const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
8
+ const dedicated_machines_1 = require("../../../lib/dedicated-machines");
9
+ const harnesses_1 = require("../../../lib/harnesses");
10
+ const attach_harness_1 = __importDefault(require("../../agents/attach-harness"));
11
+ /**
12
+ * `skrr machines dedicated attach` — run an agent on this machine.
13
+ *
14
+ * The only way to put an agent's turns on a Dedicated Runtime is to pin it to a
15
+ * harness row the machine's daemon registered, and the lease payload names no
16
+ * daemon, so the caller had to cross-reference `harnesses list` by metadata the
17
+ * table does not print. The admission error even told users to "attach it",
18
+ * naming a command that did not exist.
19
+ *
20
+ * This resolves the row from the lease and hands off to `agents attach-harness`,
21
+ * so execution-order semantics, agent resolution and task-pin migration stay in
22
+ * the one command that owns them.
23
+ */
24
+ class DedicatedMachinesAttach extends dedicated_lease_command_1.DedicatedLeaseCommand {
25
+ static description = 'Pin an agent to a Dedicated Runtime so its turns and tasks run on that machine, under the engine you choose';
26
+ static examples = [
27
+ '<%= config.bin %> machines dedicated attach <lease-id> --agent <agent-id>',
28
+ '<%= config.bin %> machines dedicated attach <lease-id> --agent <agent-id> --engine claude --rebind-tasks',
29
+ '<%= config.bin %> machines dedicated attach <lease-id> --agent <agent-id> --engine codex --json',
30
+ ];
31
+ static args = {
32
+ lease: core_1.Args.string({
33
+ description: 'Dedicated Runtime lease id',
34
+ required: true,
35
+ ignoreStdin: true,
36
+ }),
37
+ };
38
+ static flags = {
39
+ agent: core_1.Flags.string({
40
+ description: 'Agent to attach. Inside an agent session it defaults to that agent (OVERSKY_SESSION_AGENT_ID).',
41
+ }),
42
+ engine: core_1.Flags.string({
43
+ description: 'Which engine row to attach when the machine registered several (claude, codex, sky-code, oversky). Required only when it is ambiguous.',
44
+ }),
45
+ 'rebind-tasks': core_1.Flags.boolean({
46
+ description: 'Publish the updated agent release and move dormant assigned task pins to it. Running and finished tasks are never changed.',
47
+ }),
48
+ json: core_1.Flags.boolean({ description: 'Output the attachment result as JSON' }),
49
+ };
50
+ async run() {
51
+ this.requireAuth();
52
+ const { args, flags } = await this.parse(DedicatedMachinesAttach);
53
+ const response = await this.callDedicated(() => (0, dedicated_machines_1.getDedicatedRuntime)(args.lease, this.scopeFrom(flags)));
54
+ const lease = response?.lease;
55
+ if (!lease)
56
+ this.error('Dedicated Runtime lease not found.', { exit: 1 });
57
+ const listed = await this.callDedicated(() => (0, harnesses_1.listHarnesses)());
58
+ const selection = (0, dedicated_machines_1.selectDedicatedAttachHarness)(lease, listed?.harnesses ?? [], flags.engine);
59
+ if ('error' in selection) {
60
+ this.error(selection.error, { exit: 1 });
61
+ }
62
+ const { harness } = selection;
63
+ if (!flags.json) {
64
+ process.stderr.write(`Attaching ${harness.provider} on ${args.lease} (harness ${harness._id}, ${harness.status || 'status unknown'}).\n` +
65
+ 'Everything the agent runs there acts as you: it shares the machine’s sign-ins and your skrr login.\n');
66
+ }
67
+ await attach_harness_1.default.run([
68
+ harness._id,
69
+ ...(flags.agent ? ['--agent', flags.agent] : []),
70
+ ...(flags['rebind-tasks'] ? ['--rebind-tasks'] : []),
71
+ ...(flags.json ? ['--json'] : []),
72
+ ], this.config);
73
+ }
74
+ }
75
+ exports.default = DedicatedMachinesAttach;
@@ -0,0 +1,17 @@
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ /**
3
+ * `skrr machines dedicated catalog` — what `create` will accept, and what it costs.
4
+ *
5
+ * `--size-preset`, `--storage-gb`, `--retention-class` and `--backup-profile`
6
+ * carry no client-side values on purpose: presets are gated by server flags and
7
+ * the storage bounds are configuration. That made `create --help` point at
8
+ * `harnesses products`, whose table shows none of them without `--json`.
9
+ */
10
+ export default class DedicatedMachinesCatalog extends DedicatedLeaseCommand {
11
+ static description: string;
12
+ static examples: string[];
13
+ static flags: {
14
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
5
+ const dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ const harnesses_1 = require("../../../lib/harnesses");
7
+ /**
8
+ * `skrr machines dedicated catalog` — what `create` will accept, and what it costs.
9
+ *
10
+ * `--size-preset`, `--storage-gb`, `--retention-class` and `--backup-profile`
11
+ * carry no client-side values on purpose: presets are gated by server flags and
12
+ * the storage bounds are configuration. That made `create --help` point at
13
+ * `harnesses products`, whose table shows none of them without `--json`.
14
+ */
15
+ class DedicatedMachinesCatalog extends dedicated_lease_command_1.DedicatedLeaseCommand {
16
+ static description = 'Show the Dedicated Runtime sizes, storage bounds, retention classes, backup profiles and prices that create accepts';
17
+ static examples = [
18
+ '<%= config.bin %> machines dedicated catalog',
19
+ '<%= config.bin %> machines dedicated catalog --json',
20
+ ];
21
+ static flags = {
22
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
23
+ };
24
+ async run() {
25
+ this.requireAuth();
26
+ const { flags } = await this.parse(DedicatedMachinesCatalog);
27
+ const payload = await this.callDedicated(() => (0, harnesses_1.listHarnessProducts)());
28
+ const catalog = (0, dedicated_machines_1.dedicatedCatalogFromProducts)(payload);
29
+ if (!catalog) {
30
+ this.error('This server does not offer Dedicated Runtime.', { exit: 1 });
31
+ }
32
+ if (flags.json) {
33
+ this.log(JSON.stringify({ catalog }, null, 2));
34
+ return;
35
+ }
36
+ this.log(`Dedicated Runtime — ${catalog.availability}`);
37
+ if (catalog.disabledReason)
38
+ this.log(` ${catalog.disabledReason}`);
39
+ if (catalog.headline)
40
+ this.log(` ${catalog.headline}`);
41
+ this.log('\nSizes (--size-preset)');
42
+ if (catalog.sizes.length === 0)
43
+ this.log(' none offered right now');
44
+ for (const size of catalog.sizes) {
45
+ const spec = [
46
+ size.vcpus ? `${size.vcpus} vCPU` : '',
47
+ size.memoryGiB ? `${size.memoryGiB} GiB RAM` : '',
48
+ size.accelerator || '',
49
+ ]
50
+ .filter(Boolean)
51
+ .join(' · ');
52
+ this.log(` ${String(size.id).padEnd(14)}${spec}`);
53
+ }
54
+ if (catalog.storage) {
55
+ const { minGb, defaultGb, maxGb } = catalog.storage;
56
+ this.log(`\nWorkspace (--storage-gb)\n ${minGb}–${maxGb} GiB, default ${defaultGb}; grow later, never shrink`);
57
+ }
58
+ if (catalog.retentionClasses.length) {
59
+ this.log(`\nRetention (--retention-class)\n ${catalog.retentionClasses.join(', ')}`);
60
+ }
61
+ if (catalog.backupProfiles.length) {
62
+ this.log(`\nBackup (--backup-profile)\n ${catalog.backupProfiles.join(', ')}`);
63
+ }
64
+ if (catalog.regionClasses.length) {
65
+ this.log(`\nRegion (--region-class)\n ${catalog.regionClasses.join(', ')}`);
66
+ }
67
+ this.log('\nPrices');
68
+ if (catalog.priceLines.length === 0)
69
+ this.log(' not published by this server');
70
+ for (const line of catalog.priceLines) {
71
+ this.log(` ${line.display || `${(0, dedicated_machines_1.formatUsdCents)(line.amountCents)} ${line.label || ''}`}`);
72
+ }
73
+ }
74
+ }
75
+ exports.default = DedicatedMachinesCatalog;
@@ -0,0 +1,22 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * `skrr machines dedicated cp` — copy ONE file to or from a Dedicated Runtime.
4
+ *
5
+ * On the machine, paths resolve against /workspace and must be inside `repos/`
6
+ * or `user-data/`; the rest of the workspace belongs to the runtime. Files are
7
+ * limited to 64 MiB. See lib/dedicated-copy.ts for how neither end is ever left
8
+ * half-written.
9
+ */
10
+ export default class DedicatedMachinesCp extends BaseCommand {
11
+ static description: string;
12
+ static examples: string[];
13
+ static args: {
14
+ source: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
15
+ destination: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
16
+ };
17
+ static flags: {
18
+ workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<void>;
22
+ }
@@ -0,0 +1,76 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ const dedicated_copy_1 = require("../../../lib/dedicated-copy");
7
+ /**
8
+ * `skrr machines dedicated cp` — copy ONE file to or from a Dedicated Runtime.
9
+ *
10
+ * On the machine, paths resolve against /workspace and must be inside `repos/`
11
+ * or `user-data/`; the rest of the workspace belongs to the runtime. Files are
12
+ * limited to 64 MiB. See lib/dedicated-copy.ts for how neither end is ever left
13
+ * half-written.
14
+ */
15
+ class DedicatedMachinesCp extends base_command_1.BaseCommand {
16
+ static description = 'Copy a file to or from a Dedicated Runtime machine';
17
+ static examples = [
18
+ '<%= config.bin %> machines dedicated cp ./dist/app.tar.gz rtl_abc123:repos/app/app.tar.gz',
19
+ '<%= config.bin %> machines dedicated cp rtl_abc123:repos/app/coverage/report.html ./report.html',
20
+ ];
21
+ // `ignoreStdin: true` on both, per the CLI's own hygiene rule: without it oclif
22
+ // will read a missing positional from stdin, so `skrr machines dedicated cp`
23
+ // typed with one argument hangs on a pipe instead of saying what it needs.
24
+ static args = {
25
+ source: core_1.Args.string({
26
+ description: 'Local path, or <lease-id>:<path> on the machine',
27
+ required: true,
28
+ ignoreStdin: true,
29
+ }),
30
+ destination: core_1.Args.string({
31
+ description: 'Local path, or <lease-id>:<path> on the machine',
32
+ required: true,
33
+ ignoreStdin: true,
34
+ }),
35
+ };
36
+ static flags = {
37
+ workspace: core_1.Flags.string({ description: 'Workspace scope' }),
38
+ json: core_1.Flags.boolean({ description: 'Output the result as JSON' }),
39
+ };
40
+ async run() {
41
+ this.requireAuth();
42
+ const { args, flags } = await this.parse(DedicatedMachinesCp);
43
+ let plan;
44
+ try {
45
+ plan = (0, dedicated_machines_1.parseDedicatedCopyEndpoints)(args.source, args.destination);
46
+ }
47
+ catch (err) {
48
+ this.error(err.message, { exit: 2 });
49
+ }
50
+ const scope = { workspaceId: flags.workspace };
51
+ let result;
52
+ try {
53
+ result =
54
+ plan.direction === 'upload'
55
+ ? await (0, dedicated_copy_1.uploadToDedicatedRuntime)(plan.localPath, plan.leaseId, plan.remotePath, scope)
56
+ : await (0, dedicated_copy_1.downloadFromDedicatedRuntime)(plan.leaseId, plan.remotePath, plan.localPath, scope);
57
+ }
58
+ catch (err) {
59
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
60
+ if (message)
61
+ this.error(message, { exit: 1, code: (0, dedicated_machines_1.dedicatedRuntimeApiErrorCode)(err) });
62
+ // A local failure (missing file, too large, changed mid-copy) is ours to
63
+ // explain; an API failure without a readable body goes to the shared path.
64
+ if (err instanceof Error && typeof err.status !== 'number') {
65
+ this.error(err.message, { exit: 1 });
66
+ }
67
+ this.handleApiError(err);
68
+ }
69
+ if (flags.json) {
70
+ this.log(JSON.stringify({ direction: plan.direction, ...result }, null, 2));
71
+ return;
72
+ }
73
+ this.log(`${plan.direction === 'upload' ? 'Uploaded' : 'Downloaded'} ${result.bytes} bytes → ${result.destination}`);
74
+ }
75
+ }
76
+ exports.default = DedicatedMachinesCp;
@@ -1,8 +1,11 @@
1
- import { BaseCommand } from '../../../base-command';
2
- export default class DedicatedMachinesCreate extends BaseCommand {
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ export default class DedicatedMachinesCreate extends DedicatedLeaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ wait: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ timeout: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ interval: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
9
  name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
10
  'size-preset': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
11
  'region-class': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -10,6 +13,7 @@ export default class DedicatedMachinesCreate extends BaseCommand {
10
13
  'retention-class': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
14
  'backup-profile': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
15
  'spending-limit-cents': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
16
+ repository: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
17
  workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
18
  'request-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
19
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -2,18 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const node_crypto_1 = require("node:crypto");
5
- const base_command_1 = require("../../../base-command");
5
+ const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
6
6
  const dedicated_machines_1 = require("../../../lib/dedicated-machines");
7
- class DedicatedMachinesCreate extends base_command_1.BaseCommand {
8
- static description = 'Request a Dedicated Runtime lease';
7
+ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCommand {
8
+ static description = 'Request a Dedicated Runtime lease. See `machines dedicated catalog` for sizes, storage bounds and prices.';
9
9
  static examples = [
10
10
  '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --spending-limit-cents 20000',
11
- '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --spending-limit-cents 20000 --json',
11
+ '<%= config.bin %> machines dedicated create --name dev-box --size-preset standard --spending-limit-cents 20000 --repository acme/app --wait --json',
12
12
  ];
13
13
  static flags = {
14
14
  name: core_1.Flags.string({ description: 'Display name for the dedicated machine', required: true }),
15
15
  'size-preset': core_1.Flags.string({
16
- description: 'Instance size preset (from harnesses products)',
16
+ description: 'Instance size preset (see `machines dedicated catalog`)',
17
17
  required: true,
18
18
  }),
19
19
  // None of these four carry a client-side default, deliberately.
@@ -33,26 +33,34 @@ class DedicatedMachinesCreate extends base_command_1.BaseCommand {
33
33
  description: 'Workspace storage size in GB. Defaults to the product catalog size.',
34
34
  }),
35
35
  'retention-class': core_1.Flags.string({
36
- description: 'Retention class for stopped instances. Defaults to the product catalog value.',
36
+ description: 'What destroy does with the workspace: none (delete), standard or extended (archive it). Defaults to the product catalog value.',
37
37
  }),
38
38
  'backup-profile': core_1.Flags.string({
39
39
  description: 'crash-consistent (AWS Backup) or application-consistent (guest fsfreeze while the daemon is reachable; scheduled DLM when that plan is enabled). Defaults to the product catalog value.',
40
40
  }),
41
41
  'spending-limit-cents': core_1.Flags.integer({
42
- description: 'Monthly spending limit in cents. Required before start.',
42
+ description: 'Monthly spending limit in cents. Required; change it later with `machines dedicated spending-limit`.',
43
43
  required: true,
44
44
  }),
45
+ repository: core_1.Flags.string({
46
+ description: 'Default GitHub repository (owner/repo) for git credentials on the machine. Requires the GitHub App installed for it; a clone of another repository the App covers still works.',
47
+ }),
45
48
  workspace: core_1.Flags.string({ description: 'Workspace scope' }),
46
49
  'request-id': core_1.Flags.string({
47
50
  description: 'Idempotency key. Reuse it to safely retry the same create request.',
48
51
  }),
49
52
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
53
+ ...dedicated_lease_command_1.dedicatedWaitFlags,
50
54
  };
51
55
  async run() {
52
56
  this.requireAuth();
53
57
  const { flags } = await this.parse(DedicatedMachinesCreate);
58
+ if (flags.repository !== undefined && !(0, dedicated_machines_1.isDedicatedRepositoryRef)(flags.repository)) {
59
+ this.error(`--repository must look like owner/repo, got "${flags.repository}".`, { exit: 2 });
60
+ }
61
+ const requestId = flags['request-id'] || newRequestId();
54
62
  const body = (0, dedicated_machines_1.dedicatedRuntimeCreateInput)({
55
- requestId: flags['request-id'] || newRequestId(),
63
+ requestId,
56
64
  displayName: flags.name,
57
65
  sizePreset: flags['size-preset'],
58
66
  monthlySpendingLimitCents: flags['spending-limit-cents'],
@@ -60,27 +68,28 @@ class DedicatedMachinesCreate extends base_command_1.BaseCommand {
60
68
  storageGb: flags['storage-gb'],
61
69
  retentionClass: flags['retention-class'],
62
70
  backupProfile: flags['backup-profile'],
71
+ repositoryRef: flags.repository,
63
72
  workspaceId: flags.workspace,
64
73
  });
65
- let response;
66
- try {
67
- response = await (0, dedicated_machines_1.createDedicatedRuntime)(body);
68
- }
69
- catch (err) {
70
- const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
71
- if (message)
72
- this.error(message, { exit: 1 });
73
- this.handleApiError(err);
74
- }
74
+ const response = await this.callDedicated(() => (0, dedicated_machines_1.createDedicatedRuntime)(body));
75
75
  const lease = response?.lease;
76
+ const id = (0, dedicated_machines_1.dedicatedLeaseId)(lease);
77
+ if (flags.wait) {
78
+ // The request id is what makes a retry safe, and a --wait that times out
79
+ // is exactly when someone retries — so say it before watching.
80
+ if (!flags.json && !flags['request-id']) {
81
+ process.stderr.write(`Request id ${requestId} (reuse it with --request-id to retry safely)\n`);
82
+ }
83
+ await this.reportLeaseAction(response, flags, { kind: 'running' }, 'Provisioning');
84
+ return;
85
+ }
76
86
  if (flags.json) {
77
87
  this.log(JSON.stringify(response, null, 2));
78
88
  return;
79
89
  }
80
- const id = (0, dedicated_machines_1.dedicatedLeaseId)(lease);
81
90
  this.log(`Dedicated Runtime ${(0, dedicated_machines_1.dedicatedLeaseDisplayName)(lease)} (${id || 'unknown'}) ${(0, dedicated_machines_1.dedicatedLeaseState)(lease)}.`);
82
91
  if (id) {
83
- this.log(`Poll: ${this.config.bin} machines dedicated show ${id}`);
92
+ this.log(`Poll: ${this.config.bin} machines dedicated show ${id} (or pass --wait)`);
84
93
  }
85
94
  }
86
95
  }
@@ -1,14 +1,18 @@
1
- import { BaseCommand } from '../../../base-command';
2
- export default class DedicatedMachinesDestroy extends BaseCommand {
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ export default class DedicatedMachinesDestroy extends DedicatedLeaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
6
  lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
+ wait: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ timeout: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ interval: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
12
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
13
  'retain-storage': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
14
  'purge-storage': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
+ yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
16
  };
13
17
  run(): Promise<void>;
14
18
  }