@skrr-ai/cli 0.1.87 → 0.1.88

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 (92) hide show
  1. package/dist/commands/agents/chat.js +27 -1
  2. package/dist/commands/computer/index.d.ts +19 -0
  3. package/dist/commands/computer/index.js +37 -0
  4. package/dist/commands/labels/create.d.ts +1 -0
  5. package/dist/commands/labels/create.js +2 -0
  6. package/dist/commands/labels/restore.d.ts +1 -0
  7. package/dist/commands/labels/restore.js +6 -3
  8. package/dist/commands/labels/update.d.ts +1 -0
  9. package/dist/commands/labels/update.js +2 -0
  10. package/dist/commands/machines/computer-adoption.d.ts +15 -0
  11. package/dist/commands/machines/computer-adoption.js +33 -0
  12. package/dist/commands/machines/dedicated/archive.d.ts +26 -0
  13. package/dist/commands/machines/dedicated/archive.js +113 -0
  14. package/dist/commands/machines/dedicated/audit.d.ts +25 -0
  15. package/dist/commands/machines/dedicated/audit.js +113 -0
  16. package/dist/commands/machines/dedicated/computer.d.ts +23 -0
  17. package/dist/commands/machines/dedicated/computer.js +64 -0
  18. package/dist/commands/machines/dedicated/cp.d.ts +2 -0
  19. package/dist/commands/machines/dedicated/cp.js +74 -2
  20. package/dist/commands/machines/dedicated/git-status.d.ts +15 -0
  21. package/dist/commands/machines/dedicated/git-status.js +56 -0
  22. package/dist/commands/machines/dedicated/index.js +15 -1
  23. package/dist/commands/machines/dedicated/ls.d.ts +27 -0
  24. package/dist/commands/machines/dedicated/ls.js +100 -0
  25. package/dist/commands/machines/dedicated/mkdir.d.ts +18 -0
  26. package/dist/commands/machines/dedicated/mkdir.js +57 -0
  27. package/dist/commands/machines/dedicated/mv.d.ts +24 -0
  28. package/dist/commands/machines/dedicated/mv.js +83 -0
  29. package/dist/commands/machines/dedicated/rm.d.ts +26 -0
  30. package/dist/commands/machines/dedicated/rm.js +100 -0
  31. package/dist/commands/machines/dedicated/search.d.ts +24 -0
  32. package/dist/commands/machines/dedicated/search.js +78 -0
  33. package/dist/commands/machines/dedicated/stat.d.ts +22 -0
  34. package/dist/commands/machines/dedicated/stat.js +92 -0
  35. package/dist/commands/machines/hosted/index.js +1 -1
  36. package/dist/commands/machines/hosted/list.js +1 -1
  37. package/dist/commands/machines/hosted/start.js +1 -1
  38. package/dist/commands/machines/services/declare.d.ts +24 -0
  39. package/dist/commands/machines/services/declare.js +73 -0
  40. package/dist/commands/machines/services/index.d.ts +15 -0
  41. package/dist/commands/machines/services/index.js +31 -0
  42. package/dist/commands/machines/services/ls.d.ts +17 -0
  43. package/dist/commands/machines/services/ls.js +60 -0
  44. package/dist/commands/machines/services/withdraw.d.ts +19 -0
  45. package/dist/commands/machines/services/withdraw.js +47 -0
  46. package/dist/commands/machines/share.d.ts +29 -0
  47. package/dist/commands/machines/share.js +100 -0
  48. package/dist/commands/machines/shared.d.ts +19 -0
  49. package/dist/commands/machines/shared.js +62 -0
  50. package/dist/commands/machines/shares.d.ts +16 -0
  51. package/dist/commands/machines/shares.js +69 -0
  52. package/dist/commands/machines/unshare.d.ts +19 -0
  53. package/dist/commands/machines/unshare.js +61 -0
  54. package/dist/commands/tasks/labels/create.d.ts +1 -0
  55. package/dist/commands/tasks/labels/create.js +3 -0
  56. package/dist/commands/views/create.d.ts +1 -0
  57. package/dist/commands/views/create.js +5 -0
  58. package/dist/lib/agent-home-workspace.d.ts +7 -0
  59. package/dist/lib/agent-home-workspace.js +147 -0
  60. package/dist/lib/computer-adoption.d.ts +55 -0
  61. package/dist/lib/computer-adoption.js +67 -0
  62. package/dist/lib/computer-consent.d.ts +13 -0
  63. package/dist/lib/computer-consent.js +33 -0
  64. package/dist/lib/computer-files.d.ts +239 -0
  65. package/dist/lib/computer-files.js +707 -0
  66. package/dist/lib/dedicated-copy.d.ts +45 -9
  67. package/dist/lib/dedicated-copy.js +141 -44
  68. package/dist/lib/dedicated-machines.d.ts +49 -0
  69. package/dist/lib/dedicated-machines.js +97 -6
  70. package/dist/lib/label-scope.d.ts +12 -0
  71. package/dist/lib/label-scope.js +15 -1
  72. package/dist/lib/machine-audit.d.ts +34 -0
  73. package/dist/lib/machine-audit.js +50 -0
  74. package/dist/lib/machine-grants.d.ts +70 -0
  75. package/dist/lib/machine-grants.js +47 -0
  76. package/dist/lib/machine-services.d.ts +64 -0
  77. package/dist/lib/machine-services.js +60 -0
  78. package/dist/lib/task-view-render.d.ts +2 -0
  79. package/dist/lib/task-view-render.js +1 -1
  80. package/dist/lib/views/vocabulary.d.ts +1 -1
  81. package/dist/lib/views/vocabulary.js +2 -1
  82. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +955 -0
  83. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +1070 -0
  84. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +7 -0
  85. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +955 -0
  86. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +1051 -0
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +7 -0
  88. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  89. package/dist/node_modules/@skrr-ai/data-provider/index.js +22143 -20728
  90. package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
  91. package/oclif.manifest.json +11550 -9963
  92. package/package.json +2 -2
