@bli-cockpit/cli 0.2.88 → 0.2.90
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/dist/commands/local-args-tower-admin.js +4 -0
- package/dist/commands/local-args-tower-models.js +44 -0
- package/dist/commands/local-args-tower.js +1 -0
- package/dist/commands/local-args.js +3 -1
- package/dist/commands/local-help-commands.js +21 -1
- package/dist/commands/local-help.js +3 -1
- package/dist/commands/local.js +3 -0
- package/dist/commands/models.js +95 -0
- package/dist/commands/ops.js +51 -1
- package/dist/commands/public-root.js +1 -1
- package/package.json +2 -2
|
@@ -68,6 +68,9 @@ export function parseOpsArgs(args) {
|
|
|
68
68
|
"--memory-days",
|
|
69
69
|
// BLI-3798: the coverage half on its own, with the bucket table.
|
|
70
70
|
"--coverage",
|
|
71
|
+
// BLI-3912: one line per model — latency, tokens/s, today's spend, last
|
|
72
|
+
// probe verdict. A SECOND door like --memory, asked for only when named.
|
|
73
|
+
"--models",
|
|
71
74
|
"--person",
|
|
72
75
|
"--dry-run",
|
|
73
76
|
"--json",
|
|
@@ -108,6 +111,7 @@ export function parseOpsArgs(args) {
|
|
|
108
111
|
coverage: coverageOnly,
|
|
109
112
|
skips: values.booleans.has("--skips"),
|
|
110
113
|
memory: values.booleans.has("--memory") || memoryDays !== undefined,
|
|
114
|
+
models: values.booleans.has("--models"),
|
|
111
115
|
...(memoryDays === undefined ? {} : { memoryDays: Number(memoryDays) }),
|
|
112
116
|
...base,
|
|
113
117
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cockpit models` — the model-card shelf, in the terminal (BLI-3912).
|
|
3
|
+
*
|
|
4
|
+
* cockpit models # every card: documented vs measured
|
|
5
|
+
* cockpit models list --json
|
|
6
|
+
* cockpit models show openai:gpt-5.6-terra
|
|
7
|
+
*
|
|
8
|
+
* Two nouns that look alike and are not: `cockpit model` is the ORG SETTING for
|
|
9
|
+
* which model a slot runs on (`model show`, `model set`); `cockpit models` is
|
|
10
|
+
* the shelf that setting picks FROM — windows, prices, reasoning vocabulary,
|
|
11
|
+
* quirks, probe verdicts and stale marks. The help text for each says so.
|
|
12
|
+
*/
|
|
13
|
+
import { optionalNonEmpty, optionalUrl, parseNamedArgs } from "./local-arg-values.js";
|
|
14
|
+
export function parseModelsArgs(args) {
|
|
15
|
+
const values = parseNamedArgs(args, {
|
|
16
|
+
allowedFlags: ["--home", "--dashboard-url", "--json"],
|
|
17
|
+
valueFlags: ["--home", "--dashboard-url"],
|
|
18
|
+
});
|
|
19
|
+
if (values.positionals.length > 2) {
|
|
20
|
+
throw new Error("models accepts at most an action (list|show) and one model id.");
|
|
21
|
+
}
|
|
22
|
+
const [rawAction, rawId] = values.positionals;
|
|
23
|
+
const base = {
|
|
24
|
+
homeDir: optionalNonEmpty(values.flags.get("--home")),
|
|
25
|
+
dashboardUrl: optionalUrl(values.flags.get("--dashboard-url")),
|
|
26
|
+
json: values.booleans.has("--json"),
|
|
27
|
+
};
|
|
28
|
+
if (!rawAction || rawAction === "list") {
|
|
29
|
+
if (rawAction === "list" && rawId) {
|
|
30
|
+
throw new Error("models list takes no model id — use `models show <provider:model>`.");
|
|
31
|
+
}
|
|
32
|
+
return { kind: "models", action: "list", ...base };
|
|
33
|
+
}
|
|
34
|
+
if (rawAction !== "show") {
|
|
35
|
+
// A bare id is the shape people type first; treat it as `show <id>` rather
|
|
36
|
+
// than refusing on a technicality.
|
|
37
|
+
return { kind: "models", action: "show", modelId: rawAction, ...base };
|
|
38
|
+
}
|
|
39
|
+
const modelId = optionalNonEmpty(rawId);
|
|
40
|
+
if (!modelId) {
|
|
41
|
+
throw new Error("models show needs a model id, e.g. `models show openai:gpt-5.6-terra`.");
|
|
42
|
+
}
|
|
43
|
+
return { kind: "models", action: "show", modelId, ...base };
|
|
44
|
+
}
|
|
@@ -33,5 +33,6 @@ export { SCOUT_MIN_PREFIX_LENGTH, parseScoutArgs, parseOpsArgs, SLACK_WORKSPACE_
|
|
|
33
33
|
export { parseDocsArgs, parseMsgArgs, } from "./local-args-tower-docs-msg.js";
|
|
34
34
|
export { ISSUE_STATES, parseIssueArgs, parseProjectArgs, } from "./local-args-tower-work.js";
|
|
35
35
|
export { SEARCH_KINDS, parseSearchArgs } from "./local-args-tower-search.js";
|
|
36
|
+
export { parseModelsArgs } from "./local-args-tower-models.js";
|
|
36
37
|
export { parseMailArgs } from "./local-args-tower-mail.js";
|
|
37
38
|
export { parseCalArgs } from "./local-args-tower-cal.js";
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* verbatim, no logic change.
|
|
17
17
|
*/
|
|
18
18
|
import { parseAgentRulesArgs, parseAnalyzeArgs, parseAutostartArgs, parseBackfillArgs, parseCleanArgs, parseDoctorArgs, parseInstallArgs, parseLoginArgs, parseMemoryArgs, parseLogoutArgs, parseOnboardArgs, parseReleaseArgs, parseServeArgs, parseSessionsArgs, parseStartArgs, parseStatusArgs, parseSyncArgs, parseUpdateArgs, } from "./local-args-collector.js";
|
|
19
|
-
import { parseBriefArgs, parseCorrectArgs, parseDocsArgs, parseIssueArgs, parseJarvisArgs, parseMailArgs, parseCalArgs, parseModelArgs, parseMsgArgs, parseNotesArgs, parseOpsArgs, parseProjectArgs, parseScoutArgs, parseSearchArgs, parseSettingsArgs, parseSlackArgs, parseTeamArgs, parseWorkbookArgs, } from "./local-args-tower.js";
|
|
19
|
+
import { parseBriefArgs, parseCorrectArgs, parseDocsArgs, parseIssueArgs, parseJarvisArgs, parseMailArgs, parseCalArgs, parseModelArgs, parseMsgArgs, parseNotesArgs, parseOpsArgs, parseProjectArgs, parseModelsArgs, parseScoutArgs, parseSearchArgs, parseSettingsArgs, parseSlackArgs, parseTeamArgs, parseWorkbookArgs, } from "./local-args-tower.js";
|
|
20
20
|
// `normalizeUrl` has always been part of this module's surface — `local.ts` and
|
|
21
21
|
// `local-auth.ts` import it from here — so it stays exported from this address
|
|
22
22
|
// even though it now lives next door. The same goes for the four names the
|
|
@@ -63,6 +63,8 @@ export function parseLocalArgs(argv) {
|
|
|
63
63
|
return parseJarvisArgs(argv.slice(1));
|
|
64
64
|
case "model":
|
|
65
65
|
return parseModelArgs(argv.slice(1));
|
|
66
|
+
case "models":
|
|
67
|
+
return parseModelsArgs(argv.slice(1));
|
|
66
68
|
case "scout":
|
|
67
69
|
return parseScoutArgs(argv.slice(1));
|
|
68
70
|
case "ops":
|
|
@@ -273,6 +273,26 @@ export function localSubcommandHelp(command) {
|
|
|
273
273
|
"Run `cockpit login` first if this machine is not paired.",
|
|
274
274
|
],
|
|
275
275
|
],
|
|
276
|
+
[
|
|
277
|
+
"models",
|
|
278
|
+
[
|
|
279
|
+
"Usage: cockpit models [list|show <provider:model>] [--dashboard-url <url>] [--json]",
|
|
280
|
+
"",
|
|
281
|
+
"The model-card shelf: one card per model, with what the PROVIDER documents (context",
|
|
282
|
+
"window, max output, price per million, reasoning modes, tool calling, structured output,",
|
|
283
|
+
"quirks) beside what OUR machine measured (latency p50/p95, observed tokens/s, the last",
|
|
284
|
+
"belt and conformance probe verdicts).",
|
|
285
|
+
"Every value carries the page it was read from; a field the provider does not state reads",
|
|
286
|
+
"unknown with the reason, never a guess.",
|
|
287
|
+
"STALE:<field> means a measurement contradicted the documentation — reasoning-off refused",
|
|
288
|
+
"where the card says it can be turned off, the belt refused where the card says tool",
|
|
289
|
+
"calling. The line under it is the command that re-reads the provider.",
|
|
290
|
+
"`cockpit models` is the SHELF; `cockpit model` is the org setting saying which model a",
|
|
291
|
+
"slot runs on. Different nouns, one letter apart, on purpose.",
|
|
292
|
+
"--json writes one object to stdout; operational lines stay on stderr.",
|
|
293
|
+
"Run `cockpit login` first if this machine is not paired.",
|
|
294
|
+
],
|
|
295
|
+
],
|
|
276
296
|
[
|
|
277
297
|
"scout",
|
|
278
298
|
[
|
|
@@ -295,7 +315,7 @@ export function localSubcommandHelp(command) {
|
|
|
295
315
|
[
|
|
296
316
|
"ops",
|
|
297
317
|
[
|
|
298
|
-
"Usage: cockpit ops [status [--job <id>] [--coverage] [--skips] [--memory [--memory-days N]] | recompile --person <email|name|id> [--dry-run]] [--json]",
|
|
318
|
+
"Usage: cockpit ops [status [--job <id>] [--coverage] [--skips] [--memory [--memory-days N]] [--models] | recompile --person <email|name|id> [--dry-run]] [--json]",
|
|
299
319
|
"",
|
|
300
320
|
" cockpit ops status",
|
|
301
321
|
" One line per scheduled job: when it last produced something, and whether that",
|
|
@@ -25,6 +25,7 @@ export const rootCommandNames = new Set([
|
|
|
25
25
|
"analyze",
|
|
26
26
|
"jarvis",
|
|
27
27
|
"model",
|
|
28
|
+
"models",
|
|
28
29
|
"scout",
|
|
29
30
|
"ops",
|
|
30
31
|
"slack",
|
|
@@ -70,8 +71,9 @@ export function localCommandHelp(command) {
|
|
|
70
71
|
" cockpit analyze [--workspace <path>] [--dashboard-url <url>] [--max-depth <n>] [--max-repos <n>] [--json]",
|
|
71
72
|
" cockpit jarvis [question] [--prompt <question>] [--as <person>] [--date <YYYY-MM-DD>] [--thread <name>] [--model <key>] [--image <path>|--file <path>] [--no-stream] [--threads|--history [--limit <n>]] [--trace <id|last>] [--dashboard-url <url>] [--json [--show-approval-code]]",
|
|
72
73
|
" cockpit model [show|set <provider:model>] [--json]",
|
|
74
|
+
" cockpit models [list|show <provider:model>] [--dashboard-url <url>] [--json]",
|
|
73
75
|
" cockpit scout [start|dismiss|undo <experiment-id>] [--days <n>] [--dashboard-url <url>] [--json]",
|
|
74
|
-
" cockpit ops [status [--job <id>] [--skips] [--memory] | recompile --person <email|name|id> [--dry-run]] [--dashboard-url <url>] [--json]",
|
|
76
|
+
" cockpit ops [status [--job <id>] [--skips] [--memory] [--models] | recompile --person <email|name|id> [--dry-run]] [--dashboard-url <url>] [--json]",
|
|
75
77
|
" cockpit slack [coverage [--workspace bli|blue_pearl] [--stale-only] | read [--person <p>] [--channel <c>] [--query <text>] [--since <YYYY-MM-DD>] [--until <YYYY-MM-DD>] [--limit <n>]] [--json]",
|
|
76
78
|
" cockpit settings [personal [--chat-model <key>] [--brief-model <key>] | switches [set <key> <value>] | models [set --chat <key>] [--memory <id>] | cli-floor [<version>] | env list|set --project <p> --file <f> --content-stdin|delete --id <uuid> [--yes]] [--json]",
|
|
77
79
|
" cockpit team [members | invite <email> --role <role> [--team-id <uuid>] | role <userId> --role <role> [--yes] | device [list] | device revoke <id|name> --reason <label> [--note \"<text>\"] [--yes]] [--json]",
|
package/dist/commands/local.js
CHANGED
|
@@ -16,6 +16,7 @@ import { runDoctor } from "./doctor.js";
|
|
|
16
16
|
import { runStatus } from "./status.js";
|
|
17
17
|
import { runSessions } from "./sessions.js";
|
|
18
18
|
import { runJarvis } from "./jarvis.js";
|
|
19
|
+
import { runModels } from "./models.js";
|
|
19
20
|
import { runScout } from "./scout.js";
|
|
20
21
|
import { runOps } from "./ops.js";
|
|
21
22
|
import { runSlack } from "./slack.js";
|
|
@@ -104,6 +105,8 @@ export async function runLocalCockpitCli(argv, io = defaultIo()) {
|
|
|
104
105
|
return await runJarvis(command, io);
|
|
105
106
|
case "model":
|
|
106
107
|
return await runModel(command, io);
|
|
108
|
+
case "models":
|
|
109
|
+
return await runModels(command, io);
|
|
107
110
|
case "scout":
|
|
108
111
|
return await runScout(command, io);
|
|
109
112
|
case "ops":
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cockpit models` — what do we know about each model? (BLI-3912)
|
|
3
|
+
*
|
|
4
|
+
* Prints the shelf: one line per model with the provider's documented window,
|
|
5
|
+
* price, reasoning mode and tool support beside OUR measured latency, observed
|
|
6
|
+
* tokens/s and last probe verdict, and a `STALE:<field>` mark wherever the two
|
|
7
|
+
* disagree — with the command that fixes it on the next line.
|
|
8
|
+
*
|
|
9
|
+
* `models show <id>` asks the same door for the SIX SECTIONS (BLI-3918) —
|
|
10
|
+
* what's going on, what's wrong, what's right, what should we do, double down,
|
|
11
|
+
* forget about it — so a terminal reads the model page a browser reads, in the
|
|
12
|
+
* same order, including today's spend and the `stale:<field>` markers.
|
|
13
|
+
*
|
|
14
|
+
* **Every line comes from the server** (`/api/models/cards`, rendered by
|
|
15
|
+
* `lib/models/model-lines.ts` and `card-section-lines.ts`). This file decides an
|
|
16
|
+
* exit code and nothing else. A terminal that formatted a card itself would
|
|
17
|
+
* eventually disagree with the board about what "stale" means, and the one that
|
|
18
|
+
* is wrong is always the one somebody is reading. A server that has not shipped
|
|
19
|
+
* the sections yet sends no `section_lines`, and the whole-card `lines` are
|
|
20
|
+
* printed instead — never an empty screen.
|
|
21
|
+
*
|
|
22
|
+
* **Exit codes.** Zero when the shelf was read, whatever it says — a stale card
|
|
23
|
+
* is a fact to act on, not a broken command. One when the read failed or when a
|
|
24
|
+
* named card does not exist, because a script asking about a specific model
|
|
25
|
+
* needs to know it asked about nothing.
|
|
26
|
+
*/
|
|
27
|
+
import { writeLine } from "./cli-io.js";
|
|
28
|
+
import { loadPairedSession, towerJsonRequest, } from "../tower-client.js";
|
|
29
|
+
/** A card read is checked-in data on the server side; it should never hang a shell. */
|
|
30
|
+
const READ_DEADLINE_MS = 30_000;
|
|
31
|
+
export async function runModels(command, io) {
|
|
32
|
+
const session = await loadPairedSession("models", command.homeDir);
|
|
33
|
+
const dashboardUrl = command.dashboardUrl ?? session.dashboard_url;
|
|
34
|
+
const log = (line) => writeLine(io.stderr, line);
|
|
35
|
+
const path = command.action === "show" && command.modelId
|
|
36
|
+
? `/api/models/cards?id=${encodeURIComponent(command.modelId)}§ions=1`
|
|
37
|
+
: "/api/models/cards";
|
|
38
|
+
const result = await towerJsonRequest({
|
|
39
|
+
dashboardUrl,
|
|
40
|
+
path,
|
|
41
|
+
method: "GET",
|
|
42
|
+
deviceToken: session.device_token,
|
|
43
|
+
fetch: io.fetch,
|
|
44
|
+
label: "models",
|
|
45
|
+
timeoutMs: READ_DEADLINE_MS,
|
|
46
|
+
log,
|
|
47
|
+
});
|
|
48
|
+
if (!result.ok) {
|
|
49
|
+
writeFailure(command, io, result);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
const payload = (result.body ?? {});
|
|
53
|
+
if (command.json) {
|
|
54
|
+
writeLine(io.stdout, JSON.stringify({ ok: true, ...payload }));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const lines = payload.section_lines?.length ? payload.section_lines : (payload.lines ?? []);
|
|
58
|
+
for (const line of lines)
|
|
59
|
+
writeLine(io.stdout, line);
|
|
60
|
+
}
|
|
61
|
+
// Never silent about a card the server could not read: a shelf quietly one
|
|
62
|
+
// model short is exactly what the card registry exists to prevent.
|
|
63
|
+
for (const unreadable of payload.unreadable ?? []) {
|
|
64
|
+
writeLine(io.stderr, `[models cli] card unreadable ${JSON.stringify({ card: unreadable })}`);
|
|
65
|
+
}
|
|
66
|
+
writeLine(io.stderr, `[models cli] read ${JSON.stringify({
|
|
67
|
+
action: command.action,
|
|
68
|
+
model: command.modelId ?? null,
|
|
69
|
+
cards: Array.isArray(payload.cards) ? payload.cards.length : payload.card ? 1 : 0,
|
|
70
|
+
sections: payload.section_lines?.length ? "server" : "absent",
|
|
71
|
+
unreadable: payload.unreadable?.length ?? 0,
|
|
72
|
+
})}`);
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
function writeFailure(command, io, failure) {
|
|
76
|
+
// `TowerFailure` carries the server's own words in `detail` — a 404 for a
|
|
77
|
+
// model nobody has carded says exactly which id it was and what to run.
|
|
78
|
+
const message = failure.detail;
|
|
79
|
+
if (command.json) {
|
|
80
|
+
writeLine(io.stdout, JSON.stringify({
|
|
81
|
+
ok: false,
|
|
82
|
+
error: failure.reason,
|
|
83
|
+
message,
|
|
84
|
+
httpStatus: failure.httpStatus ?? null,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
writeLine(io.stdout, message);
|
|
89
|
+
}
|
|
90
|
+
writeLine(io.stderr, `[models cli] not read ${JSON.stringify({
|
|
91
|
+
reason: failure.reason,
|
|
92
|
+
http_status: failure.httpStatus ?? null,
|
|
93
|
+
model: command.modelId ?? null,
|
|
94
|
+
})}`);
|
|
95
|
+
}
|
package/dist/commands/ops.js
CHANGED
|
@@ -77,6 +77,11 @@ async function runOpsStatus(command, io, tower) {
|
|
|
77
77
|
// status board's exit code — "is the org using memory" going unread is not a
|
|
78
78
|
// pipeline outage.
|
|
79
79
|
const memory = command.memory ? await readMemoryUsage(command, io, tower) : null;
|
|
80
|
+
// BLI-3912. A THIRD door, asked for only when `--models` was, and never able
|
|
81
|
+
// to fail the board: "what is each model costing and how fast is it" is a
|
|
82
|
+
// different question from "is the pipeline healthy", and a card shelf that
|
|
83
|
+
// could not be read is not a collection outage.
|
|
84
|
+
const models = command.models ? await readModelBoardLines(io, tower) : null;
|
|
80
85
|
// BLI-3723: `failing` joins the list. An older CLI that has never heard of it
|
|
81
86
|
// simply does not count it — which is why the server keeps the sentence in
|
|
82
87
|
// `detail`, so an un-upgraded terminal still PRINTS the outage even when it
|
|
@@ -88,7 +93,13 @@ async function runOpsStatus(command, io, tower) {
|
|
|
88
93
|
// BLI-3762: a job that ran and wrote less than it owed is not healthy.
|
|
89
94
|
row.verdict === "degraded");
|
|
90
95
|
if (command.json) {
|
|
91
|
-
writeLine(io.stdout, JSON.stringify(
|
|
96
|
+
writeLine(io.stdout, JSON.stringify({
|
|
97
|
+
...payload,
|
|
98
|
+
...(memory
|
|
99
|
+
? { memory: memory.section, hooks: memory.hooks, experience: memory.experience }
|
|
100
|
+
: {}),
|
|
101
|
+
...(models ? { models: models.board, modelsReason: models.reason } : {}),
|
|
102
|
+
}));
|
|
92
103
|
}
|
|
93
104
|
else {
|
|
94
105
|
const styled = colorEnabled(io);
|
|
@@ -116,6 +127,17 @@ async function runOpsStatus(command, io, tower) {
|
|
|
116
127
|
for (const line of memory.experience.lines)
|
|
117
128
|
writeLine(io.stdout, ` ${line}`);
|
|
118
129
|
}
|
|
130
|
+
if (models) {
|
|
131
|
+
writeLine(io.stdout, "");
|
|
132
|
+
if (models.lines.length > 0) {
|
|
133
|
+
for (const line of models.lines)
|
|
134
|
+
writeLine(io.stdout, line);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Never a silent gap where a section was asked for.
|
|
138
|
+
writeLine(io.stdout, `MODELS not read (${models.reason})`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
119
141
|
if (memory && !memory.section) {
|
|
120
142
|
// Never a silent gap where a section was asked for: the reason the gauge
|
|
121
143
|
// could not be read is printed where the gauge would have been.
|
|
@@ -160,6 +182,11 @@ async function runOpsStatus(command, io, tower) {
|
|
|
160
182
|
// names one — a rollout fact, not a count of zero.
|
|
161
183
|
memory_hook_via_daemon_24h: memory?.hooks?.via?.daemon ?? null,
|
|
162
184
|
memory_hook_via_direct_24h: memory?.hooks?.via?.direct ?? null,
|
|
185
|
+
// BLI-3912: whether the model board was asked for, and what it said.
|
|
186
|
+
models_asked: Boolean(command.models),
|
|
187
|
+
models_reason: models?.reason ?? null,
|
|
188
|
+
models_counted: models?.board?.rows?.length ?? null,
|
|
189
|
+
models_cost_today_usd: models?.board?.total_cost_today_usd ?? null,
|
|
163
190
|
})}`);
|
|
164
191
|
return unhealthy.length > 0 ? 1 : 0;
|
|
165
192
|
}
|
|
@@ -195,6 +222,29 @@ async function readMemoryUsage(command, io, tower) {
|
|
|
195
222
|
return { section: null, hooks: body.hooks ?? null, experience: body.experience ?? null, reason: "memory_section_absent" };
|
|
196
223
|
return { section: body.memory, hooks: body.hooks ?? null, experience: body.experience ?? null, reason: "ok" };
|
|
197
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* The per-model board, read through its own door (BLI-3912).
|
|
227
|
+
*
|
|
228
|
+
* Same posture as the adoption gauge next door: it never fails the status
|
|
229
|
+
* board, and when it cannot be read the reason is printed where the section
|
|
230
|
+
* would have been. The LINES come from the server, so a terminal and the
|
|
231
|
+
* dashboard cannot disagree about what a model cost today.
|
|
232
|
+
*/
|
|
233
|
+
async function readModelBoardLines(io, tower) {
|
|
234
|
+
const result = await callTower(tower, { path: "/api/ops/models", label: "ops-models" });
|
|
235
|
+
if (!result.ok) {
|
|
236
|
+
writeLine(io.stderr, `[ops cli] model board not read ${JSON.stringify({
|
|
237
|
+
reason: result.reason,
|
|
238
|
+
http_status: result.httpStatus ?? null,
|
|
239
|
+
})}`);
|
|
240
|
+
return { lines: [], board: null, reason: result.reason };
|
|
241
|
+
}
|
|
242
|
+
const body = asRecord(result.body);
|
|
243
|
+
if (!body.lines || body.lines.length === 0) {
|
|
244
|
+
return { lines: [], board: body.board ?? null, reason: "models_section_absent" };
|
|
245
|
+
}
|
|
246
|
+
return { lines: body.lines, board: body.board ?? null, reason: "ok" };
|
|
247
|
+
}
|
|
198
248
|
async function runOpsRecompile(command, io, tower) {
|
|
199
249
|
const person = command.person ?? "";
|
|
200
250
|
if (!command.json) {
|
|
@@ -15,7 +15,7 @@ export async function runCockpitCli(argv, io) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (command === "--version" || command === "-V" || command === "version") {
|
|
18
|
-
writeLine(io?.stdout ?? process.stdout, "0.2.
|
|
18
|
+
writeLine(io?.stdout ?? process.stdout, "0.2.90");
|
|
19
19
|
return 0;
|
|
20
20
|
}
|
|
21
21
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.90",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@bli-cockpit/memory-mcp": "0.1.20",
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.23",
|
|
32
32
|
"@bli-cockpit/telemetry-core": "0.1.37"
|
|
33
33
|
}
|
|
34
34
|
}
|