@skrr-ai/cli 0.1.37 → 0.1.39

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 (177) hide show
  1. package/bin/dev-fallback.js +175 -4
  2. package/dist/base-command.js +46 -2
  3. package/dist/commands/agents/actions/create.js +18 -7
  4. package/dist/commands/agents/actions/delete.d.ts +1 -0
  5. package/dist/commands/agents/actions/delete.js +7 -0
  6. package/dist/commands/agents/actions/update.js +5 -3
  7. package/dist/commands/agents/api-actions/add.js +5 -2
  8. package/dist/commands/agents/api-actions/index.d.ts +11 -0
  9. package/dist/commands/agents/api-actions/index.js +27 -1
  10. package/dist/commands/agents/avatar/default/index.d.ts +11 -0
  11. package/dist/commands/agents/avatar/default/index.js +17 -0
  12. package/dist/commands/agents/avatar/default/set.d.ts +2 -1
  13. package/dist/commands/agents/avatar/default/set.js +32 -7
  14. package/dist/commands/agents/avatar/upload.js +7 -1
  15. package/dist/commands/agents/categories/delete.js +8 -0
  16. package/dist/commands/agents/categories/index.d.ts +21 -5
  17. package/dist/commands/agents/categories/index.js +68 -15
  18. package/dist/commands/agents/chat.d.ts +21 -0
  19. package/dist/commands/agents/chat.js +94 -7
  20. package/dist/commands/agents/create.js +11 -4
  21. package/dist/commands/agents/list.d.ts +15 -0
  22. package/dist/commands/agents/list.js +31 -1
  23. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  24. package/dist/commands/agents/scene-background/upload.js +97 -0
  25. package/dist/commands/agents/show.js +13 -0
  26. package/dist/commands/agents/tools/calls.d.ts +23 -5
  27. package/dist/commands/agents/tools/calls.js +81 -18
  28. package/dist/commands/agents/update.js +15 -5
  29. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  30. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  31. package/dist/commands/convos/messages.d.ts +13 -0
  32. package/dist/commands/convos/messages.js +40 -16
  33. package/dist/commands/daemon/index.js +8 -1
  34. package/dist/commands/daemon/logs.d.ts +26 -0
  35. package/dist/commands/daemon/logs.js +56 -0
  36. package/dist/commands/daemon/uninstall.js +1 -1
  37. package/dist/commands/followups/act.js +3 -2
  38. package/dist/commands/followups/cancel.d.ts +6 -3
  39. package/dist/commands/followups/cancel.js +16 -6
  40. package/dist/commands/followups/observability.d.ts +23 -0
  41. package/dist/commands/followups/observability.js +114 -0
  42. package/dist/commands/followups/reschedule.js +1 -1
  43. package/dist/commands/followups/resolve.js +3 -3
  44. package/dist/commands/followups/show.js +2 -1
  45. package/dist/commands/followups/watch.js +9 -5
  46. package/dist/commands/harnesses/forget.js +28 -0
  47. package/dist/commands/harnesses/install.d.ts +41 -0
  48. package/dist/commands/harnesses/install.js +165 -0
  49. package/dist/commands/harnesses/installers.d.ts +45 -0
  50. package/dist/commands/harnesses/installers.js +155 -0
  51. package/dist/commands/harnesses/list.d.ts +82 -1
  52. package/dist/commands/harnesses/list.js +104 -12
  53. package/dist/commands/harnesses/models.js +10 -1
  54. package/dist/commands/harnesses/show.d.ts +15 -0
  55. package/dist/commands/harnesses/show.js +44 -3
  56. package/dist/commands/harnesses/update.js +1 -1
  57. package/dist/commands/harnesses/usage.d.ts +25 -0
  58. package/dist/commands/harnesses/usage.js +24 -2
  59. package/dist/commands/instructions/status.js +13 -1
  60. package/dist/commands/machines/dedicated/catalog.js +4 -15
  61. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  62. package/dist/commands/machines/dedicated/create.js +63 -2
  63. package/dist/commands/machines/dedicated/destroy.js +19 -5
  64. package/dist/commands/machines/dedicated/exec.js +3 -1
  65. package/dist/commands/machines/dedicated/list.js +9 -6
  66. package/dist/commands/machines/dedicated/price-book.d.ts +17 -0
  67. package/dist/commands/machines/dedicated/price-book.js +35 -0
  68. package/dist/commands/machines/hosted/list.js +12 -0
  69. package/dist/commands/machines/list.js +11 -0
  70. package/dist/commands/machines/show.d.ts +1 -0
  71. package/dist/commands/machines/show.js +40 -11
  72. package/dist/commands/profiles/list.d.ts +23 -0
  73. package/dist/commands/profiles/list.js +96 -0
  74. package/dist/commands/search/skills.d.ts +11 -1
  75. package/dist/commands/search/skills.js +26 -8
  76. package/dist/commands/skills/details.d.ts +2 -0
  77. package/dist/commands/skills/details.js +24 -1
  78. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  79. package/dist/commands/skills/import-as-actions.js +175 -0
  80. package/dist/commands/skills/install.d.ts +2 -0
  81. package/dist/commands/skills/install.js +24 -1
  82. package/dist/commands/skills/invocable.js +6 -2
  83. package/dist/commands/skills/local.d.ts +40 -0
  84. package/dist/commands/skills/local.js +160 -0
  85. package/dist/commands/skills/search.d.ts +11 -1
  86. package/dist/commands/skills/search.js +29 -8
  87. package/dist/commands/spaces/actions/add.js +23 -5
  88. package/dist/commands/spaces/actions/available.js +3 -0
  89. package/dist/commands/spaces/actions/list.d.ts +5 -0
  90. package/dist/commands/spaces/actions/list.js +49 -3
  91. package/dist/commands/spaces/actions/run.d.ts +28 -0
  92. package/dist/commands/spaces/actions/run.js +146 -0
  93. package/dist/commands/spaces/actions/update.js +27 -5
  94. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  95. package/dist/commands/spaces/label-summaries.js +64 -0
  96. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  97. package/dist/commands/spaces/task-rollups.js +15 -4
  98. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
  99. package/dist/commands/tasks/actions/attach.js +32 -7
  100. package/dist/commands/tasks/complete.js +13 -0
  101. package/dist/commands/tasks/create.js +1 -1
  102. package/dist/commands/tasks/events/append.js +29 -6
  103. package/dist/commands/tasks/expectations/assess.d.ts +1 -0
  104. package/dist/commands/tasks/expectations/assess.js +43 -5
  105. package/dist/commands/tasks/expectations.js +77 -16
  106. package/dist/commands/tasks/follow-up.js +2 -2
  107. package/dist/commands/tasks/lifecycle/definitions.js +21 -2
  108. package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
  109. package/dist/commands/tasks/lifecycle/detach.js +43 -3
  110. package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
  111. package/dist/commands/tasks/lifecycle/migrate.js +42 -3
  112. package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
  113. package/dist/commands/tasks/lifecycle/reopen.js +40 -3
  114. package/dist/commands/tasks/lifecycle/transition.js +33 -2
  115. package/dist/commands/tasks/list.js +16 -9
  116. package/dist/commands/tasks/move.js +11 -13
  117. package/dist/commands/tasks/review-queue.d.ts +15 -0
  118. package/dist/commands/tasks/review-queue.js +180 -31
  119. package/dist/commands/tasks/show.d.ts +11 -0
  120. package/dist/commands/tasks/show.js +105 -12
  121. package/dist/commands/tasks/trust.js +7 -2
  122. package/dist/commands/tasks/update.js +46 -10
  123. package/dist/commands/tasks/updates/add.js +1 -1
  124. package/dist/commands/token/list.d.ts +22 -0
  125. package/dist/commands/token/list.js +68 -0
  126. package/dist/commands/token/revoke.d.ts +23 -0
  127. package/dist/commands/token/revoke.js +53 -0
  128. package/dist/lib/agent-actions.d.ts +57 -2
  129. package/dist/lib/agent-actions.js +113 -21
  130. package/dist/lib/agent-pin-mute.d.ts +10 -5
  131. package/dist/lib/agent-pin-mute.js +17 -4
  132. package/dist/lib/agent-visual-refs.d.ts +27 -0
  133. package/dist/lib/agent-visual-refs.js +127 -2
  134. package/dist/lib/agentic-stream.d.ts +6 -0
  135. package/dist/lib/agentic-stream.js +34 -3
  136. package/dist/lib/assignee-resolver.d.ts +1 -1
  137. package/dist/lib/assignee-resolver.js +8 -0
  138. package/dist/lib/bulk-task-targets.d.ts +12 -0
  139. package/dist/lib/bulk-task-targets.js +34 -1
  140. package/dist/lib/cli-installers.d.ts +130 -0
  141. package/dist/lib/cli-installers.js +145 -0
  142. package/dist/lib/command-miss.js +55 -7
  143. package/dist/lib/dedicated-lease-command.d.ts +16 -1
  144. package/dist/lib/dedicated-lease-command.js +26 -3
  145. package/dist/lib/dedicated-machines.d.ts +135 -1
  146. package/dist/lib/dedicated-machines.js +307 -3
  147. package/dist/lib/failed-lookup.d.ts +3 -0
  148. package/dist/lib/failed-lookup.js +40 -0
  149. package/dist/lib/first-party-harness-agent.js +9 -2
  150. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  151. package/dist/lib/first-party-harness-doctor.js +126 -27
  152. package/dist/lib/followups.d.ts +14 -6
  153. package/dist/lib/followups.js +31 -6
  154. package/dist/lib/harnesses.d.ts +8 -0
  155. package/dist/lib/instruction-input.d.ts +19 -1
  156. package/dist/lib/instruction-input.js +52 -1
  157. package/dist/lib/label-scope.d.ts +15 -0
  158. package/dist/lib/label-scope.js +25 -5
  159. package/dist/lib/local-skills.d.ts +98 -0
  160. package/dist/lib/local-skills.js +195 -0
  161. package/dist/lib/machines.d.ts +10 -10
  162. package/dist/lib/machines.js +23 -1
  163. package/dist/lib/search-query.d.ts +19 -0
  164. package/dist/lib/search-query.js +35 -0
  165. package/dist/lib/space-custom-actions.d.ts +72 -0
  166. package/dist/lib/space-custom-actions.js +117 -0
  167. package/dist/lib/space-resolver.d.ts +1 -21
  168. package/dist/lib/space-resolver.js +19 -10
  169. package/dist/lib/task-closure.d.ts +3 -2
  170. package/dist/lib/task-closure.js +18 -20
  171. package/dist/lib/task-lifecycle-output.js +117 -9
  172. package/dist/lib/task-ref-resolver.d.ts +1 -17
  173. package/dist/lib/task-ref-resolver.js +18 -8
  174. package/dist/lib/tasks.js +9 -3
  175. package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
  176. package/oclif.manifest.json +15505 -14460
  177. package/package.json +9 -3