@@ -538,7 +538,13 @@ class AgentsChat extends base_command_1.BaseCommand {
538
538
  // Only the LOOKUP starts here: the default is committed just before the
539
539
  // turn, so those reads still go out without a workspace header, as before.
540
540
  // `--quick` never consulted it. Never rejects.
541
- const homeWorkspace = flags.quick
541
+ //
542
+ // OSK-13599 — and a server already observed to choose the agent's home
543
+ // workspace itself is not asked at all: that read existed only to pick the
544
+ // header, and it is a full round trip before every turn. Only the SERVER's
545
+ // capability is remembered, never the agent's workspace (agents move).
546
+ const serverChoosesWorkspace = !flags.quick && (0, agent_home_workspace_1.serverAppliesAgentHomeDefault)(this.config?.cacheDir, this.cliConfig.baseURL);
547
+ const homeWorkspace = flags.quick || serverChoosesWorkspace
542
548
  ? null
543
549
  : (0, agent_home_workspace_1.lookupAgentHomeWorkspaceDefault)(args.agentId, (agentId) => data_provider_1.dataService.getAgentById({ agent_id: agentId }));
544
550
  let threadConversationId = flags.conversation;
@@ -679,6 +685,18 @@ class AgentsChat extends base_command_1.BaseCommand {
679
685
  // model is not supported."). The reader had to guess (OSK-4897).
680
686
  this.handleApiError(err);
681
687
  }
688
+ // OSK-13599 — the acceptance names the workspace the turn runs in. Adopt it
689
+ // for the rest of this turn (the stream's header, the message reads that
690
+ // reconcile its end, envelope replay) exactly as the pre-flight read's
691
+ // answer used to be adopted; a workspace the caller chose still wins, and
692
+ // an autonomous process still never takes one. It also tells us whether
693
+ // this server chooses the workspace itself, for the next turn.
694
+ const workspaceDefault = (0, agent_home_workspace_1.recordAgentHomeDefaultObservation)(this.config?.cacheDir, this.cliConfig.baseURL, started);
695
+ if (serverChoosesWorkspace && workspaceDefault === 'unsupported') {
696
+ this.warn('This server no longer chooses the agent’s home workspace, so this turn ran without ' +
697
+ 'one. The next turn will select it again; pass --workspace to choose one now.');
698
+ }
699
+ (0, agent_home_workspace_1.commitAgentHomeWorkspaceDefault)(typeof started.workspaceId === 'string' && started.workspaceId ? started.workspaceId : null);
682
700
  if (!started.sessionId) {
683
701
  this.error('Agentic chat was accepted without a session ID; cannot attach the stream.', {
684
702
  exit: 1,
@@ -696,6 +714,14 @@ class AgentsChat extends base_command_1.BaseCommand {
696
714
  conversationId: started.conversationId,
697
715
  ...(flags['thread-root'] ? { threadRootMessageId: flags['thread-root'] } : {}),
698
716
  responseMessageId: started.responseMessageId,
717
+ // OSK-13599 — where the turn runs and who chose it, when the server
718
+ // says (`request` | `agent_home`); absent against an older server.
719
+ ...(workspaceDefault === 'supported'
720
+ ? {
721
+ workspaceId: started.workspaceId ?? null,
722
+ workspaceSource: started.workspaceSource ?? null,
723
+ }
724
+ : {}),
699
725
  agentId: args.agentId,
700
726
  url: (0, web_url_1.agentUrlFrom)(this.cliConfig.baseURL, { id: args.agentId }),
701
727
  });
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ /**
3
+ * `skrr computer allow|deny|status` — the machine-local consent for the
4
+ * computer's new surfaces (§18/D11): the file surface and a person's own
5
+ * browser windows. Off by default; the daemon announces the answer as
6
+ * capabilities so the shell can say "this machine has not allowed the
7
+ * computer" instead of failing later.
8
+ */
9
+ export default class Computer extends Command {
10
+ static description: string;
11
+ static args: {
12
+ action: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
13
+ };
14
+ static flags: {
15
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ static examples: string[];
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const computer_consent_1 = require("../../lib/computer-consent");
5
+ /**
6
+ * `skrr computer allow|deny|status` — the machine-local consent for the
7
+ * computer's new surfaces (§18/D11): the file surface and a person's own
8
+ * browser windows. Off by default; the daemon announces the answer as
9
+ * capabilities so the shell can say "this machine has not allowed the
10
+ * computer" instead of failing later.
11
+ */
12
+ class Computer extends core_1.Command {
13
+ static description = 'Allow, deny, or show whether the computer may reach this machine — ' +
14
+ 'its file surface and your own browser windows (off by default)';
15
+ static args = {
16
+ action: core_1.Args.string({
17
+ description: 'allow, deny, or status',
18
+ required: true,
19
+ options: ['allow', 'deny', 'status'],
20
+ ignoreStdin: true,
21
+ }),
22
+ };
23
+ static flags = {
24
+ json: core_1.Flags.boolean({ description: 'Output JSON' }),
25
+ };
26
+ static examples = [
27
+ '<%= config.bin %> computer status',
28
+ '<%= config.bin %> computer allow',
29
+ '<%= config.bin %> computer deny',
30
+ '<%= config.bin %> computer status --json',
31
+ ];
32
+ async run() {
33
+ const { args, flags } = await this.parse(Computer);
34
+ await (0, computer_consent_1.forwardComputerConsent)(this, [args.action, ...(flags.json ? ['--json'] : [])]);
35
+ }
36
+ }
37
+ exports.default = Computer;
@@ -3,6 +3,7 @@ export default class LabelsCreate extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ 'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
6
7
  name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
8
  color: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
9
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -30,6 +30,7 @@ class LabelsCreate extends base_command_1.BaseCommand {
30
30
  'group-exclusive': core_1.Flags.boolean({
31
31
  description: 'Make this label an exclusive group: at most one of ITS children may be attached to an entity at a time. Attaching a second child auto-detaches the first.',
32
32
  }),
33
+ ...label_scope_1.allowDuplicateNameFlags,
33
34
  };
34
35
  async run() {
35
36
  this.requireAuth();
@@ -58,6 +59,7 @@ class LabelsCreate extends base_command_1.BaseCommand {
58
59
  ...(applicableTo ? { applicableTo } : {}),
59
60
  ...(flags.parent ? { parentId: flags.parent } : {}),
60
61
  ...(flags['group-exclusive'] ? { groupExclusive: true } : {}),
62
+ ...(flags['allow-duplicate-name'] ? { allowDuplicateName: true } : {}),
61
63
  });
62
64
  }
63
65
  catch (err) {
@@ -6,6 +6,7 @@ export default class LabelsRestore extends BaseCommand {
6
6
  id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
+ 'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
10
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
+ const label_scope_1 = require("../../lib/label-scope");
6
7
  class LabelsRestore extends base_command_1.BaseCommand {
7
8
  static description = 'Restore an archived label';
8
9
  static examples = [
@@ -14,16 +15,18 @@ class LabelsRestore extends base_command_1.BaseCommand {
14
15
  };
15
16
  static flags = {
16
17
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
18
+ ...label_scope_1.allowDuplicateNameFlags,
17
19
  };
18
20
  async run() {
19
21
  this.requireAuth();
20
22
  const { args, flags } = await this.parse(LabelsRestore);
21
23
  // A live label with the same name may have been created while this one was
22
- // archived; the server answers 409 `duplicate_name` in that case, which
23
- // `handleApiError` surfaces verbatim.
24
+ // archived; the server answers 409 `duplicate_name` (same scope) or
25
+ // `duplicate_name_other_scope` in that case, which `handleApiError`
26
+ // surfaces verbatim.
24
27
  let result;
25
28
  try {
26
- result = await data_provider_1.dataService.restoreLabel(args.id);
29
+ result = await data_provider_1.dataService.restoreLabel(args.id, flags['allow-duplicate-name'] ? { allowDuplicateName: true } : {});
27
30
  }
28
31
  catch (err) {
29
32
  this.handleApiError(err);
@@ -6,6 +6,7 @@ export default class LabelsUpdate extends BaseCommand {
6
6
  id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
+ 'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
10
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
11
  name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
12
  color: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -36,6 +36,7 @@ class LabelsUpdate extends base_command_1.BaseCommand {
36
36
  'expected-version': core_1.Flags.integer({
37
37
  description: 'Optimistic-concurrency guard — fails with 409 if the label changed since you read it',
38
38
  }),
39
+ ...label_scope_1.allowDuplicateNameFlags,
39
40
  };
40
41
  async run() {
41
42
  this.requireAuth();
@@ -78,6 +79,7 @@ class LabelsUpdate extends base_command_1.BaseCommand {
78
79
  ...(flags['expected-version'] !== undefined
79
80
  ? { expectedVersion: flags['expected-version'] }
80
81
  : {}),
82
+ ...(flags['allow-duplicate-name'] ? { allowDuplicateName: true } : {}),
81
83
  });
82
84
  }
83
85
  catch (err) {
@@ -0,0 +1,15 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr machines computer-adoption` — the share of daemons still on the old
4
+ * event families, which is the number retiring `browser:liveview:*` and
5
+ * `agent:terminal:*` waits on (OSK-13578). Admin only on the server: it
6
+ * describes the fleet, not the caller.
7
+ */
8
+ export default class MachinesComputerAdoption extends BaseCommand {
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
+ };
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,33 @@
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 computer_adoption_1 = require("../../lib/computer-adoption");
6
+ /**
7
+ * `skrr machines computer-adoption` — the share of daemons still on the old
8
+ * event families, which is the number retiring `browser:liveview:*` and
9
+ * `agent:terminal:*` waits on (OSK-13578). Admin only on the server: it
10
+ * describes the fleet, not the caller.
11
+ */
12
+ class MachinesComputerAdoption extends base_command_1.BaseCommand {
13
+ static description = 'Show how many daemons are still on the old browser live view / agent terminal event families instead of the computer protocol, and whether the retirement criterion is met (admin only)';
14
+ static examples = [
15
+ '<%= config.bin %> machines computer-adoption',
16
+ '<%= config.bin %> machines computer-adoption --json',
17
+ ];
18
+ static flags = {
19
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
20
+ };
21
+ async run() {
22
+ this.requireAuth();
23
+ const { flags } = await this.parse(MachinesComputerAdoption);
24
+ const read = await (0, computer_adoption_1.getComputerAdoption)();
25
+ if (flags.json) {
26
+ this.log(JSON.stringify(read, null, 2));
27
+ return;
28
+ }
29
+ for (const line of (0, computer_adoption_1.computerAdoptionLines)(read))
30
+ this.log(line);
31
+ }
32
+ }
33
+ exports.default = MachinesComputerAdoption;
@@ -0,0 +1,26 @@
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ /**
3
+ * `skrr machines dedicated archive` — download a folder from the machine as a
4
+ * tar file.
5
+ *
6
+ * The machine builds one staged archive of the folder (prepare), the CLI reads
7
+ * it in chunks (read) and frees it (release — always, including on a failed or
8
+ * cancelled download). The local file is written beside its destination and
9
+ * renamed into place, the same discipline `cp` keeps, so a cancelled download
10
+ * never leaves a partial `<name>.tar` — only the machine's staged copy, which
11
+ * release then removes.
12
+ */
13
+ export default class DedicatedMachinesArchive extends DedicatedLeaseCommand {
14
+ static summary: string;
15
+ static description: string;
16
+ static examples: string[];
17
+ static args: {
18
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
19
+ path: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
20
+ destination: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
21
+ };
22
+ static flags: {
23
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
24
+ };
25
+ run(): Promise<void>;
26
+ }
@@ -0,0 +1,113 @@
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_lease_command_1 = require("../../../lib/dedicated-lease-command");
6
+ const dedicated_machines_1 = require("../../../lib/dedicated-machines");
7
+ const dedicated_copy_1 = require("../../../lib/dedicated-copy");
8
+ const computer_files_1 = require("../../../lib/computer-files");
9
+ const LIMIT_MIB = dedicated_machines_1.DEDICATED_FILE_MAX_BYTES / (1024 * 1024);
10
+ /**
11
+ * `skrr machines dedicated archive` — download a folder from the machine as a
12
+ * tar file.
13
+ *
14
+ * The machine builds one staged archive of the folder (prepare), the CLI reads
15
+ * it in chunks (read) and frees it (release — always, including on a failed or
16
+ * cancelled download). The local file is written beside its destination and
17
+ * renamed into place, the same discipline `cp` keeps, so a cancelled download
18
+ * never leaves a partial `<name>.tar` — only the machine's staged copy, which
19
+ * release then removes.
20
+ */
21
+ class DedicatedMachinesArchive extends dedicated_lease_command_1.DedicatedLeaseCommand {
22
+ static summary = 'Download a folder from a Dedicated Runtime machine as a tar file';
23
+ static description = [
24
+ `Downloads one folder as a single tar archive of at most ${LIMIT_MIB} MiB and 10,000 entries. ` +
25
+ 'A destination left off, or one that names a directory, receives <name>.tar.',
26
+ 'The staged archive is the folder as it stood when the command ran — the tree may keep ' +
27
+ 'changing, the archive does not. Symlinks are stored as links and never followed out of ' +
28
+ 'the folder.',
29
+ 'A Ctrl-C removes the local partial and releases the staged copy on the machine (exit 130; ' +
30
+ '143 for SIGTERM, 129 when the terminal closes). Progress is shown on a terminal, but not ' +
31
+ 'with --json or under CI.',
32
+ ].join('\n\n');
33
+ static examples = [
34
+ '<%= config.bin %> machines dedicated archive rtl_abc123 repos/app',
35
+ '<%= config.bin %> machines dedicated archive rtl_abc123 repos/app ./backups/',
36
+ '<%= config.bin %> machines dedicated archive rtl_abc123 repos/app ./app-snapshot.tar --json',
37
+ ];
38
+ static args = {
39
+ lease: core_1.Args.string({
40
+ description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
41
+ required: true,
42
+ ignoreStdin: true,
43
+ }),
44
+ path: core_1.Args.string({
45
+ description: 'Folder on the machine, relative to /workspace (inside repos/ or user-data/)',
46
+ required: true,
47
+ ignoreStdin: true,
48
+ }),
49
+ destination: core_1.Args.string({
50
+ description: 'Local file, or a directory to receive <name>.tar (default: ./<name>.tar)',
51
+ required: false,
52
+ ignoreStdin: true,
53
+ }),
54
+ };
55
+ static flags = {
56
+ json: core_1.Flags.boolean({ description: 'Output the result as JSON' }),
57
+ };
58
+ async run() {
59
+ this.requireAuth();
60
+ const { args, flags } = await this.parse(DedicatedMachinesArchive);
61
+ const leaseId = await this.resolveLeaseArg(args.lease);
62
+ const scope = this.scopeFrom(flags);
63
+ const progress = !flags.json && process.stderr.isTTY && !(0, dedicated_copy_1.isContinuousIntegration)()
64
+ ? (0, dedicated_copy_1.createCopyProgress)({
65
+ label: 'Downloading',
66
+ write: (text) => process.stderr.write(text),
67
+ })
68
+ : null;
69
+ // Same interrupt contract as `cp`: the first signal asks the download to
70
+ // stop; the partial is removed and the staged archive released.
71
+ const interrupt = (0, dedicated_copy_1.listenForCopyInterrupt)();
72
+ let result;
73
+ try {
74
+ // Once: prepare, every chunk and the release present this generation.
75
+ const generation = await (0, computer_files_1.resolveDedicatedFileGeneration)(leaseId, scope);
76
+ result = await (0, computer_files_1.downloadComputerFileArchive)(leaseId, args.path, args.destination, generation, scope, {
77
+ signal: interrupt.signal,
78
+ onProgress: progress?.update,
79
+ });
80
+ }
81
+ catch (thrown) {
82
+ progress?.clear();
83
+ const interruptedBy = interrupt.interruptedBy();
84
+ const err = interruptedBy ? new dedicated_copy_1.DedicatedCopyCancelledError(interruptedBy) : thrown;
85
+ if (err instanceof dedicated_copy_1.DedicatedCopyCancelledError) {
86
+ this.error(`${err.message} Nothing was written for ${args.path}; the staged archive on the machine is released.`, { exit: err.exitCode, code: err.code });
87
+ }
88
+ const refusal = (0, base_command_1.apiRefusal)(err);
89
+ if (refusal.body?.retryable === true)
90
+ this.handleApiError(err);
91
+ const failure = (0, computer_files_1.describeComputerFileFailure)(err, { bin: this.config.bin, leaseId });
92
+ if (failure)
93
+ this.failWithCliError({ ...failure, exit: 1 });
94
+ if (err instanceof Error && typeof err.status !== 'number') {
95
+ this.error(err.message, { exit: 1, code: 'DEDICATED_RUNTIME_FILE_TRANSFER_FAILED' });
96
+ }
97
+ this.handleApiError(err);
98
+ }
99
+ finally {
100
+ interrupt.dispose();
101
+ }
102
+ progress?.clear();
103
+ if (flags.json) {
104
+ this.log(JSON.stringify(result, null, 2));
105
+ return;
106
+ }
107
+ this.log(`Downloaded ${result.name}.${result.format} (${result.entries} entries, ${result.size} bytes) → ${result.destination}`);
108
+ if (!result.archiveReleased) {
109
+ this.warn('The staged archive could not be released; the machine removes it within the hour.');
110
+ }
111
+ }
112
+ }
113
+ exports.default = DedicatedMachinesArchive;
@@ -0,0 +1,25 @@
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ import { type ComputerAuditPage } from '../../../lib/machine-audit';
3
+ type AuditEvent = ComputerAuditPage['events'][number];
4
+ /** The actor as ids: the audit record stores no display name. */
5
+ export declare function describeAuditActor(actor: AuditEvent['actor']): string;
6
+ /** What the event touched, as the record says it — a surface, a path, a reason. */
7
+ export declare function describeAuditEvent(event: AuditEvent): string;
8
+ export default class DedicatedMachinesAudit extends DedicatedLeaseCommand {
9
+ static description: string;
10
+ static examples: string[];
11
+ static args: {
12
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
13
+ };
14
+ static flags: {
15
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
16
+ kind: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
+ actor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
+ since: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
+ until: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
21
+ cursor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
+ };
23
+ run(): Promise<void>;
24
+ }
25
+ export {};
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeAuditActor = describeAuditActor;
4
+ exports.describeAuditEvent = describeAuditEvent;
5
+ const core_1 = require("@oclif/core");
6
+ const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
7
+ const format_1 = require("../../../lib/format");
8
+ const machine_audit_1 = require("../../../lib/machine-audit");
9
+ /** The actor as ids: the audit record stores no display name. */
10
+ function describeAuditActor(actor) {
11
+ return actor.kind === 'human' ? `person ${actor.userId}` : `agent ${actor.agentId}`;
12
+ }
13
+ /** What the event touched, as the record says it — a surface, a path, a reason. */
14
+ function describeAuditEvent(event) {
15
+ const parts = [];
16
+ if (event.summary)
17
+ parts.push(event.summary);
18
+ else {
19
+ const detail = event.detail ?? {};
20
+ const path = typeof detail.path === 'string' ? detail.path : undefined;
21
+ const reason = typeof detail.reason === 'string' ? detail.reason : undefined;
22
+ if (path)
23
+ parts.push(typeof detail.toPath === 'string' ? `${path} -> ${detail.toPath}` : path);
24
+ if (reason)
25
+ parts.push(reason);
26
+ }
27
+ if (event.surfaceId)
28
+ parts.push(`[${event.surfaceKind ?? 'surface'} ${event.surfaceId}]`);
29
+ return parts.join(' ') || '-';
30
+ }
31
+ class DedicatedMachinesAudit extends dedicated_lease_command_1.DedicatedLeaseCommand {
32
+ static description = "Read a machine's audit record — who changed control, wrote files, opened, closed or shared a surface, changed a grant or used power — long after the live Activity window has rolled off";
33
+ static examples = [
34
+ '<%= config.bin %> machines dedicated audit <lease-id>',
35
+ '<%= config.bin %> machines dedicated audit <lease-id> --kind control_changed --since 14d --until 7d',
36
+ '<%= config.bin %> machines dedicated audit <lease-id> --actor <user-id> --json',
37
+ '<%= config.bin %> machines dedicated audit <lease-id> --cursor <next-cursor>',
38
+ ];
39
+ static args = {
40
+ lease: core_1.Args.string({
41
+ ignoreStdin: true,
42
+ description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
43
+ required: true,
44
+ }),
45
+ };
46
+ static flags = {
47
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
48
+ kind: core_1.Flags.string({
49
+ description: `Only this kind; repeat for several (${machine_audit_1.COMPUTER_AUDIT_KINDS.join(', ')})`,
50
+ multiple: true,
51
+ options: [...machine_audit_1.COMPUTER_AUDIT_KINDS],
52
+ }),
53
+ actor: core_1.Flags.string({ description: 'Only events this person (user id) did' }),
54
+ since: core_1.Flags.string({
55
+ description: 'Only events at or after this time: 2026-09-24, an ISO time, or an age like 7d',
56
+ }),
57
+ until: core_1.Flags.string({
58
+ description: 'Only events before this time: 2026-09-24, an ISO time, or an age like 7d',
59
+ }),
60
+ limit: core_1.Flags.integer({ description: 'Events per page (1-200)', default: 50, min: 1, max: 200 }),
61
+ cursor: core_1.Flags.string({ description: 'Continue from the `next` cursor of a previous page' }),
62
+ };
63
+ async run() {
64
+ this.requireAuth();
65
+ const { args, flags } = await this.parse(DedicatedMachinesAudit);
66
+ const leaseId = await this.resolveLeaseArg(args.lease);
67
+ let since;
68
+ let until;
69
+ try {
70
+ since = flags.since ? (0, machine_audit_1.parseAuditTime)(flags.since, '--since') : undefined;
71
+ until = flags.until ? (0, machine_audit_1.parseAuditTime)(flags.until, '--until') : undefined;
72
+ }
73
+ catch (err) {
74
+ this.error(err instanceof Error ? err.message : String(err), { exit: 2 });
75
+ }
76
+ const page = await this.callDedicated(() => (0, machine_audit_1.listMachineAudit)(`lease:${leaseId}`, {
77
+ kinds: flags.kind,
78
+ actorUserId: flags.actor,
79
+ since,
80
+ until,
81
+ cursor: flags.cursor,
82
+ limit: flags.limit,
83
+ }));
84
+ if (flags.json) {
85
+ this.log(JSON.stringify(page, null, 2));
86
+ return;
87
+ }
88
+ const events = page?.events ?? [];
89
+ if (events.length === 0) {
90
+ // An empty page is "nothing matched", never "nothing happened": the
91
+ // record is kept 400 days and a grantee is not shown grant changes.
92
+ this.log(flags.kind || flags.actor || flags.since || flags.until
93
+ ? 'No audit events match those filters.'
94
+ : 'No audit events recorded for this machine.');
95
+ return;
96
+ }
97
+ (0, format_1.renderTable)(events.map((event) => ({
98
+ when: (0, format_1.localStamp)(event.occurredAt),
99
+ actor: (0, format_1.formatCell)(describeAuditActor(event.actor)),
100
+ kind: (0, format_1.formatCell)(event.kind),
101
+ what: (0, format_1.formatCell)(describeAuditEvent(event)),
102
+ })), [
103
+ { key: 'when', header: `WHEN (${(0, format_1.localZone)()})` },
104
+ { key: 'actor', header: 'ACTOR', verbatim: true },
105
+ { key: 'kind', header: 'KIND' },
106
+ { key: 'what', header: 'WHAT', verbatim: true },
107
+ ], (line) => this.log(line));
108
+ if (page?.nextCursor) {
109
+ this.log(`More (older) events: re-run with --cursor ${page.nextCursor}`);
110
+ }
111
+ }
112
+ }
113
+ exports.default = DedicatedMachinesAudit;
@@ -0,0 +1,23 @@
1
+ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
2
+ /**
3
+ * `skrr machines dedicated computer` — open the machine's computer: its
4
+ * terminals, browser and files as one desktop of windows (design §11, §17).
5
+ *
6
+ * The page is `/machines/<machineId>/computer`, where the machine id is the
7
+ * inventory's `lease:<leaseId>`. The command prints that URL (always, on
8
+ * stdout) and opens it unless stdout is not a terminal or --json was passed —
9
+ * the same open contract `projects open` and the other entity opens keep.
10
+ */
11
+ export default class DedicatedMachinesComputer extends DedicatedLeaseCommand {
12
+ static summary: string;
13
+ static examples: string[];
14
+ static args: {
15
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
16
+ };
17
+ static flags: {
18
+ web: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
19
+ app: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ };
22
+ run(): Promise<void>;
23
+ }
@@ -0,0 +1,64 @@
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 entity_open_1 = require("../../../lib/entity-open");
6
+ const dedicated_machines_1 = require("../../../lib/dedicated-machines");
7
+ /**
8
+ * `skrr machines dedicated computer` — open the machine's computer: its
9
+ * terminals, browser and files as one desktop of windows (design §11, §17).
10
+ *
11
+ * The page is `/machines/<machineId>/computer`, where the machine id is the
12
+ * inventory's `lease:<leaseId>`. The command prints that URL (always, on
13
+ * stdout) and opens it unless stdout is not a terminal or --json was passed —
14
+ * the same open contract `projects open` and the other entity opens keep.
15
+ */
16
+ class DedicatedMachinesComputer extends dedicated_lease_command_1.DedicatedLeaseCommand {
17
+ static summary = 'Open a Dedicated Runtime machine’s computer in the browser or skrr Desktop';
18
+ static examples = [
19
+ '<%= config.bin %> machines dedicated computer rtl_abc123',
20
+ '<%= config.bin %> machines dedicated computer rtl_abc123 --app',
21
+ '<%= config.bin %> machines dedicated computer rtl_abc123 --json',
22
+ ];
23
+ static args = {
24
+ lease: core_1.Args.string({
25
+ description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
26
+ required: true,
27
+ ignoreStdin: true,
28
+ }),
29
+ };
30
+ static flags = {
31
+ web: core_1.Flags.boolean({ char: 'w', description: 'Open in the browser (default)' }),
32
+ app: core_1.Flags.boolean({ char: 'a', description: 'Open in skrr Desktop (macOS)' }),
33
+ json: core_1.Flags.boolean({ description: 'Print the canonical URL as JSON without opening it' }),
34
+ };
35
+ async run() {
36
+ this.requireAuth();
37
+ const { args, flags } = await this.parse(DedicatedMachinesComputer);
38
+ const leaseId = await this.resolveLeaseArg(args.lease);
39
+ // The lease read doubles as the existence check: an id that resolves to
40
+ // nothing is refused before a browser opens on a dead page.
41
+ const response = await this.callDedicated(() => (0, dedicated_machines_1.getDedicatedRuntime)(leaseId, this.scopeFrom(flags)));
42
+ if (!response?.lease) {
43
+ this.error('Dedicated Runtime lease not found.', { exit: 1 });
44
+ }
45
+ const url = (0, dedicated_machines_1.dedicatedComputerUrl)(this.cliConfig.baseURL, leaseId);
46
+ if (!url) {
47
+ this.error(`No computer page exists for ${leaseId}.`, { exit: 1 });
48
+ }
49
+ const result = await (0, entity_open_1.openEntity)({
50
+ baseURL: this.cliConfig.baseURL,
51
+ entity: { url },
52
+ urlFor: () => null,
53
+ target: flags.app ? 'app' : 'web',
54
+ json: Boolean(flags.json),
55
+ });
56
+ if (flags.json) {
57
+ this.log(JSON.stringify(result, null, 2));
58
+ return;
59
+ }
60
+ const where = result.target === 'app' ? 'skrr Desktop' : 'browser';
61
+ this.log(result.opened ? `Opened ${where}: ${result.url}` : result.url);
62
+ }
63
+ }
64
+ exports.default = DedicatedMachinesComputer;
@@ -17,6 +17,8 @@ export default class DedicatedMachinesCp extends BaseCommand {
17
17
  };
18
18
  static flags: {
19
19
  workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
+ overwrite: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ 'if-tag': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
22
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
23
  };
22
24
  run(): Promise<void>;