@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.
- package/bin/dev-fallback.js +175 -4
- package/dist/base-command.js +46 -2
- package/dist/commands/agents/actions/create.js +18 -7
- package/dist/commands/agents/actions/delete.d.ts +1 -0
- package/dist/commands/agents/actions/delete.js +7 -0
- package/dist/commands/agents/actions/update.js +5 -3
- package/dist/commands/agents/api-actions/add.js +5 -2
- package/dist/commands/agents/api-actions/index.d.ts +11 -0
- package/dist/commands/agents/api-actions/index.js +27 -1
- package/dist/commands/agents/avatar/default/index.d.ts +11 -0
- package/dist/commands/agents/avatar/default/index.js +17 -0
- package/dist/commands/agents/avatar/default/set.d.ts +2 -1
- package/dist/commands/agents/avatar/default/set.js +32 -7
- package/dist/commands/agents/avatar/upload.js +7 -1
- package/dist/commands/agents/categories/delete.js +8 -0
- package/dist/commands/agents/categories/index.d.ts +21 -5
- package/dist/commands/agents/categories/index.js +68 -15
- package/dist/commands/agents/chat.d.ts +21 -0
- package/dist/commands/agents/chat.js +94 -7
- package/dist/commands/agents/create.js +11 -4
- package/dist/commands/agents/list.d.ts +15 -0
- package/dist/commands/agents/list.js +31 -1
- package/dist/commands/agents/scene-background/upload.d.ts +30 -0
- package/dist/commands/agents/scene-background/upload.js +97 -0
- package/dist/commands/agents/show.js +13 -0
- package/dist/commands/agents/tools/calls.d.ts +23 -5
- package/dist/commands/agents/tools/calls.js +81 -18
- package/dist/commands/agents/update.js +15 -5
- package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
- package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
- package/dist/commands/convos/messages.d.ts +13 -0
- package/dist/commands/convos/messages.js +40 -16
- package/dist/commands/daemon/index.js +8 -1
- package/dist/commands/daemon/logs.d.ts +26 -0
- package/dist/commands/daemon/logs.js +56 -0
- package/dist/commands/daemon/uninstall.js +1 -1
- package/dist/commands/followups/act.js +3 -2
- package/dist/commands/followups/cancel.d.ts +6 -3
- package/dist/commands/followups/cancel.js +16 -6
- package/dist/commands/followups/observability.d.ts +23 -0
- package/dist/commands/followups/observability.js +114 -0
- package/dist/commands/followups/reschedule.js +1 -1
- package/dist/commands/followups/resolve.js +3 -3
- package/dist/commands/followups/show.js +2 -1
- package/dist/commands/followups/watch.js +9 -5
- package/dist/commands/harnesses/forget.js +28 -0
- package/dist/commands/harnesses/install.d.ts +41 -0
- package/dist/commands/harnesses/install.js +165 -0
- package/dist/commands/harnesses/installers.d.ts +45 -0
- package/dist/commands/harnesses/installers.js +155 -0
- package/dist/commands/harnesses/list.d.ts +82 -1
- package/dist/commands/harnesses/list.js +104 -12
- package/dist/commands/harnesses/models.js +10 -1
- package/dist/commands/harnesses/show.d.ts +15 -0
- package/dist/commands/harnesses/show.js +44 -3
- package/dist/commands/harnesses/update.js +1 -1
- package/dist/commands/harnesses/usage.d.ts +25 -0
- package/dist/commands/harnesses/usage.js +24 -2
- package/dist/commands/instructions/status.js +13 -1
- package/dist/commands/machines/dedicated/catalog.js +4 -15
- package/dist/commands/machines/dedicated/create.d.ts +8 -0
- package/dist/commands/machines/dedicated/create.js +63 -2
- package/dist/commands/machines/dedicated/destroy.js +19 -5
- package/dist/commands/machines/dedicated/exec.js +3 -1
- package/dist/commands/machines/dedicated/list.js +9 -6
- package/dist/commands/machines/dedicated/price-book.d.ts +17 -0
- package/dist/commands/machines/dedicated/price-book.js +35 -0
- package/dist/commands/machines/hosted/list.js +12 -0
- package/dist/commands/machines/list.js +11 -0
- package/dist/commands/machines/show.d.ts +1 -0
- package/dist/commands/machines/show.js +40 -11
- package/dist/commands/profiles/list.d.ts +23 -0
- package/dist/commands/profiles/list.js +96 -0
- package/dist/commands/search/skills.d.ts +11 -1
- package/dist/commands/search/skills.js +26 -8
- package/dist/commands/skills/details.d.ts +2 -0
- package/dist/commands/skills/details.js +24 -1
- package/dist/commands/skills/import-as-actions.d.ts +32 -0
- package/dist/commands/skills/import-as-actions.js +175 -0
- package/dist/commands/skills/install.d.ts +2 -0
- package/dist/commands/skills/install.js +24 -1
- package/dist/commands/skills/invocable.js +6 -2
- package/dist/commands/skills/local.d.ts +40 -0
- package/dist/commands/skills/local.js +160 -0
- package/dist/commands/skills/search.d.ts +11 -1
- package/dist/commands/skills/search.js +29 -8
- package/dist/commands/spaces/actions/add.js +23 -5
- package/dist/commands/spaces/actions/available.js +3 -0
- package/dist/commands/spaces/actions/list.d.ts +5 -0
- package/dist/commands/spaces/actions/list.js +49 -3
- package/dist/commands/spaces/actions/run.d.ts +28 -0
- package/dist/commands/spaces/actions/run.js +146 -0
- package/dist/commands/spaces/actions/update.js +27 -5
- package/dist/commands/spaces/label-summaries.d.ts +27 -0
- package/dist/commands/spaces/label-summaries.js +64 -0
- package/dist/commands/spaces/task-rollups.d.ts +5 -0
- package/dist/commands/spaces/task-rollups.js +15 -4
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
- package/dist/commands/tasks/actions/attach.js +32 -7
- package/dist/commands/tasks/complete.js +13 -0
- package/dist/commands/tasks/create.js +1 -1
- package/dist/commands/tasks/events/append.js +29 -6
- package/dist/commands/tasks/expectations/assess.d.ts +1 -0
- package/dist/commands/tasks/expectations/assess.js +43 -5
- package/dist/commands/tasks/expectations.js +77 -16
- package/dist/commands/tasks/follow-up.js +2 -2
- package/dist/commands/tasks/lifecycle/definitions.js +21 -2
- package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
- package/dist/commands/tasks/lifecycle/detach.js +43 -3
- package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
- package/dist/commands/tasks/lifecycle/migrate.js +42 -3
- package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
- package/dist/commands/tasks/lifecycle/reopen.js +40 -3
- package/dist/commands/tasks/lifecycle/transition.js +33 -2
- package/dist/commands/tasks/list.js +16 -9
- package/dist/commands/tasks/move.js +11 -13
- package/dist/commands/tasks/review-queue.d.ts +15 -0
- package/dist/commands/tasks/review-queue.js +180 -31
- package/dist/commands/tasks/show.d.ts +11 -0
- package/dist/commands/tasks/show.js +105 -12
- package/dist/commands/tasks/trust.js +7 -2
- package/dist/commands/tasks/update.js +46 -10
- package/dist/commands/tasks/updates/add.js +1 -1
- package/dist/commands/token/list.d.ts +22 -0
- package/dist/commands/token/list.js +68 -0
- package/dist/commands/token/revoke.d.ts +23 -0
- package/dist/commands/token/revoke.js +53 -0
- package/dist/lib/agent-actions.d.ts +57 -2
- package/dist/lib/agent-actions.js +113 -21
- package/dist/lib/agent-pin-mute.d.ts +10 -5
- package/dist/lib/agent-pin-mute.js +17 -4
- package/dist/lib/agent-visual-refs.d.ts +27 -0
- package/dist/lib/agent-visual-refs.js +127 -2
- package/dist/lib/agentic-stream.d.ts +6 -0
- package/dist/lib/agentic-stream.js +34 -3
- package/dist/lib/assignee-resolver.d.ts +1 -1
- package/dist/lib/assignee-resolver.js +8 -0
- package/dist/lib/bulk-task-targets.d.ts +12 -0
- package/dist/lib/bulk-task-targets.js +34 -1
- package/dist/lib/cli-installers.d.ts +130 -0
- package/dist/lib/cli-installers.js +145 -0
- package/dist/lib/command-miss.js +55 -7
- package/dist/lib/dedicated-lease-command.d.ts +16 -1
- package/dist/lib/dedicated-lease-command.js +26 -3
- package/dist/lib/dedicated-machines.d.ts +135 -1
- package/dist/lib/dedicated-machines.js +307 -3
- package/dist/lib/failed-lookup.d.ts +3 -0
- package/dist/lib/failed-lookup.js +40 -0
- package/dist/lib/first-party-harness-agent.js +9 -2
- package/dist/lib/first-party-harness-doctor.d.ts +15 -0
- package/dist/lib/first-party-harness-doctor.js +126 -27
- package/dist/lib/followups.d.ts +14 -6
- package/dist/lib/followups.js +31 -6
- package/dist/lib/harnesses.d.ts +8 -0
- package/dist/lib/instruction-input.d.ts +19 -1
- package/dist/lib/instruction-input.js +52 -1
- package/dist/lib/label-scope.d.ts +15 -0
- package/dist/lib/label-scope.js +25 -5
- package/dist/lib/local-skills.d.ts +98 -0
- package/dist/lib/local-skills.js +195 -0
- package/dist/lib/machines.d.ts +10 -10
- package/dist/lib/machines.js +23 -1
- package/dist/lib/search-query.d.ts +19 -0
- package/dist/lib/search-query.js +35 -0
- package/dist/lib/space-custom-actions.d.ts +72 -0
- package/dist/lib/space-custom-actions.js +117 -0
- package/dist/lib/space-resolver.d.ts +1 -21
- package/dist/lib/space-resolver.js +19 -10
- package/dist/lib/task-closure.d.ts +3 -2
- package/dist/lib/task-closure.js +18 -20
- package/dist/lib/task-lifecycle-output.js +117 -9
- package/dist/lib/task-ref-resolver.d.ts +1 -17
- package/dist/lib/task-ref-resolver.js +18 -8
- package/dist/lib/tasks.js +9 -3
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
- package/oclif.manifest.json +15505 -14460
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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: {
|