@@ -0,0 +1,155 @@
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 data_provider_1 = require("@skrr-ai/data-provider");
6
+ const cli_installers_1 = require("../../lib/cli-installers");
7
+ const daemon_ref_1 = require("../../lib/daemon-ref");
8
+ const daemon_target_1 = require("../../lib/daemon-target");
9
+ const format_1 = require("../../lib/format");
10
+ /**
11
+ * What each managed provider's state is on one machine.
12
+ *
13
+ * ## Why this command exists
14
+ *
15
+ * `d878b79581` replaced "is the binary there" with four states — installed,
16
+ * ready, interactive-required, headless-key-required — and routed them from
17
+ * `cli-installers` through health to the web harness create modal. It reached
18
+ * exactly one surface. From the CLI there was no way to ask.
19
+ *
20
+ * That gap is not cosmetic, because `skrr harnesses list` answers a DIFFERENT
21
+ * question and looks like it answers this one. Its `status` is the daemon's
22
+ * socket, so on this account `skrr-code` read `online` with no binary installed
23
+ * at all, and `harnesses ping` answered it in 78ms — the daemon was there, the
24
+ * harness was not. A user could not discover that from inside the product; it
25
+ * took `command -v` in a shell.
26
+ *
27
+ * So this prints what the DAEMON says about each provider: whether the binary
28
+ * is present, which one and where, and whether it is signed in. The four states
29
+ * keep their server-side names here rather than being re-prosed, so that a
30
+ * message quoted from the CLI matches the one in the modal.
31
+ *
32
+ * `unknown` is a first-class answer and is not `ready`. A provider whose recipe
33
+ * declares no credential resolver genuinely cannot be assessed from here, and
34
+ * saying so is the point — the failure this whole vocabulary exists to prevent
35
+ * is a surface that reports ready for a harness that will refuse on first use.
36
+ */
37
+ class HarnessesInstallers extends base_command_1.BaseCommand {
38
+ static description = 'What each managed coding-agent CLI looks like on one machine — installed, which version, where, and whether it is signed in';
39
+ static examples = [
40
+ '<%= config.bin %> harnesses installers',
41
+ '<%= config.bin %> harnesses installers --daemon daemon_07c086e3c249_cli_default',
42
+ '<%= config.bin %> harnesses installers --json',
43
+ ];
44
+ static flags = {
45
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
46
+ daemon: core_1.Flags.string({
47
+ description: 'Which machine to ask. Defaults to your only active daemon; required when several are active.',
48
+ }),
49
+ };
50
+ async run() {
51
+ this.requireAuth();
52
+ const { flags } = await this.parse(HarnessesInstallers);
53
+ const target = await (0, daemon_target_1.resolveDaemonTarget)({
54
+ explicit: flags.daemon,
55
+ local: (0, daemon_target_1.localMachine)(),
56
+ listActiveDaemons: () => (0, daemon_target_1.listActiveDaemons)(this.resolvedCredential),
57
+ });
58
+ const daemonId = this.daemonIdFor(target);
59
+ let res;
60
+ try {
61
+ res = await (0, cli_installers_1.listCliInstallers)(daemonId, this.resolvedCredential);
62
+ }
63
+ catch (err) {
64
+ this.handleApiError(err);
65
+ return;
66
+ }
67
+ const providers = res.providers ?? [];
68
+ if (flags.json) {
69
+ this.log(JSON.stringify({ daemonId, platform: res.platform, providers }, null, 2));
70
+ return;
71
+ }
72
+ if (providers.length === 0) {
73
+ // A daemon too old to carry the inventory answers with no providers. That
74
+ // is not "you have none" — say which it is, since the remedy differs.
75
+ this.log(`The daemon ${daemonId} returned no installer inventory. ` +
76
+ 'It may predate managed CLI installers — update the skrr app or run `skrr daemon restart`.');
77
+ return;
78
+ }
79
+ // The RAW id, not `formatDaemonRef`. This header identifies which machine
80
+ // the answer is about, and the id is what the reader pastes back into
81
+ // `--daemon`; an 8-character prefix of a `daemon_<hash>_<source>_<profile>`
82
+ // id is `d_daemon_0...`, which distinguishes nothing and cannot be pasted.
83
+ this.log(`Managed coding-agent CLIs on ${daemonId} (${res.platform ?? '?'}, daemon ${res.daemonVersion ?? 'version not reported'})`);
84
+ this.log('');
85
+ (0, format_1.renderTable)(providers.map((p) => {
86
+ const readiness = (0, cli_installers_1.readinessOf)(p);
87
+ return {
88
+ provider: (0, format_1.formatCell)(p.provider),
89
+ readiness: readiness.label,
90
+ version: (0, format_1.formatCell)(p.version),
91
+ where: p.resolvedPath ?? '-',
92
+ };
93
+ }), [
94
+ { key: 'provider', header: 'PROVIDER' },
95
+ { key: 'readiness', header: 'READINESS' },
96
+ { key: 'version', header: 'VERSION' },
97
+ // Verbatim and UNCLIPPED. The resolved path answers "which copy is it
98
+ // using", and the cases where that matters are exactly the long ones:
99
+ // Devin's CLI lives at
100
+ // /Applications/Devin.app/Contents/Resources/app/extensions/windsurf/devin/bin/devin
101
+ // and is never on PATH, so `command -v devin` says it is not installed.
102
+ // Clipping from the right would hide the binary name and keep the
103
+ // surprise this column exists to reveal.
104
+ { key: 'where', header: 'RESOLVED PATH', verbatim: true },
105
+ ], (line) => this.log(` ${line}`));
106
+ // The detail lines carry the daemon's own wording. Printing them under the
107
+ // table rather than in a column keeps a full sentence readable — these are
108
+ // instructions ("Run `devin auth login`"), not statuses.
109
+ const notes = providers
110
+ .map((p) => ({ provider: p.provider, ...(0, cli_installers_1.readinessOf)(p) }))
111
+ .filter((n) => !n.ok && n.detail);
112
+ if (notes.length > 0) {
113
+ this.log('');
114
+ for (const note of notes)
115
+ this.log(` ${note.provider}: ${note.detail}`);
116
+ }
117
+ // A provider the platform knows and this daemon did not mention. Without
118
+ // this it simply is not on the list, which reads as dropped support rather
119
+ // than as an older daemon — `devin` arrived in daemon 0.8.43, so 0.8.35
120
+ // answers with six providers and says nothing about the seventh.
121
+ const missing = (0, cli_installers_1.providersNotOffered)(providers, data_provider_1.MANAGED_CLI_PROVIDER_IDS);
122
+ if (missing.length > 0) {
123
+ this.log('');
124
+ this.log(` Not offered by this daemon: ${missing.join(', ')}. ` +
125
+ 'That usually means the daemon is older than the provider — ' +
126
+ 'check `skrr daemon status` for its version and how to update it.');
127
+ }
128
+ }
129
+ /**
130
+ * Refuse rather than guess which machine to report on.
131
+ *
132
+ * Reading is harmless, but naming the wrong machine is not: the whole value
133
+ * here is "is the thing on MY laptop usable", and an answer about a sandbox
134
+ * the user forgot about is worse than no answer, because it looks like one.
135
+ */
136
+ daemonIdFor(target) {
137
+ if (target.kind === 'daemon')
138
+ return target.daemonId;
139
+ if (target.kind === 'this-machine') {
140
+ if (target.daemonId)
141
+ return target.daemonId;
142
+ this.error('This machine is a Dedicated Runtime whose daemon has not published an id this process can read. Name a machine with --daemon.', { exit: 1 });
143
+ }
144
+ if (target.kind === 'none') {
145
+ this.error(`No active daemon is registered for this account, so nothing can report on it. Start the local runtime with \`${this.config.bin} daemon start\`.`, { exit: 1 });
146
+ }
147
+ const choices = target.daemons
148
+ .slice(0, 10)
149
+ .map((d) => ` ${(0, daemon_ref_1.formatDaemonRef)(d.id)} ${d.name || '—'}`)
150
+ .join('\n');
151
+ this.error(`${target.daemons.length} active daemons — say which machine to report on:\n${choices}\n` +
152
+ `Pass one with --daemon, or see them all with \`${this.config.bin} list-daemons\`.`, { exit: 1 });
153
+ }
154
+ }
155
+ exports.default = HarnessesInstallers;
@@ -18,7 +18,68 @@ export type HarnessRow = {
18
18
  machineProduct?: string;
19
19
  source?: string;
20
20
  };
