@skrr-ai/cli 0.1.49 → 0.1.51

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 (180) hide show
  1. package/README.md +5 -4
  2. package/dist/base-command.js +75 -6
  3. package/dist/commands/agents/chat.d.ts +14 -0
  4. package/dist/commands/agents/chat.js +37 -1
  5. package/dist/commands/agents/create.js +14 -7
  6. package/dist/commands/agents/show.d.ts +9 -0
  7. package/dist/commands/agents/show.js +53 -0
  8. package/dist/commands/agents/update.d.ts +9 -0
  9. package/dist/commands/agents/update.js +38 -5
  10. package/dist/commands/balance/show.d.ts +34 -0
  11. package/dist/commands/balance/show.js +92 -2
  12. package/dist/commands/code/index.d.ts +1 -0
  13. package/dist/commands/code/index.js +49 -5
  14. package/dist/commands/code/run.js +5 -1
  15. package/dist/commands/commitments/cycles.d.ts +19 -0
  16. package/dist/commands/commitments/cycles.js +46 -0
  17. package/dist/commands/commitments/doctor.d.ts +1 -0
  18. package/dist/commands/commitments/doctor.js +19 -4
  19. package/dist/commands/commitments/effective-policy.js +40 -1
  20. package/dist/commands/commitments/explain.d.ts +17 -0
  21. package/dist/commands/commitments/explain.js +40 -0
  22. package/dist/commands/commitments/next-tick.js +3 -1
  23. package/dist/commands/commitments/preflight.js +10 -0
  24. package/dist/commands/commitments/show.js +11 -1
  25. package/dist/commands/daemon/config.d.ts +15 -0
  26. package/dist/commands/daemon/config.js +38 -0
  27. package/dist/commands/daemon/index.js +1 -0
  28. package/dist/commands/daemon/status.js +15 -1
  29. package/dist/commands/goals/create.d.ts +1 -0
  30. package/dist/commands/goals/create.js +22 -0
  31. package/dist/commands/harnesses/models.d.ts +54 -0
  32. package/dist/commands/harnesses/models.js +100 -7
  33. package/dist/commands/harnesses/show.d.ts +11 -0
  34. package/dist/commands/harnesses/show.js +35 -0
  35. package/dist/commands/harnesses/update.d.ts +28 -1
  36. package/dist/commands/harnesses/update.js +73 -10
  37. package/dist/commands/logout.js +12 -1
  38. package/dist/commands/machines/dedicated/index.js +1 -1
  39. package/dist/commands/machines/dedicated/sign-in.js +4 -1
  40. package/dist/commands/machines/dedicated/terminal/kill.d.ts +21 -0
  41. package/dist/commands/machines/dedicated/terminal/kill.js +55 -0
  42. package/dist/commands/machines/dedicated/terminal/ls.d.ts +22 -0
  43. package/dist/commands/machines/dedicated/terminal/ls.js +70 -0
  44. package/dist/commands/machines/dedicated/terminal/rename.d.ts +23 -0
  45. package/dist/commands/machines/dedicated/terminal/rename.js +64 -0
  46. package/dist/commands/machines/dedicated/terminal.d.ts +9 -0
  47. package/dist/commands/machines/dedicated/terminal.js +27 -2
  48. package/dist/commands/machines/hosted/connect.js +2 -2
  49. package/dist/commands/machines/hosted/destroy.js +1 -1
  50. package/dist/commands/machines/hosted/exec.js +1 -1
  51. package/dist/commands/machines/hosted/list.js +1 -1
  52. package/dist/commands/machines/hosted/pause.js +1 -1
  53. package/dist/commands/machines/hosted/pull.js +1 -1
  54. package/dist/commands/machines/hosted/resume.js +1 -1
  55. package/dist/commands/machines/hosted/start.js +2 -2
  56. package/dist/commands/machines/hosted/status.js +2 -1
  57. package/dist/commands/outbox/list.d.ts +12 -1
  58. package/dist/commands/outbox/list.js +31 -2
  59. package/dist/commands/revoke-daemon.js +9 -0
  60. package/dist/commands/spaces/create.d.ts +1 -0
  61. package/dist/commands/spaces/create.js +17 -0
  62. package/dist/commands/spaces/show.js +5 -0
  63. package/dist/commands/spaces/update.d.ts +1 -0
  64. package/dist/commands/spaces/update.js +16 -0
  65. package/dist/commands/tasks/create.d.ts +1 -0
  66. package/dist/commands/tasks/create.js +6 -0
  67. package/dist/commands/tasks/list.js +1 -0
  68. package/dist/commands/tasks/show.js +45 -0
  69. package/dist/commands/tasks/update.d.ts +1 -0
  70. package/dist/commands/tasks/update.js +22 -0
  71. package/dist/commands/tasks/upsert.d.ts +1 -0
  72. package/dist/commands/tasks/upsert.js +16 -0
  73. package/dist/lib/agent-config.d.ts +2 -0
  74. package/dist/lib/agent-config.js +3 -1
  75. package/dist/lib/agentic-stream.d.ts +68 -1
  76. package/dist/lib/agentic-stream.js +294 -7
  77. package/dist/lib/api-fetch.js +19 -0
  78. package/dist/lib/auth-core-init.d.ts +2 -10
  79. package/dist/lib/auth-core-init.js +20 -67
  80. package/dist/lib/auth-failure.d.ts +33 -0
  81. package/dist/lib/auth-failure.js +65 -0
  82. package/dist/lib/auth-storage.d.ts +14 -0
  83. package/dist/lib/auth-storage.js +14 -0
  84. package/dist/lib/commitments.d.ts +37 -2
  85. package/dist/lib/commitments.js +148 -0
  86. package/dist/lib/daemonBroker.d.ts +134 -31
  87. package/dist/lib/daemonBroker.js +376 -31
  88. package/dist/lib/dedicated-lease-command.d.ts +14 -0
  89. package/dist/lib/dedicated-lease-command.js +30 -1
  90. package/dist/lib/dedicated-machines.js +8 -25
  91. package/dist/lib/dedicated-service-command.d.ts +11 -3
  92. package/dist/lib/dedicated-service-command.js +20 -3
  93. package/dist/lib/dedicated-service.d.ts +46 -0
  94. package/dist/lib/dedicated-service.js +85 -7
  95. package/dist/lib/dedicated-terminal.d.ts +21 -0
  96. package/dist/lib/dedicated-terminal.js +104 -9
  97. package/dist/lib/delegated-cli.js +4 -1
  98. package/dist/lib/exec-runtime-binary.d.ts +3 -1
  99. package/dist/lib/exec-runtime-binary.js +4 -2
  100. package/dist/lib/first-party-harness-broker.d.ts +10 -0
  101. package/dist/lib/first-party-harness-broker.js +13 -1
  102. package/dist/lib/first-party-harness-doctor.js +41 -1
  103. package/dist/lib/first-party-harness-managed.d.ts +4 -3
  104. package/dist/lib/first-party-harness-project-trust.d.ts +125 -0
  105. package/dist/lib/first-party-harness-project-trust.js +364 -0
  106. package/dist/lib/first-party-harness.d.ts +9 -2
  107. package/dist/lib/first-party-harness.js +6 -5
  108. package/dist/lib/harnesses.d.ts +10 -0
  109. package/dist/lib/hosted-machines.d.ts +10 -1
  110. package/dist/lib/hosted-machines.js +36 -2
  111. package/dist/lib/live-runtime-binary.d.ts +40 -0
  112. package/dist/lib/live-runtime-binary.js +193 -0
  113. package/dist/lib/login.d.ts +23 -0
  114. package/dist/lib/login.js +60 -10
  115. package/dist/lib/machine-spend-cap.d.ts +23 -0
  116. package/dist/lib/machine-spend-cap.js +68 -0
  117. package/dist/lib/message-intent.js +3 -1
  118. package/dist/lib/node-adapter.js +15 -3
  119. package/dist/lib/tasks.d.ts +20 -0
  120. package/dist/lib/tasks.js +54 -0
  121. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +90 -0
  122. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +113 -0
  123. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +2 -0
  124. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +26 -1
  125. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.d.ts +47 -0
  126. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.js +69 -0
  127. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.d.ts +133 -0
  128. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +189 -0
  129. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +41 -3
  130. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +3 -1
  131. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +25 -3
  132. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loopbackHttp.d.ts +32 -0
  133. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/loopbackHttp.js +268 -0
  134. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineId.d.ts +2 -2
  135. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineId.js +11 -10
  136. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +36 -0
  137. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +156 -0
  138. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +8 -3
  139. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +9 -4
  140. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.d.ts +8 -0
  141. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.js +16 -10
  142. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refreshClassification.d.ts +6 -0
  143. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refreshClassification.js +32 -9
  144. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.d.ts +81 -0
  145. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.js +87 -0
  146. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +22 -0
  147. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +61 -2
  148. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/types.d.ts +6 -1
  149. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +90 -0
  150. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +105 -0
  151. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +2 -0
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +25 -1
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.d.ts +47 -0
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.js +66 -0
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.d.ts +133 -0
  156. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +182 -0
  157. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +41 -3
  158. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +3 -1
  159. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +10 -1
  160. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loopbackHttp.d.ts +32 -0
  161. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/loopbackHttp.js +257 -0
  162. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineId.d.ts +2 -2
  163. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineId.js +11 -10
  164. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +36 -0
  165. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +147 -0
  166. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +8 -3
  167. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +9 -4
  168. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.d.ts +8 -0
  169. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.js +16 -10
  170. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refreshClassification.d.ts +6 -0
  171. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refreshClassification.js +31 -9
  172. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.d.ts +81 -0
  173. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.js +80 -0
  174. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +22 -0
  175. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +58 -1
  176. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/types.d.ts +6 -1
  177. package/dist/node_modules/@skrr-ai/auth-core/package.json +61 -1
  178. package/dist/node_modules/@skrr-ai/data-provider/index.js +3418 -3344
  179. package/oclif.manifest.json +24813 -24242
  180. package/package.json +4 -1
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const dedicated_service_command_1 = require("../../../../lib/dedicated-service-command");
5
+ const dedicated_service_1 = require("../../../../lib/dedicated-service");
6
+ /**
7
+ * `skrr machines dedicated terminal kill` — end a persistent session.
8
+ *
9
+ * Detaching (Ctrl-], a dropped connection, `terminal` exiting) never ends a
10
+ * session — the machine keeps the tmux session and everything in it alive
11
+ * by design. This is the explicit verb that destroys one: the shell and its
12
+ * whole process tree stop with it.
13
+ */
14
+ class DedicatedMachinesTerminalKill extends dedicated_service_command_1.DedicatedServiceCommand {
15
+ static description = 'End a persistent terminal session on a Dedicated Runtime — its shell and everything running in it stop.';
16
+ static examples = [
17
+ '<%= config.bin %> machines dedicated terminal kill <lease-id> term-2',
18
+ '<%= config.bin %> machines dedicated terminal ls <lease-id>',
19
+ ];
20
+ static args = {
21
+ lease: core_1.Args.string({
22
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
23
+ required: true,
24
+ ignoreStdin: true,
25
+ }),
26
+ name: core_1.Args.string({
27
+ description: 'Session name, as `terminal ls` prints it (e.g. main, term-2)',
28
+ required: true,
29
+ ignoreStdin: true,
30
+ }),
31
+ };
32
+ static flags = {
33
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
34
+ };
35
+ async run() {
36
+ this.requireAuth();
37
+ const { args, flags } = await this.parse(DedicatedMachinesTerminalKill);
38
+ if (!dedicated_service_1.DEDICATED_TERMINAL_NAME_PATTERN.test(args.name)) {
39
+ this.error('A session name is lowercase letters, digits and dashes, up to 32 characters (e.g. `dev`, `build-2`).', { exit: 2 });
40
+ }
41
+ const leaseId = await this.resolveLeaseArg(args.lease);
42
+ const result = await this.callDedicatedTerminal('kill', {
43
+ leaseId,
44
+ name: args.name,
45
+ ...this.scopeFrom(flags),
46
+ });
47
+ this.closeServiceChannel();
48
+ if (flags.json) {
49
+ this.log(JSON.stringify(result ?? { name: args.name, killed: true }, null, 2));
50
+ return;
51
+ }
52
+ this.log(`Terminal session '${args.name}' ended — its shell and everything running in it stopped.`);
53
+ }
54
+ }
55
+ exports.default = DedicatedMachinesTerminalKill;
@@ -0,0 +1,22 @@
1
+ import { DedicatedServiceCommand } from '../../../../lib/dedicated-service-command';
2
+ /**
3
+ * `skrr machines dedicated terminal ls` — the sessions the machine is
4
+ * keeping alive.
5
+ *
6
+ * A persistent terminal is a tmux session on the guest (`skrr-term-<name>`)
7
+ * that survives Ctrl-] detaches, disconnects and daemon restarts; this is
8
+ * the read side of the web lease card's resume picker. Sessions only ever
9
+ * end through `terminal kill` or the shell's own `exit` — nothing here is
10
+ * created implicitly.
11
+ */
12
+ export default class DedicatedMachinesTerminalLs extends DedicatedServiceCommand {
13
+ static description: string;
14
+ static examples: string[];
15
+ static args: {
16
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ static flags: {
19
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<void>;
22
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const dedicated_service_command_1 = require("../../../../lib/dedicated-service-command");
5
+ const format_1 = require("../../../../lib/format");
6
+ /**
7
+ * `skrr machines dedicated terminal ls` — the sessions the machine is
8
+ * keeping alive.
9
+ *
10
+ * A persistent terminal is a tmux session on the guest (`skrr-term-<name>`)
11
+ * that survives Ctrl-] detaches, disconnects and daemon restarts; this is
12
+ * the read side of the web lease card's resume picker. Sessions only ever
13
+ * end through `terminal kill` or the shell's own `exit` — nothing here is
14
+ * created implicitly.
15
+ */
16
+ class DedicatedMachinesTerminalLs extends dedicated_service_command_1.DedicatedServiceCommand {
17
+ static description = 'List the persistent terminal sessions a Dedicated Runtime is keeping alive (the tmux sessions `terminal` attaches to).';
18
+ static examples = [
19
+ '<%= config.bin %> machines dedicated terminal ls <lease-id>',
20
+ '<%= config.bin %> machines dedicated terminal ls <lease-id> --json',
21
+ ];
22
+ static args = {
23
+ lease: core_1.Args.string({
24
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
25
+ required: true,
26
+ ignoreStdin: true,
27
+ }),
28
+ };
29
+ static flags = {
30
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
31
+ };
32
+ async run() {
33
+ this.requireAuth();
34
+ const { args, flags } = await this.parse(DedicatedMachinesTerminalLs);
35
+ const leaseId = await this.resolveLeaseArg(args.lease);
36
+ const result = await this.callDedicatedTerminal('list', {
37
+ leaseId,
38
+ ...this.scopeFrom(flags),
39
+ });
40
+ this.closeServiceChannel();
41
+ if (flags.json) {
42
+ this.log(JSON.stringify(result ?? { sessions: [] }, null, 2));
43
+ return;
44
+ }
45
+ const sessions = result?.sessions ?? [];
46
+ if (sessions.length === 0) {
47
+ this.log(`No terminal sessions on ${leaseId}.`);
48
+ this.log(`Open one with \`${this.config.bin} machines dedicated terminal ${leaseId}\`.`);
49
+ return;
50
+ }
51
+ (0, format_1.renderTable)(sessions.map((session) => ({
52
+ name: (0, format_1.formatCell)(session.name),
53
+ attached: session.attached ? 'yes' : 'no',
54
+ created: (0, format_1.formatCell)(
55
+ // The daemon reports tmux `session_created`, which is SECONDS —
56
+ // `localStamp` reads a number as milliseconds.
57
+ typeof session.createdAt === 'number'
58
+ ? (0, format_1.localStamp)(session.createdAt * 1000)
59
+ : session.createdAt !== undefined
60
+ ? (0, format_1.localStamp)(session.createdAt)
61
+ : undefined),
62
+ })), [
63
+ { key: 'name', header: 'NAME', verbatim: true },
64
+ { key: 'attached', header: 'ATTACHED' },
65
+ { key: 'created', header: 'CREATED' },
66
+ ], (line) => this.log(line));
67
+ this.log(`Attach with \`${this.config.bin} machines dedicated terminal ${leaseId} --name <name>\`; end one with \`terminal kill\`.`);
68
+ }
69
+ }
70
+ exports.default = DedicatedMachinesTerminalLs;
@@ -0,0 +1,23 @@
1
+ import { DedicatedServiceCommand } from '../../../../lib/dedicated-service-command';
2
+ /**
3
+ * `skrr machines dedicated terminal rename` — give a live session a better
4
+ * name.
5
+ *
6
+ * The machine auto-names sessions it allocates (`term-1`, `term-2`, …);
7
+ * renaming one to `dev` or `build` makes the `terminal --name` you attach
8
+ * with self-explanatory. The session keeps running — a rename never
9
+ * detaches or restarts it.
10
+ */
11
+ export default class DedicatedMachinesTerminalRename extends DedicatedServiceCommand {
12
+ static description: string;
13
+ static examples: string[];
14
+ static args: {
15
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
16
+ name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
+ newName: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
18
+ };
19
+ static flags: {
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_service_command_1 = require("../../../../lib/dedicated-service-command");
5
+ const dedicated_service_1 = require("../../../../lib/dedicated-service");
6
+ /**
7
+ * `skrr machines dedicated terminal rename` — give a live session a better
8
+ * name.
9
+ *
10
+ * The machine auto-names sessions it allocates (`term-1`, `term-2`, …);
11
+ * renaming one to `dev` or `build` makes the `terminal --name` you attach
12
+ * with self-explanatory. The session keeps running — a rename never
13
+ * detaches or restarts it.
14
+ */
15
+ class DedicatedMachinesTerminalRename extends dedicated_service_command_1.DedicatedServiceCommand {
16
+ static description = 'Rename a persistent terminal session on a Dedicated Runtime (the session keeps running).';
17
+ static examples = [
18
+ '<%= config.bin %> machines dedicated terminal rename <lease-id> term-2 dev',
19
+ '<%= config.bin %> machines dedicated terminal ls <lease-id>',
20
+ ];
21
+ static args = {
22
+ lease: core_1.Args.string({
23
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
24
+ required: true,
25
+ ignoreStdin: true,
26
+ }),
27
+ name: core_1.Args.string({
28
+ description: 'Current session name, as `terminal ls` prints it',
29
+ required: true,
30
+ ignoreStdin: true,
31
+ }),
32
+ newName: core_1.Args.string({
33
+ description: 'The name to give it — lowercase letters, digits and dashes',
34
+ required: true,
35
+ ignoreStdin: true,
36
+ }),
37
+ };
38
+ static flags = {
39
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
40
+ };
41
+ async run() {
42
+ this.requireAuth();
43
+ const { args, flags } = await this.parse(DedicatedMachinesTerminalRename);
44
+ for (const name of [args.name, args.newName]) {
45
+ if (!dedicated_service_1.DEDICATED_TERMINAL_NAME_PATTERN.test(name)) {
46
+ this.error('A session name is lowercase letters, digits and dashes, up to 32 characters (e.g. `dev`, `build-2`).', { exit: 2 });
47
+ }
48
+ }
49
+ const leaseId = await this.resolveLeaseArg(args.lease);
50
+ const result = await this.callDedicatedTerminal('rename', {
51
+ leaseId,
52
+ name: args.name,
53
+ newName: args.newName,
54
+ ...this.scopeFrom(flags),
55
+ });
56
+ this.closeServiceChannel();
57
+ if (flags.json) {
58
+ this.log(JSON.stringify(result ?? { name: args.name, newName: args.newName }, null, 2));
59
+ return;
60
+ }
61
+ this.log(`Terminal session '${args.name}' is now '${args.newName}'.`);
62
+ }
63
+ }
64
+ exports.default = DedicatedMachinesTerminalRename;
@@ -7,6 +7,12 @@ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
7
7
  * `gh auth login`, a REPL, a dev server's interactive setup and `claude` itself
8
8
  * reachable only from a browser. This opens the same real PTY the web does,
9
9
  * through the same relay and the same admission.
10
+ *
11
+ * Sessions are persistent by default (always-on §6.4): the machine runs the
12
+ * shell inside tmux, so Ctrl-] detaches and the same command re-attaches the
13
+ * same session — a lost connection re-attaches too, when the machine's image
14
+ * announces `persistent_terminal_v1`. `terminal ls|kill|rename` are the
15
+ * session's management verbs; `--ephemeral` is the old disposable shell.
10
16
  */
11
17
  export default class DedicatedMachinesTerminal extends DedicatedLeaseCommand {
12
18
  static description: string;
@@ -17,6 +23,9 @@ export default class DedicatedMachinesTerminal extends DedicatedLeaseCommand {
17
23
  static flags: {
18
24
  cwd: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
25
  command: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
26
+ name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
27
+ new: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
28
+ ephemeral: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
29
  };
21
30
  run(): Promise<void>;
22
31
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
5
+ const dedicated_service_1 = require("../../../lib/dedicated-service");
5
6
  /**
6
7
  * `skrr machines dedicated terminal` — the web lease card's "Open terminal",
7
8
  * from a terminal.
@@ -10,13 +11,23 @@ const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command"
10
11
  * `gh auth login`, a REPL, a dev server's interactive setup and `claude` itself
11
12
  * reachable only from a browser. This opens the same real PTY the web does,
12
13
  * through the same relay and the same admission.
14
+ *
15
+ * Sessions are persistent by default (always-on §6.4): the machine runs the
16
+ * shell inside tmux, so Ctrl-] detaches and the same command re-attaches the
17
+ * same session — a lost connection re-attaches too, when the machine's image
18
+ * announces `persistent_terminal_v1`. `terminal ls|kill|rename` are the
19
+ * session's management verbs; `--ephemeral` is the old disposable shell.
13
20
  */
14
21
  class DedicatedMachinesTerminal extends dedicated_lease_command_1.DedicatedLeaseCommand {
15
- static description = 'Open an interactive shell on a Dedicated Runtime (a real PTY, as skrr-workload). Ctrl-] closes it.';
22
+ static description = 'Open an interactive shell on a Dedicated Runtime (a real PTY, as skrr-workload). The session persists on the machine: Ctrl-] detaches, and the same command re-attaches it.';
16
23
  static examples = [
17
24
  '<%= config.bin %> machines dedicated terminal <lease-id>',
25
+ '<%= config.bin %> machines dedicated terminal <lease-id> --name dev',
26
+ '<%= config.bin %> machines dedicated terminal <lease-id> --new',
27
+ '<%= config.bin %> machines dedicated terminal <lease-id> --ephemeral',
18
28
  '<%= config.bin %> machines dedicated terminal <lease-id> --cwd /workspace/repos/app',
19
29
  '<%= config.bin %> machines dedicated terminal <lease-id> --command "npm run dev"',
30
+ '<%= config.bin %> machines dedicated terminal ls <lease-id>',
20
31
  ];
21
32
  static args = {
22
33
  lease: core_1.Args.string({
@@ -27,15 +38,29 @@ class DedicatedMachinesTerminal extends dedicated_lease_command_1.DedicatedLease
27
38
  };
28
39
  static flags = {
29
40
  cwd: core_1.Flags.string({
30
- description: 'Directory to start in (defaults to HOME, /workspace/user-data)',
41
+ description: 'Directory to start in (defaults to HOME, /workspace/user-data). Only applied when the session is created — re-attaching one ignores it.',
31
42
  }),
32
43
  command: core_1.Flags.string({
33
44
  description: 'A command to type into the shell once it shows its prompt, as if you typed it. Not typed if you type first. The shell stays open after it.',
34
45
  }),
46
+ name: core_1.Flags.string({
47
+ description: "Attach or create the persistent session with this name (default 'main'). Lowercase letters, digits and dashes, up to 32 characters.",
48
+ }),
49
+ new: core_1.Flags.boolean({
50
+ description: 'Open a new persistent session instead of attaching one — the machine names it (term-1, term-2, …) and prints the name so it can be re-attached.',
51
+ exclusive: ['name'],
52
+ }),
53
+ ephemeral: core_1.Flags.boolean({
54
+ description: 'Open a disposable shell that ends with the connection — the pre-persistent semantics; nothing is kept on the machine.',
55
+ exclusive: ['name', 'new'],
56
+ }),
35
57
  };
36
58
  async run() {
37
59
  this.requireAuth();
38
60
  const { args, flags } = await this.parse(DedicatedMachinesTerminal);
61
+ if (flags.name !== undefined && !dedicated_service_1.DEDICATED_TERMINAL_NAME_PATTERN.test(flags.name)) {
62
+ this.error('A session name is lowercase letters, digits and dashes, up to 32 characters (e.g. `dev`, `build-2`).', { exit: 2 });
63
+ }
39
64
  await this.openDedicatedTerminal(args.lease, flags, flags.command);
40
65
  }
41
66
  }
@@ -45,7 +45,7 @@ class HostedMachinesConnect extends base_command_1.BaseCommand {
45
45
  this.log(`Preview: ${response.session.previewUrl}`);
46
46
  }
47
47
  catch (err) {
48
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
48
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
49
49
  if (message)
50
50
  this.error(message, { exit: 1 });
51
51
  this.handleApiError(err);
@@ -79,7 +79,7 @@ class HostedMachinesConnect extends base_command_1.BaseCommand {
79
79
  this.log(`[exit ${exitCode}]`);
80
80
  }
81
81
  catch (err) {
82
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
82
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
83
83
  this.log(message || err.message);
84
84
  }
85
85
  }
@@ -28,7 +28,7 @@ class HostedMachinesDestroy extends base_command_1.BaseCommand {
28
28
  response = await (0, hosted_machines_1.destroyHostedMachine)(args.id, { workspaceId: flags.workspace });
29
29
  }
30
30
  catch (err) {
31
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
31
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
32
32
  if (message)
33
33
  this.error(message, { exit: 1 });
34
34
  this.handleApiError(err);
@@ -95,7 +95,7 @@ class HostedMachinesExec extends base_command_1.BaseCommand {
95
95
  response = await (0, hosted_machines_1.execHostedMachine)(id, body, { workspaceId: flags.workspace });
96
96
  }
97
97
  catch (err) {
98
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
98
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
99
99
  if (message)
100
100
  this.error(message, { exit: 1 });
101
101
  this.handleApiError(err);
@@ -28,7 +28,7 @@ class HostedMachinesList extends base_command_1.BaseCommand {
28
28
  response = await (0, hosted_machines_1.listHostedMachines)({ workspaceId: flags.workspace, limit: flags.limit });
29
29
  }
30
30
  catch (err) {
31
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
31
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
32
32
  if (message)
33
33
  this.error(message, { exit: 1 });
34
34
  this.handleApiError(err);
@@ -28,7 +28,7 @@ class HostedMachinesPause extends base_command_1.BaseCommand {
28
28
  response = await (0, hosted_machines_1.pauseHostedMachine)(args.id, { workspaceId: flags.workspace });
29
29
  }
30
30
  catch (err) {
31
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
31
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
32
32
  if (message)
33
33
  this.error(message, { exit: 1 });
34
34
  this.handleApiError(err);
@@ -85,7 +85,7 @@ class HostedMachinesPull extends base_command_1.BaseCommand {
85
85
  }, { workspaceId: flags.workspace });
86
86
  }
87
87
  catch (err) {
88
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
88
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
89
89
  if (message) {
90
90
  this.failWithCliError({
91
91
  message,
@@ -48,7 +48,7 @@ class HostedMachinesResume extends base_command_1.BaseCommand {
48
48
  }
49
49
  }
50
50
  catch (err) {
51
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
51
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
52
52
  if (message)
53
53
  this.error(message, { exit: 1 });
54
54
  this.handleApiError(err);
@@ -128,7 +128,7 @@ class HostedMachinesStart extends base_command_1.BaseCommand {
128
128
  // than offering none.
129
129
  const strandedId = (0, hosted_machines_1.hostedMachineId)(session);
130
130
  if (strandedId) {
131
- const cause = (0, hosted_machines_1.formatHostedMachineApiError)(err) ||
131
+ const cause = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin }) ||
132
132
  err?.message ||
133
133
  'Hosted Machine setup failed.';
134
134
  this.failWithCliError({
@@ -143,7 +143,7 @@ class HostedMachinesStart extends base_command_1.BaseCommand {
143
143
  retryable: false,
144
144
  });
145
145
  }
146
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
146
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
147
147
  if (message)
148
148
  this.error(message, { exit: 1 });
149
149
  this.handleApiError(err);
@@ -42,7 +42,7 @@ class HostedMachinesStatus extends base_command_1.BaseCommand {
42
42
  response = await (0, hosted_machines_1.getHostedMachine)(args.id, { workspaceId: flags.workspace });
43
43
  }
44
44
  catch (err) {
45
- const message = (0, hosted_machines_1.formatHostedMachineApiError)(err);
45
+ const message = (0, hosted_machines_1.formatHostedMachineApiError)(err, { bin: this.config.bin });
46
46
  if (message)
47
47
  this.error(message, { exit: 1 });
48
48
  this.handleApiError(err);
@@ -78,6 +78,7 @@ class HostedMachinesStatus extends base_command_1.BaseCommand {
78
78
  this.log(`Machine lease: ${session.machineLeaseId || '-'}`);
79
79
  this.log(`Repos: ${(0, hosted_machines_1.hostedMachineRepoSummary)(session)}`);
80
80
  this.log(`Preview: ${session.previewUrl || '-'}`);
81
+ this.log(`Isolation: ${(0, hosted_machines_1.hostedMachineIsolationSummary)(session)}`);
81
82
  if (session.idleTimeoutMs)
82
83
  this.log(`Idle timeout: ${session.idleTimeoutMs}ms`);
83
84
  const error = (0, hosted_machines_1.hostedMachineErrorSummary)(session);
@@ -1,6 +1,17 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ import { type OutboxEntry } from '../../lib/outbox';
3
+ /**
4
+ * Why one entry was queued, in words (OSK-12121). The queue holds more than
5
+ * outage casualties — a 401 is queued too, so authored content survives a lapsed
6
+ * sign-in — and a list that described every entry as "the API was unavailable"
7
+ * sent a signed-out reader to wait for an outage that was not happening.
8
+ */
9
+ export declare function queuedBecause(entry: Pick<OutboxEntry, 'reason'>): {
10
+ label: string;
11
+ kind: 'auth' | 'throttled' | 'unavailable' | 'other';
12
+ };
2
13
  export declare function outboxListSnapshot(profile?: string): {
3
- queued: import("../../lib/outbox").OutboxEntry[];
14
+ queued: OutboxEntry[];
4
15
  incident: import("../../lib/outbox").IncidentState | null;
5
16
  incidentActive: boolean;
6
17
  };
@@ -1,11 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queuedBecause = queuedBecause;
3
4
  exports.outboxListSnapshot = outboxListSnapshot;
4
5
  const core_1 = require("@oclif/core");
5
6
  const base_command_1 = require("../../base-command");
6
7
  const keychain_1 = require("../../lib/keychain");
7
8
  const format_1 = require("../../lib/format");
8
9
  const outbox_1 = require("../../lib/outbox");
10
+ /**
11
+ * Why one entry was queued, in words (OSK-12121). The queue holds more than
12
+ * outage casualties — a 401 is queued too, so authored content survives a lapsed
13
+ * sign-in — and a list that described every entry as "the API was unavailable"
14
+ * sent a signed-out reader to wait for an outage that was not happening.
15
+ */
16
+ function queuedBecause(entry) {
17
+ const status = entry.reason?.status;
18
+ if (status === 401)
19
+ return { label: 'sign-in refused (401)', kind: 'auth' };
20
+ if (status === 429)
21
+ return { label: 'throttled (429)', kind: 'throttled' };
22
+ if ((typeof status === 'number' && status >= 500) || (0, outbox_1.isOutageFailure)(entry.reason)) {
23
+ return { label: status ? `unavailable (${status})` : 'unreachable', kind: 'unavailable' };
24
+ }
25
+ return { label: status ? `HTTP ${status}` : 'unknown', kind: 'other' };
26
+ }
9
27
  function outboxListSnapshot(profile) {
10
28
  const queued = (0, outbox_1.listWrites)(profile);
11
29
  const incident = (0, outbox_1.readIncident)(profile);
@@ -24,7 +42,7 @@ function outboxListSnapshot(profile) {
24
42
  * where they find out.
25
43
  */
26
44
  class OutboxList extends base_command_1.BaseCommand {
27
- static description = 'Show writes queued locally because the API was unavailable';
45
+ static description = 'Show writes queued locally because the server could not take them: an outage, throttling, or a lapsed sign-in';
28
46
  static examples = [
29
47
  '<%= config.bin %> outbox list',
30
48
  '<%= config.bin %> outbox list --json',
@@ -46,7 +64,9 @@ class OutboxList extends base_command_1.BaseCommand {
46
64
  `${incident.consecutive} consecutive failure(s).`);
47
65
  this.log('');
48
66
  }
49
- else if (incident) {
67
+ else if (incident && entries.some((e) => queuedBecause(e).kind === 'unavailable')) {
68
+ // A past incident is context only for entries an outage put here. Beside
69
+ // a queue of sign-in refusals it reads as the explanation, and is not.
50
70
  this.log(`Last service incident: ${incident.baseURL} failed at ${incident.lastOutageAt}` +
51
71
  `${incident.status ? ` (HTTP ${incident.status})` : ''}. ` +
52
72
  'No current outage is confirmed.');
@@ -61,6 +81,7 @@ class OutboxList extends base_command_1.BaseCommand {
61
81
  queued: e.queuedAt,
62
82
  command: e.command,
63
83
  summary: e.summary,
84
+ why: queuedBecause(e).label,
64
85
  // Surface the attempt count only once it matters; a column of "0"s is
65
86
  // noise, but an entry nearing the cap needs a human.
66
87
  attempts: e.attempts > 0 ? `${e.attempts}/${outbox_1.MAX_REPLAY_ATTEMPTS}` : '-',
@@ -69,6 +90,7 @@ class OutboxList extends base_command_1.BaseCommand {
69
90
  { key: 'queued', header: 'QUEUED', maxWidth: 24 },
70
91
  { key: 'command', header: 'COMMAND', maxWidth: 24 },
71
92
  { key: 'summary', header: 'SUMMARY', maxWidth: 40 },
93
+ { key: 'why', header: 'WHY', maxWidth: 24 },
72
94
  { key: 'attempts', header: 'TRIES' },
73
95
  ], (line) => this.log(line));
74
96
  const exhausted = entries.filter((e) => e.attempts >= outbox_1.MAX_REPLAY_ATTEMPTS);
@@ -77,6 +99,13 @@ class OutboxList extends base_command_1.BaseCommand {
77
99
  this.log(`${exhausted.length} entry(ies) hit the ${outbox_1.MAX_REPLAY_ATTEMPTS}-attempt cap and will not be ` +
78
100
  'retried automatically. Inspect the failure, then retry or drop them explicitly.');
79
101
  }
102
+ const authQueued = entries.filter((e) => queuedBecause(e).kind === 'auth').length;
103
+ if (authQueued > 0) {
104
+ this.log('');
105
+ this.log(`${authQueued} entry(ies) were queued because the server refused this sign-in, not because ` +
106
+ `it was down. Sign in with \`${this.config.bin} login\` first; a replay before that is ` +
107
+ 'refused the same way.');
108
+ }
80
109
  this.log('');
81
110
  this.log(`Replay: ${this.config.bin} outbox retry`);
82
111
  this.log(`Discard: ${this.config.bin} outbox drop <id>`);
@@ -67,6 +67,8 @@ class RevokeDaemon extends base_command_1.BaseCommand {
67
67
  const host = resolvedDaemon?.machineUuid
68
68
  ? resolvedDaemon.machineUuid.slice(0, 12)
69
69
  : (resolvedDaemon?.os ?? 'unknown host');
70
+ this.log('Revoking a daemon also revokes every CLI sign-in it brokered — on its machine, ' +
71
+ `\`${this.config.bin} login\` without a browser signs in through the daemon.`);
70
72
  const confirmed = await this.confirm(`Revoke daemon "${name}" on ${host}? [y/N] `);
71
73
  if (!confirmed) {
72
74
  this.log('Aborted.');
@@ -99,6 +101,13 @@ class RevokeDaemon extends base_command_1.BaseCommand {
99
101
  const daemonId = (res.daemon?.id ?? resolvedId).slice(0, 12);
100
102
  if (res.revoked) {
101
103
  this.log(`Revoked daemon ${daemonName} (${daemonId}).`);
104
+ if (res.callerSessionRevoked === true) {
105
+ // Said now, not discovered later as a refresh failure: the sign-in this
106
+ // command used was brokered by that daemon and died with it.
107
+ this.warn(`This also signed out ${this.config.bin} on this machine: its sign-in was brokered ` +
108
+ `by that daemon and was revoked with it. Run \`${this.config.bin} daemon login\` to ` +
109
+ `re-enroll the daemon, then \`${this.config.bin} login\` to sign the CLI in again.`);
110
+ }
102
111
  // exit 0 (default)
103
112
  }
104
113
  else if (res.alreadyRevoked) {
@@ -19,6 +19,7 @@ export default class SpacesCreate extends BaseCommand {
19
19
  'group-chat': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
20
  parent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
21
  'inherit-access': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
22
+ 'working-directory': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
23
  };
23
24
  run(): Promise<void>;
24
25
  }
@@ -9,6 +9,7 @@ const label_ref_1 = require("../../lib/label-ref");
9
9
  const web_url_1 = require("../../lib/web-url");
10
10
  const prompt_1 = require("../../lib/prompt");
11
11
  const created_line_1 = require("../../lib/created-line");
12
+ const tasks_1 = require("../../lib/tasks");
12
13
  class SpacesCreate extends base_command_1.BaseCommand {
13
14
  static description = 'Create a new space';
14
15
  static examples = [
@@ -51,6 +52,11 @@ class SpacesCreate extends base_command_1.BaseCommand {
51
52
  allowNo: true,
52
53
  description: 'Inherit members/permissions from the parent chain (default). Use --no-inherit-access to make a private subspace.',
53
54
  }),
55
+ 'working-directory': core_1.Flags.string({
56
+ description: "Space-level working directory for this space's local Harness runs — an absolute " +
57
+ "path on the executing daemon's filesystem. The middle layer of task → space → " +
58
+ 'agent: a per-task override wins, the agent default applies when neither is set.',
59
+ }),
54
60
  };
55
61
  async run() {
56
62
  this.requireAuth();
@@ -95,7 +101,18 @@ class SpacesCreate extends base_command_1.BaseCommand {
95
101
  groupChatId: flags['group-chat'],
96
102
  parentSpaceId: flags.parent,
97
103
  inheritAccess: flags['inherit-access'],
104
+ workingDirectory: (0, tasks_1.workingDirectoryFromFlag)(flags['working-directory']),
98
105
  });
106
+ // Post-merge, so --working-directory and --from-json are checked
107
+ // identically: a relative path is refused here rather than stored and
108
+ // silently fallen back from at run time on the daemon.
109
+ const workingDirectoryError = (0, tasks_1.workingDirectoryValidationError)(payload.workingDirectory);
110
+ if (workingDirectoryError) {
111
+ this.error(workingDirectoryError, { exit: 2 });
112
+ }
113
+ // 'none' on create means "no space default" — the field is absent anyway.
114
+ if (payload.workingDirectory === null)
115
+ delete payload.workingDirectory;
99
116
  if (!payload.title || typeof payload.title !== 'string') {
100
117
  this.error('A title is required. Pass --title "..." or include it in --from-json.', {
101
118
  exit: 1,
@@ -57,6 +57,11 @@ class SpacesShow extends base_command_1.BaseCommand {
57
57
  this.log(`Leader: ${s.leadAgentId ?? '-'}`);
58
58
  this.log(`Target Date: ${s.targetDate ?? '-'}`);
59
59
  this.log(`Goals: ${Array.isArray(s.goalIds) ? s.goalIds.length : 0}`);
60
+ // Only when set — the space default is the middle layer of the
61
+ // task → space → agent cascade, and most spaces leave it unset.
62
+ if (typeof s.workingDirectory === 'string' && s.workingDirectory) {
63
+ this.log(`Directory: ${s.workingDirectory}`);
64
+ }
60
65
  this.log(`Archived: ${s.archivedAt ? 'yes' : 'no'}`);
61
66
  this.log(`Parent: ${s.parentSpaceId ?? '(root)'}`);
62
67
  this.log(`Depth: ${s.depth ?? 0}`);
@@ -30,6 +30,7 @@ export default class SpacesUpdate extends BaseCommand {
30
30
  'auto-start-on-backlog-exit': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
31
31
  'triage-external-intake': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
32
32
  'default-task-template': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
+ 'working-directory': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
34
  };
34
35
  run(): Promise<void>;
35
36
  }