21
+ /**
22
+ * The name everyone else sees, when YOUR alias is covering it.
23
+ *
24
+ * `name` is the effective one. Two commands write a harness's name and mean
25
+ * different things — `harnesses alias` sets yours, `harnesses update --name`
26
+ * sets the shared one — and the server used to overwrite `name` with the
27
+ * alias, so the shared value was unobservable to the person who wrote it.
28
+ * Present only when an alias is actually shadowing something.
29
+ */
30
+ sharedName?: string;
31
+ aliased?: boolean;
32
+ /**
33
+ * A later registration of the same provider on the same machine replaced this
34
+ * row, so it will never come back online. The server derives it (one rule, in
35
+ * `MachineService`); this type must carry it or the column silently reports
36
+ * `offline` for a row `machines show` calls `superseded` — and those two ask
37
+ * the reader for opposite actions.
38
+ */
39
+ superseded?: boolean;
21
40
  };
41
+ /**
42
+ * Products skrr provisions and owns. A row that says it is one of these is
43
+ * cloud, whatever `harnessLocation` reads.
44
+ */
45
+ /**
46
+ * An 8-character machine label that is still a machine.
47
+ *
48
+ * `machineUuid` is only a bare uuid when the row's identity kind is
49
+ * `machine-uuid`. The other kinds carry a PREFIXED value —
50
+ * `dedicated-runtime:rtl_…`, `hosted-machine:…` — so a blind `.slice(0, 8)`
51
+ * printed the literal word `dedicate` for every dedicated runtime, identically,
52
+ * across machines that had nothing to do with each other. Keeping the tail is
53
+ * what makes the column distinguish anything: the prefix is the part every row
54
+ * of that kind shares.
55
+ */
56
+ export declare function shortMachineLabel(machineUuid?: string): string;
57
+ /**
58
+ * What to say when the table would be empty.
59
+ *
60
+ * A `--provider` filter that matched nothing is not evidence about the daemon.
61
+ * The old copy asked "Is your daemon connected?" regardless, which sent a user
62
+ * to debug a connection they had just listed six online rows from — and read
63
+ * identically for `--provider devin` (a real provider with no row here) and
64
+ * `--provider bogus-xyz` (a typo).
65
+ *
66
+ * The provider set is OPEN — a daemon registers a row per capability it
67
+ * detects — so a typo cannot honestly be rejected up front. But it can be told
68
+ * apart from an empty account, by naming the filter that did the excluding and
69
+ * pointing at the one command that shows which providers actually exist.
70
+ */
71
+ export declare function emptyListMessage(input: {
72
+ provider: string | null;
73
+ local: boolean;
74
+ }): string;
75
+ /**
76
+ * What the DEFAULT column says for one row.
77
+ *
78
+ * Names the SCOPE rather than printing a bare marker, because a workspace
79
+ * default suppresses a user one — so "mine is set and nothing changed" is a
80
+ * real state, and a column that only said "yes" could not explain it.
81
+ */
82
+ export declare function defaultMarker(harnessId: string, defaults: HarnessDefaults): string;
22
83
  export type HarnessSelection = {
23
84
  /** Rows to print, after every filter and the `--limit` slice. */
24
85
  visible: HarnessRow[];
@@ -96,9 +157,29 @@ export type HarnessesFilters = {
96
157
  limit: number | null;
97
158
  };
98
159
  /** The exact object `skrr harnesses list --json` prints. */
160
+ /**
161
+ * The default-harness block `GET /api/harnesses` already returns.
162
+ *
163
+ * `harnesses set-default` writes it and, until now, nothing in the CLI read it
164
+ * back: no column, no JSON field, and no `get-default` command. At
165
+ * `--scope workspace` — which the setter's own help says "affects every
166
+ * member" — that meant a user could change the default for everyone and then
167
+ * have no command that reports what it currently is.
168
+ *
169
+ * The scope matters as much as the id, because a workspace default SUPPRESSES a
170
+ * user one. Reporting only "which harness" would leave the more confusing case
171
+ * ("I set mine and it did not take effect") unexplained.
172
+ */
173
+ export type HarnessDefaults = {
174
+ effectiveDefaultHarnessId?: string | null;
175
+ defaultHarnessScope?: 'user' | 'workspace' | null;
176
+ userDefaultHarnessId?: string | null;
177
+ workspaceDefaultHarnessId?: string | null;
178
+ };
99
179
  export type HarnessesJson = {
100
180
  harnesses: HarnessRow[];
101
181
  filters: HarnessesFilters;
182
+ defaults: HarnessDefaults;
102
183
  counts: {
103
184
  /** `harnesses.length`. */
104
185
  returned: number;
@@ -132,7 +213,7 @@ export type HarnessesJson = {
132
213
  * (`compareHarnessRows`), not in server order. Round 1 introduced that sort;
133
214
  * it is deliberate, so `.harnesses[0]` is the row you almost always want.
134
215
  */
135
- export declare function buildHarnessesJson(selection: HarnessSelection, filters: HarnessesFilters): HarnessesJson;
216
+ export declare function buildHarnessesJson(selection: HarnessSelection, filters: HarnessesFilters, defaults?: HarnessDefaults): HarnessesJson;
136
217
  /**
137
218
  * Rows with the provider in its CANONICAL spelling.
138
219
  *
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shortMachineLabel = shortMachineLabel;
4
+ exports.emptyListMessage = emptyListMessage;
5
+ exports.defaultMarker = defaultMarker;
3
6
  exports.isLocalHarnessRow = isLocalHarnessRow;
4
7
  exports.selectHarnessRows = selectHarnessRows;
5
8
  exports.buildHarnessesJson = buildHarnessesJson;
@@ -14,6 +17,67 @@ const harness_provider_input_1 = require("../../lib/harness-provider-input");
14
17
  * Products skrr provisions and owns. A row that says it is one of these is
15
18
  * cloud, whatever `harnessLocation` reads.
16
19
  */
20
+ /**
21
+ * An 8-character machine label that is still a machine.
22
+ *
23
+ * `machineUuid` is only a bare uuid when the row's identity kind is
24
+ * `machine-uuid`. The other kinds carry a PREFIXED value —
25
+ * `dedicated-runtime:rtl_…`, `hosted-machine:…` — so a blind `.slice(0, 8)`
26
+ * printed the literal word `dedicate` for every dedicated runtime, identically,
27
+ * across machines that had nothing to do with each other. Keeping the tail is
28
+ * what makes the column distinguish anything: the prefix is the part every row
29
+ * of that kind shares.
30
+ */
31
+ function shortMachineLabel(machineUuid) {
32
+ if (!machineUuid)
33
+ return '-';
34
+ const separator = machineUuid.indexOf(':');
35
+ const identifier = separator === -1 ? machineUuid : machineUuid.slice(separator + 1);
36
+ return identifier.slice(0, 8) || '-';
37
+ }
38
+ /**
39
+ * What to say when the table would be empty.
40
+ *
41
+ * A `--provider` filter that matched nothing is not evidence about the daemon.
42
+ * The old copy asked "Is your daemon connected?" regardless, which sent a user
43
+ * to debug a connection they had just listed six online rows from — and read
44
+ * identically for `--provider devin` (a real provider with no row here) and
45
+ * `--provider bogus-xyz` (a typo).
46
+ *
47
+ * The provider set is OPEN — a daemon registers a row per capability it
48
+ * detects — so a typo cannot honestly be rejected up front. But it can be told
49
+ * apart from an empty account, by naming the filter that did the excluding and
50
+ * pointing at the one command that shows which providers actually exist.
51
+ */
52
+ function emptyListMessage(input) {
53
+ const hiddenByDefault = input.local
54
+ ? ' Offline rows are hidden by default (--all shows them) and hosted rows are excluded (--no-local includes them).'
55
+ : '';
56
+ if (input.provider) {
57
+ return (`No harnesses with provider "${input.provider}".${hiddenByDefault}` +
58
+ ' Run `skrr harnesses list --all` without --provider to see which providers this account has registered.');
59
+ }
60
+ return input.local
61
+ ? `No harnesses found. Is your daemon connected?${hiddenByDefault}`
62
+ : 'No harnesses found.';
63
+ }
64
+ /**
65
+ * What the DEFAULT column says for one row.
66
+ *
67
+ * Names the SCOPE rather than printing a bare marker, because a workspace
68
+ * default suppresses a user one — so "mine is set and nothing changed" is a
69
+ * real state, and a column that only said "yes" could not explain it.
70
+ */
71
+ function defaultMarker(harnessId, defaults) {
72
+ if (defaults.effectiveDefaultHarnessId === harnessId) {
73
+ return defaults.defaultHarnessScope === 'workspace' ? 'workspace' : 'you';
74
+ }
75
+ // Set by this user, but a workspace default is winning. Saying so here is the
76
+ // only place a user finds out why their own choice is not in effect.
77
+ if (defaults.userDefaultHarnessId === harnessId)
78
+ return 'you (overridden)';
79
+ return '-';
80
+ }
17
81
  const MANAGED_MACHINE_PRODUCTS = new Set(['hosted-machine', 'hosted-job-machine']);
18
82
  const MANAGED_REGISTRATION_SOURCES = new Set([
19
83
  'hosted-machine',
@@ -114,10 +178,16 @@ function selectHarnessRows(rows, options) {
114
178
  * (`compareHarnessRows`), not in server order. Round 1 introduced that sort;
115
179
  * it is deliberate, so `.harnesses[0]` is the row you almost always want.
116
180
  */
117
- function buildHarnessesJson(selection, filters) {
181
+ function buildHarnessesJson(selection, filters, defaults = {}) {
118
182
  return {
119
183
  harnesses: selection.visible,
120
184
  filters,
185
+ defaults: {
186
+ effectiveDefaultHarnessId: defaults.effectiveDefaultHarnessId ?? null,
187
+ defaultHarnessScope: defaults.defaultHarnessScope ?? null,
188
+ userDefaultHarnessId: defaults.userDefaultHarnessId ?? null,
189
+ workspaceDefaultHarnessId: defaults.workspaceDefaultHarnessId ?? null,
190
+ },
121
191
  counts: {
122
192
  returned: selection.visible.length,
123
193
  matched: selection.matched,
@@ -167,7 +237,13 @@ function lastSeenMs(row) {
167
237
  * previously undiscoverable.
168
238
  */
169
239
  class HarnessesList extends base_command_1.BaseCommand {
170
- static description = `List your harnesses — your local Claude Code, Codex, ${auth_core_1.FIRST_PARTY_HARNESS.displayName}, one row per (machine, provider). The id here is what \`agents attach-harness\` pins. Online only unless --all.`;
240
+ // Deliberately NOT an enumeration. The provider set is open — a daemon
241
+ // registers a row per capability it detects, so this account carries `hermes`
242
+ // and `gemini` rows that appear in no hand-written list in this package, while
243
+ // every list that was written omitted `devin` and `goose`. A subset printed as
244
+ // if it were the set is the drift root `CLAUDE.md` names: "a second
245
+ // declaration is how a surface silently drifts".
246
+ static description = 'List your harnesses — your local coding agents, one row per (machine, provider). The id here is what `agents attach-harness` pins. Online only unless --all.';
171
247
  static examples = [
172
248
  '<%= config.bin %> harnesses list',
173
249
  '<%= config.bin %> harnesses list --provider claude',
@@ -190,7 +266,12 @@ class HarnessesList extends base_command_1.BaseCommand {
190
266
  exclusive: ['status', 'online'],
191
267
  }),
192
268
  provider: core_1.Flags.string({
193
- description: `Filter by provider (e.g. claude, codex, opencode, openclaw, ${auth_core_1.FIRST_PARTY_HARNESS.provider}).`,
269
+ // Two examples, not a set. One of them is the first-party harness under
270
+ // its CANONICAL spelling, which `harness-provider-input.spec.ts` pins so
271
+ // help can never teach a retired one — but the old text read as an
272
+ // enumeration and omitted `devin`, `goose`, `hermes` and `gemini`, all of
273
+ // which are real values of this flag.
274
+ description: `Filter by provider, e.g. claude or ${auth_core_1.FIRST_PARTY_HARNESS.provider}. The PROVIDER column lists what this machine actually registered; it is wider than these examples.`,
194
275
  }),
195
276
  daemon: core_1.Flags.string({
196
277
  description: 'Filter by daemon id, matching any substring — e.g. the machine hash in daemon_<hash>_<source>_<profile>.',
@@ -222,9 +303,7 @@ class HarnessesList extends base_command_1.BaseCommand {
222
303
  const query = params.toString() ? `?${params.toString()}` : '';
223
304
  let res;
224
305
  try {
225
- res = await (0, api_fetch_1.apiFetch)(`/api/harnesses${query}`, {
226
- credential: this.resolvedCredential,
227
- });
306
+ res = await (0, api_fetch_1.apiFetch)(`/api/harnesses${query}`, { credential: this.resolvedCredential });
228
307
  }
229
308
  catch (err) {
230
309
  this.handleApiError(err);
@@ -242,7 +321,7 @@ class HarnessesList extends base_command_1.BaseCommand {
242
321
  provider: provider ?? null,
243
322
  daemon: flags.daemon ?? null,
244
323
  limit: flags.limit ?? null,
245
- });
324
+ }, res);
246
325
  this.log(JSON.stringify(payload, null, 2));
247
326
  return;
248
327
  }
@@ -263,21 +342,27 @@ class HarnessesList extends base_command_1.BaseCommand {
263
342
  this.log(`No harnesses match the default filters. Hidden: ${hidden.join(', ')}.`);
264
343
  return;
265
344
  }
266
- this.log(flags.local
267
- ? 'No harnesses found. Is your daemon connected? Offline rows are hidden by default (--all shows them) and hosted rows are excluded (--no-local includes them).'
268
- : 'No harnesses found.');
345
+ this.log(emptyListMessage({ provider: provider ?? null, local: flags.local }));
269
346
  return;
270
347
  }
271
348
  const tableRows = selection.visible.map((r) => ({
272
349
  id: (0, format_1.formatCell)(r._id),
273
350
  name: (0, format_1.formatCell)(r.name),
274
351
  provider: (0, format_1.formatCell)(r.provider),
275
- status: (0, format_1.formatCell)(r.status),
352
+ status: r.superseded ? 'superseded' : (0, format_1.formatCell)(r.status),
353
+ default: defaultMarker(r._id, res),
276
354
  daemon: (0, format_1.formatCell)(r.daemonId),
277
355
  model: (0, format_1.formatCell)(r.model),
278
- machine: r.machineUuid ? r.machineUuid.slice(0, 8) : '-',
356
+ machine: shortMachineLabel(r.machineUuid),
279
357
  seen: (0, format_1.localStamp)(r.lastSeenAt),
280
358
  }));
359
+ // `machines list` reports a fraction like `6/7` whose denominator counts
360
+ // LIVE rows — every row minus the ones a later registration replaced. From
361
+ // this table that number was underivable: ~60 rows printed for one machine
362
+ // and nothing on screen added up to 7, with the reconciling sentence living
363
+ // only inside a third command (`machines show`). Now that the server labels
364
+ // supersession here too, say it where the rows are.
365
+ const supersededShown = selection.visible.filter((r) => r.superseded).length;
281
366
  (0, format_1.renderTable)(tableRows, [
282
367
  { key: 'id', header: 'HARNESS ID' },
283
368
  // `harnesses alias` exists to make a row recognisable in a LIST. Without
@@ -286,11 +371,18 @@ class HarnessesList extends base_command_1.BaseCommand {
286
371
  { key: 'name', header: 'NAME', maxWidth: 22 },
287
372
  { key: 'provider', header: 'PROVIDER' },
288
373
  { key: 'status', header: 'STATUS' },
374
+ // `set-default` had no reader anywhere in the CLI. This is it.
375
+ { key: 'default', header: 'DEFAULT' },
289
376
  { key: 'daemon', header: 'DAEMON ID', verbatim: true },
290
377
  { key: 'model', header: 'MODEL', maxWidth: 28 },
291
378
  { key: 'machine', header: 'MACHINE' },
292
379
  { key: 'seen', header: `LAST SEEN (${(0, format_1.localZone)()})` },
293
380
  ], (line) => this.log(line));
381
+ if (supersededShown > 0) {
382
+ // This is the sentence that makes `machines list`'s fraction derivable
383
+ // from here: live = printed − superseded.
384
+ this.log(`(${supersededShown} of ${selection.visible.length} shown are superseded — a later registration on the same machine replaced them, so they will not come back. ${selection.visible.length - supersededShown} live.)`);
385
+ }
294
386
  if (selection.hiddenByLocal > 0) {
295
387
  // Same reason as the JSON `counts` block: the `--local` default is the
296
388
  // single biggest source of "the list is shorter than I expected".
@@ -38,7 +38,16 @@ class HarnessesModels extends base_command_1.BaseCommand {
38
38
  const report = payload;
39
39
  const models = report?.models ?? [];
40
40
  if (models.length === 0) {
41
- this.log('This harness advertises no models.');
41
+ // "This harness advertises no models." is true and answers none of the
42
+ // three questions a reader has: is this a product gap, my machine's
43
+ // fault, or a broken command? Saying which provider was resolved and
44
+ // where the list comes from picks one — and, in the bug this replaced,
45
+ // would have shown `provider` as null on a Claude harness and named the
46
+ // defect on sight.
47
+ const resolved = report?.provider;
48
+ this.log(resolved
49
+ ? `No models are listed for ${resolved}. skrr keeps a curated list per provider and ${resolved} has no entries in it, so pick a model on the agent instead — \`skrr agents update <agent> --model <id>\` accepts an id this list does not name.`
50
+ : 'This harness resolved to no provider, so no model list could be built. That is a defect, not a configuration problem — report it with the harness id.');
42
51
  return;
43
52
  }
44
53
  if (report?.provider || report?.source) {
@@ -10,3 +10,18 @@ export default class HarnessesShow extends BaseCommand {
10
10
  };
11
11
  run(): Promise<void>;
12
12
  }
13
+ /**
14
+ * Say what the caller DID pass, not only what they did not.
15
+ *
16
+ * Every id in this product is a bare string, and three of them are printed side
17
+ * by side: `machines show` takes a machine or daemon id, `harnesses show` takes
18
+ * a Harness id. `machines show <daemon-id>` succeeds, so a user who learns that
19
+ * reasonably tries the same id here — and got back only a restatement of the
20
+ * rule they had just broken, plus the Mongo field name `_id`, which names an
21
+ * implementation detail rather than anything they can see.
22
+ *
23
+ * The shapes below are the ones our own output prints, so recognising them
24
+ * costs nothing and turns a refusal into a redirect. Anything unrecognised
25
+ * still falls through to the general answer rather than being guessed at.
26
+ */
27
+ export declare function describeWrongHarnessRef(ref: string): string;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeWrongHarnessRef = describeWrongHarnessRef;
3
4
  const core_1 = require("@oclif/core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const harness_provider_input_1 = require("../../lib/harness-provider-input");
@@ -35,9 +36,9 @@ class HarnessesShow extends base_command_1.BaseCommand {
35
36
  // "HTTP 400 Bad Request — GET /api/harnesses/<id>". Any 400 on this
36
37
  // lookup is the id being wrong, so branch on the status.
37
38
  if (err instanceof api_fetch_1.ApiFetchError && err.status === 400) {
38
- this.error(`Not a harness id: ${args.harness}\n` +
39
- 'A harness id is the 24-character Harness _id. Run `skrr harnesses list --all` ' +
40
- 'to see the ids on this account.', { exit: 1 });
39
+ this.error(`Not a harness id: ${args.harness}\n${describeWrongHarnessRef(args.harness)}`, {
40
+ exit: 1,
41
+ });
41
42
  return;
42
43
  }
43
44
  this.handleApiError(err);
@@ -61,6 +62,14 @@ class HarnessesShow extends base_command_1.BaseCommand {
61
62
  this.log(` provider ${harness.provider}`);
62
63
  // `harnessLocation` is the difference between "your laptop" and "a cloud row",
63
64
  // and it decides which verbs apply: ping and claudeEngine are local-only.
65
+ // When YOUR alias is covering the shared name, say so and show both. The
66
+ // header prints the effective name, which is the alias — without this the
67
+ // value `harnesses update --name` just wrote appears on no surface at all,
68
+ // and you cannot tell you are reading your own label rather than the one
69
+ // everyone else sees.
70
+ if (harness.aliased && harness.sharedName && harness.sharedName !== harness.name) {
71
+ this.log(` shared name ${harness.sharedName} (your alias is showing instead)`);
72
+ }
64
73
  this.log(` mode ${harness.harnessLocation ?? 'unknown'}`);
65
74
  this.log(` status ${harness.status ?? 'unknown'}`);
66
75
  // Only for the claude provider. The daemon stamps `claudeEngine` on every
@@ -77,3 +86,35 @@ class HarnessesShow extends base_command_1.BaseCommand {
77
86
  }
78
87
  }
79
88
  exports.default = HarnessesShow;
89
+ /**
90
+ * Say what the caller DID pass, not only what they did not.
91
+ *
92
+ * Every id in this product is a bare string, and three of them are printed side
93
+ * by side: `machines show` takes a machine or daemon id, `harnesses show` takes
94
+ * a Harness id. `machines show <daemon-id>` succeeds, so a user who learns that
95
+ * reasonably tries the same id here — and got back only a restatement of the
96
+ * rule they had just broken, plus the Mongo field name `_id`, which names an
97
+ * implementation detail rather than anything they can see.
98
+ *
99
+ * The shapes below are the ones our own output prints, so recognising them
100
+ * costs nothing and turns a refusal into a redirect. Anything unrecognised
101
+ * still falls through to the general answer rather than being guessed at.
102
+ */
103
+ function describeWrongHarnessRef(ref) {
104
+ const seeIds = 'Run `skrr harnesses list --all` to see the harness ids on this account.';
105
+ if (/^daemon_/.test(ref)) {
106
+ return `That is a daemon id. \`skrr machines show ${ref}\` takes those. ${seeIds}`;
107
+ }
108
+ if (/^machine:/.test(ref)) {
109
+ return `That is a machine id. \`skrr machines show ${ref}\` takes those. ${seeIds}`;
110
+ }
111
+ if (/^(lease:|dedicated-runtime:|hosted-machine:)/.test(ref)) {
112
+ return ('That is a machine id for a rented runtime. `skrr machines show` takes it as printed; ' +
113
+ `\`skrr machines dedicated show\` takes the part after the colon. ${seeIds}`);
114
+ }
115
+ if (/^[0-9a-f]{24}$/i.test(ref)) {
116
+ // Right shape, wrong row — so the id is not the thing to explain.
117
+ return `No harness on this account has that id. ${seeIds}`;
118
+ }
119
+ return `A harness id is 24 hexadecimal characters. ${seeIds}`;
120
+ }
@@ -29,7 +29,7 @@ class HarnessesUpdate extends base_command_1.BaseCommand {
29
29
  }),
30
30
  description: core_1.Flags.string({ description: 'New description' }),
31
31
  'claude-engine': core_1.Flags.string({
32
- description: 'How Claude Code runs on this machine: `sdk` in-process (needed for AskUserQuestion), `cli` as a child process. Local harnesses only.',
32
+ description: 'How Claude Code runs on this machine: `sdk` in-process (needed for AskUserQuestion), `cli` as a child process. Local CLAUDE harnesses only — the server refuses it on any other provider, because nothing would ever read it.',
33
33
  options: ['cli', 'sdk'],
34
34
  }),
35
35
  };
@@ -1,6 +1,12 @@
1
1
  import { BaseCommand } from '../../base-command';
2
2
  export type UsageRow = {
3
3
  /** Fleet rollup only — one row per harness. */
4
+ harnessId?: string;
5
+ /**
6
+ * The pre-rename spelling of `harnessId`. The server stopped sending it at
7
+ * the harness rename; reading it alone left the HARNESS column printing `-`
8
+ * on every row of the rollup. Kept as a fallback for an older deployment.
9
+ */
4
10
  runtimeId?: string;
5
11
  name?: string;
6
12
  /** Single-harness report only — one row per hour. */
@@ -12,6 +18,25 @@ export type UsageRow = {
12
18
  outputTokens?: number;
13
19
  cachedInputTokens?: number;
14
20
  };
21
+ /**
22
+ * The rows to render, whichever spelling the server used.
23
+ *
24
+ * `byHarness` is what it sends today. This reader asked only for `byRuntime` —
25
+ * the pre-rename spelling — so the fleet rollup was ALWAYS empty and the
26
+ * command always printed "No usage recorded.", while `--json` on the same call
27
+ * showed $24.93 across 46 sessions. A rollup cannot be empty while its own
28
+ * members are not, which is what made it findable.
29
+ *
30
+ * The old spellings stay as fallbacks, after the current one, so an older
31
+ * deployment still renders. They are not the primary, which is the whole bug.
32
+ */
33
+ export declare function selectUsageRows(payload: {
34
+ byHarness?: UsageRow[];
35
+ byRuntime?: UsageRow[];
36
+ usage?: UsageRow[];
37
+ } | undefined): UsageRow[];
38
+ /** The HARNESS cell, under either spelling of the id. */
39
+ export declare function usageRowHarnessCell(row: UsageRow): string;
15
40
  export interface UsageTable {
16
41
  rows: Record<string, string>[];
17
42
  columns: {