@bli-cockpit/cli 0.2.82 → 0.2.85
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/doctor-memory-daemon.js +36 -0
- package/dist/commands/doctor.js +10 -0
- package/dist/commands/heartbeat.js +5 -0
- package/dist/commands/local-args-tower-search.js +17 -4
- package/dist/commands/local-command-shapes-search.js +18 -0
- package/dist/commands/memory-daemon-probe.js +118 -0
- package/dist/commands/memory-hook-counts.js +7 -0
- package/dist/commands/memory-install-report.js +5 -0
- package/dist/commands/memory-install.js +5 -0
- package/dist/commands/ops-render-memory.js +4 -0
- package/dist/commands/ops.js +7 -0
- package/dist/commands/public-root.js +1 -1
- package/dist/commands/search.js +8 -1
- package/package.json +4 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `memory-daemon` row (BLI-3884): is the resident local answerer running?
|
|
3
|
+
*
|
|
4
|
+
* **This row is NEVER red, and it has no fix.** A machine with no daemon is a
|
|
5
|
+
* machine whose recalls take the path they took before the daemon existed —
|
|
6
|
+
* slower by a process start, a TLS handshake and a token check, and completely
|
|
7
|
+
* correct. Colouring that red would train people to ignore doctor, the way an
|
|
8
|
+
* amber "you could be faster" line trains them to ignore everything under it.
|
|
9
|
+
*
|
|
10
|
+
* So there are exactly two outcomes:
|
|
11
|
+
*
|
|
12
|
+
* ok something answered a `ping` on this user's endpoint.
|
|
13
|
+
* skipped nothing did, and the reason is named — `daemon_absent` (the
|
|
14
|
+
* ordinary state on a machine that has not prompted in two hours),
|
|
15
|
+
* `socket_path_too_long`, or whatever the socket said.
|
|
16
|
+
*
|
|
17
|
+
* Starting one is not doctor's job either: the PROMPT HOOK is the daemon's
|
|
18
|
+
* supervisor and spawns one at most once an hour (`memory-mcp`'s
|
|
19
|
+
* `daemon/spawn.ts`). A doctor that started a daemon would be a second
|
|
20
|
+
* supervisor with different rules.
|
|
21
|
+
*/
|
|
22
|
+
import { ok, skipped } from "./doctor-report.js";
|
|
23
|
+
import { memoryDaemonLine, probeMemoryDaemon } from "./memory-daemon-probe.js";
|
|
24
|
+
export async function checkMemoryDaemonState(context, deps = {}) {
|
|
25
|
+
const probe = deps.probe ?? probeMemoryDaemon;
|
|
26
|
+
const reading = await probe({
|
|
27
|
+
...(context.command.homeDir ? { homeDir: context.command.homeDir } : {}),
|
|
28
|
+
});
|
|
29
|
+
return memoryDaemonRow(reading);
|
|
30
|
+
}
|
|
31
|
+
export function memoryDaemonRow(reading) {
|
|
32
|
+
const sentence = memoryDaemonLine(reading);
|
|
33
|
+
return reading.answering
|
|
34
|
+
? ok("memory-daemon", "answering", sentence)
|
|
35
|
+
: skipped("memory-daemon", reading.reason, sentence);
|
|
36
|
+
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { checkSingleInstallState, fixAuthState, fixRootState, readAuthState, readRootState, } from "./doctor-access.js";
|
|
2
2
|
import { backfillCompletionStepState, checkBackfillState, checkDiskState, checkGcState, checkSyncState, fixBackfillState, fixDiskState, fixGcState, fixSyncState, syncBacklogDrainingVerdict, } from "./doctor-pipeline.js";
|
|
3
3
|
import { checkMcpAnswersState } from "./doctor-mcp.js";
|
|
4
|
+
import { checkMemoryDaemonState } from "./doctor-memory-daemon.js";
|
|
4
5
|
import { checkAutostartState, checkMemoryState, fixAutostartState, fixMemoryState, } from "./doctor-registration.js";
|
|
5
6
|
import { dryRunPreview, isInteractiveDoctorFix, maybeReportDoctorEvents, writeDoctorOutput, } from "./doctor-report.js";
|
|
6
7
|
import { refreshSetupReceipt } from "./setup-receipt.js";
|
|
@@ -104,6 +105,14 @@ function doctorInvariants() {
|
|
|
104
105
|
id: "mcp-answers",
|
|
105
106
|
check: (context) => context.deps.checkMcpAnswers(context),
|
|
106
107
|
},
|
|
108
|
+
// BLI-3884. Beside the two rows above because it is the same family — what
|
|
109
|
+
// this machine's agents can reach — and, like `mcp-answers`, it has NO fix:
|
|
110
|
+
// the prompt hook is the daemon's supervisor and starts one itself. It is
|
|
111
|
+
// never red; a machine without one recalls exactly as it did before.
|
|
112
|
+
{
|
|
113
|
+
id: "memory-daemon",
|
|
114
|
+
check: (context) => context.deps.checkMemoryDaemon(context),
|
|
115
|
+
},
|
|
107
116
|
{
|
|
108
117
|
id: "backfill-complete",
|
|
109
118
|
check: (context) => context.deps.checkBackfill(context),
|
|
@@ -146,6 +155,7 @@ function defaultDoctorDeps(hooks) {
|
|
|
146
155
|
checkMemory: checkMemoryState,
|
|
147
156
|
fixMemory: fixMemoryState,
|
|
148
157
|
checkMcpAnswers: (context) => checkMcpAnswersState(context),
|
|
158
|
+
checkMemoryDaemon: (context) => checkMemoryDaemonState(context),
|
|
149
159
|
checkBackfill: checkBackfillState,
|
|
150
160
|
fixBackfill: fixBackfillState,
|
|
151
161
|
checkGc: checkGcState,
|
|
@@ -317,6 +317,11 @@ export async function sendCollectorHeartbeatBestEffort(options) {
|
|
|
317
317
|
memory_hook_runs_24h: memory.receipt?.hook_runs_24h ?? null,
|
|
318
318
|
memory_hook_timeouts_24h: memory.receipt?.hook_timeouts_24h ?? null,
|
|
319
319
|
memory_hook_stats: memory.receipt?.hook_stats_reason ?? null,
|
|
320
|
+
// BLI-3884. Which route answered those recalls. `null` on a machine
|
|
321
|
+
// whose memory-mcp does not name one yet — never zero, which would
|
|
322
|
+
// read as "the daemon answered none".
|
|
323
|
+
memory_hook_via_daemon_24h: memory.receipt?.hook_via_daemon_24h ?? null,
|
|
324
|
+
memory_hook_via_direct_24h: memory.receipt?.hook_via_direct_24h ?? null,
|
|
320
325
|
}));
|
|
321
326
|
return true;
|
|
322
327
|
}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Its own sibling of `local-args-tower.ts` rather than folded into
|
|
5
5
|
* `local-args-tower-pages.ts` or `-docs-msg.ts`: search is not one surface's
|
|
6
6
|
* verb, it is the door OVER all of them — documents, messages, issues, meeting
|
|
7
|
-
* notes
|
|
8
|
-
*
|
|
7
|
+
* notes, the caller's own mail and calendar (BLI-3880), and memory — and
|
|
8
|
+
* putting it under any one family's doc comment would say something untrue
|
|
9
|
+
* about what it reads.
|
|
9
10
|
*
|
|
10
11
|
* The query is a POSITIONAL, not a flag, because that is how every search
|
|
11
12
|
* command a person has ever typed works (`grep`, `rg`, `gh search`). Several
|
|
@@ -13,8 +14,20 @@
|
|
|
13
14
|
* behaves the way it looks, without demanding quotes.
|
|
14
15
|
*/
|
|
15
16
|
import { optionalNonEmpty, optionalPositiveInteger, optionalUrl, parseNamedArgs, } from "./local-arg-values.js";
|
|
16
|
-
/**
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Every corpus. Kept here so an unknown kind is refused BEFORE a round trip,
|
|
19
|
+
* and mirrors `SEARCH_KINDS` in the dashboard — nothing in the type system ties
|
|
20
|
+
* the two lists together.
|
|
21
|
+
*/
|
|
22
|
+
export const SEARCH_KINDS = [
|
|
23
|
+
"doc",
|
|
24
|
+
"msg",
|
|
25
|
+
"issue",
|
|
26
|
+
"note",
|
|
27
|
+
"mail",
|
|
28
|
+
"cal",
|
|
29
|
+
"memory",
|
|
30
|
+
];
|
|
18
31
|
export function parseSearchArgs(args) {
|
|
19
32
|
const values = parseNamedArgs(args, {
|
|
20
33
|
allowedFlags: ["--home", "--dashboard-url", "--kind", "--limit", "--json"],
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of `cockpit search` (BLI-3728; split out of
|
|
3
|
+
* `local-command-shapes.ts` by BLI-3880).
|
|
4
|
+
*
|
|
5
|
+
* Its own module for the reason the verb itself has one
|
|
6
|
+
* (`local-args-tower-search.ts`): search is not one surface's verb, it is the
|
|
7
|
+
* door OVER all of them, so it sits beside the surface families rather than
|
|
8
|
+
* inside any of them. Two more corpora also pushed the shape file past this
|
|
9
|
+
* repo's readability ceiling, and a member with a paragraph of its own is
|
|
10
|
+
* exactly the member to lift out first.
|
|
11
|
+
*
|
|
12
|
+
* `local-command-shapes.ts` imports this type and keeps `SearchCommandShape`
|
|
13
|
+
* in the `LocalCommand` union, so nothing that reads a parsed command changed.
|
|
14
|
+
* A new module here must also join `scripts/build-public-cli.mjs`
|
|
15
|
+
* `runtimeFiles` — but this one is types only, erased at build, and the CLI's
|
|
16
|
+
* own runtime-file assert is what proves it either way.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IS THE RESIDENT MEMORY DAEMON ANSWERING? (BLI-3884)
|
|
3
|
+
*
|
|
4
|
+
* `bli-memory-mcp daemon` holds a warm connection, credential and scope so a
|
|
5
|
+
* prompt hook stops paying for a process, a TLS handshake and the device-token
|
|
6
|
+
* gate on every turn. Two collector surfaces need to know whether it is there:
|
|
7
|
+
* `cockpit doctor`'s `memory-daemon` row and `cockpit memory status`.
|
|
8
|
+
*
|
|
9
|
+
* The probe is written HERE, in twenty lines of `node:net`, rather than by
|
|
10
|
+
* importing the memory-mcp client: that package is a devDependency of this one
|
|
11
|
+
* and is not copied into the packed CLI, so an import would be green in the
|
|
12
|
+
* repo and missing on every fleet machine (the `runtimeFiles` trap this repo
|
|
13
|
+
* has paid for more than once). What IS shared is the CONTRACT — the address,
|
|
14
|
+
* the protocol version and the line limit come from
|
|
15
|
+
* `@bli-cockpit/telemetry-core`'s `memory-daemon-endpoint.ts`, which both
|
|
16
|
+
* packages depend on.
|
|
17
|
+
*
|
|
18
|
+
* **It never fixes anything and never fails anything.** A machine with no
|
|
19
|
+
* daemon is a machine whose hooks work exactly as they did before the daemon
|
|
20
|
+
* existed; this reports presence, absence and the reason, and that is all.
|
|
21
|
+
*/
|
|
22
|
+
import net from "node:net";
|
|
23
|
+
import os from "node:os";
|
|
24
|
+
import { MEMORY_DAEMON_MAX_LINE_BYTES, MEMORY_DAEMON_PROTOCOL_VERSION, memoryDaemonEndpoint, } from "@bli-cockpit/telemetry-core";
|
|
25
|
+
/** A probe waits this long in total. A live local socket answers in ~1 ms. */
|
|
26
|
+
export const DAEMON_PROBE_TIMEOUT_MS = 400;
|
|
27
|
+
export function probeMemoryDaemon(options = {}) {
|
|
28
|
+
const now = options.now ?? Date.now;
|
|
29
|
+
const started = now();
|
|
30
|
+
const endpoint = memoryDaemonEndpoint({
|
|
31
|
+
homeDir: options.homeDir ?? os.homedir(),
|
|
32
|
+
...(options.platform ? { platform: options.platform } : {}),
|
|
33
|
+
username: options.username ?? safeUsername(),
|
|
34
|
+
});
|
|
35
|
+
const base = { kind: endpoint.kind, pid: null, startedAt: null };
|
|
36
|
+
if (endpoint.kind === "unix" && endpoint.addressTooLong) {
|
|
37
|
+
return Promise.resolve({
|
|
38
|
+
...base,
|
|
39
|
+
answering: false,
|
|
40
|
+
reason: "socket_path_too_long",
|
|
41
|
+
elapsedMs: 0,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
let settled = false;
|
|
46
|
+
let buffered = "";
|
|
47
|
+
const finish = (answering, reason, extra = {}) => {
|
|
48
|
+
if (settled)
|
|
49
|
+
return;
|
|
50
|
+
settled = true;
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
socket.destroy();
|
|
53
|
+
resolve({ ...base, ...extra, answering, reason, elapsedMs: now() - started });
|
|
54
|
+
};
|
|
55
|
+
const timer = setTimeout(() => finish(false, "probe_timeout"), options.timeoutMs ?? DAEMON_PROBE_TIMEOUT_MS);
|
|
56
|
+
timer.unref?.();
|
|
57
|
+
const socket = (options.connectImpl ?? ((address) => net.createConnection({ path: address })))(endpoint.address);
|
|
58
|
+
socket.on("error", (error) => {
|
|
59
|
+
const code = error.code;
|
|
60
|
+
finish(false, code === "ENOENT" || code === "ECONNREFUSED"
|
|
61
|
+
? "daemon_absent"
|
|
62
|
+
: `daemon_socket_${(code ?? "error").toLowerCase()}`);
|
|
63
|
+
});
|
|
64
|
+
socket.on("connect", () => {
|
|
65
|
+
socket.write(`${JSON.stringify({ v: MEMORY_DAEMON_PROTOCOL_VERSION, event: "ping" })}\n`);
|
|
66
|
+
});
|
|
67
|
+
socket.on("data", (chunk) => {
|
|
68
|
+
buffered += chunk.toString("utf8");
|
|
69
|
+
if (buffered.length > MEMORY_DAEMON_MAX_LINE_BYTES) {
|
|
70
|
+
finish(false, "daemon_answer_too_large");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const newline = buffered.indexOf("\n");
|
|
74
|
+
if (newline === -1)
|
|
75
|
+
return;
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(buffered.slice(0, newline));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
finish(false, "daemon_answer_unparseable");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const record = (parsed ?? {});
|
|
85
|
+
if (record["ok"] !== true) {
|
|
86
|
+
finish(false, typeof record["reason"] === "string" ? record["reason"] : "daemon_refused");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
finish(true, "answering", {
|
|
90
|
+
pid: typeof record["pid"] === "number" ? record["pid"] : null,
|
|
91
|
+
startedAt: typeof record["startedAt"] === "string" ? record["startedAt"] : null,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
socket.on("close", () => finish(false, "daemon_closed_without_answer"));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** One sentence a person reads, on either branch. Never a path. */
|
|
98
|
+
export function memoryDaemonLine(reading) {
|
|
99
|
+
if (reading.answering) {
|
|
100
|
+
return (`the memory daemon is answering on this machine's ${reading.kind === "pipe" ? "named pipe" : "local socket"}` +
|
|
101
|
+
`${reading.pid === null ? "" : ` (pid ${reading.pid})`}` +
|
|
102
|
+
` — prompt recalls skip a process start, a fresh connection and the token check`);
|
|
103
|
+
}
|
|
104
|
+
if (reading.reason === "socket_path_too_long") {
|
|
105
|
+
return ("the memory daemon cannot run here: this machine's home directory is deep enough that its socket path" +
|
|
106
|
+
" exceeds the operating system's limit. Recall still works — every prompt takes the direct path.");
|
|
107
|
+
}
|
|
108
|
+
return (`no memory daemon is answering (${reading.reason}); every prompt recall takes the direct path,` +
|
|
109
|
+
" which is how it worked before the daemon existed. The next prompt hook starts one.");
|
|
110
|
+
}
|
|
111
|
+
function safeUsername() {
|
|
112
|
+
try {
|
|
113
|
+
return os.userInfo().username;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -47,6 +47,13 @@ export function readMemoryHookCounts(options) {
|
|
|
47
47
|
hook_timeouts_24h: window.timeouts,
|
|
48
48
|
hook_printed_24h: window.printed,
|
|
49
49
|
hook_failed_24h: window.failed,
|
|
50
|
+
...(window.viaMeasured
|
|
51
|
+
? {
|
|
52
|
+
hook_via_daemon_24h: window.viaDaemon,
|
|
53
|
+
hook_via_direct_24h: window.viaDirect,
|
|
54
|
+
}
|
|
55
|
+
: {}),
|
|
56
|
+
hook_skipped_trivial_24h: window.skippedTrivial,
|
|
50
57
|
},
|
|
51
58
|
reason: "ok",
|
|
52
59
|
};
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Sibling of `memory-install.ts`, named in its header.
|
|
5
5
|
*/
|
|
6
6
|
import { memoryInstallGaps } from "@bli-cockpit/telemetry-core";
|
|
7
|
+
import { memoryDaemonLine } from "./memory-daemon-probe.js";
|
|
7
8
|
import { memoryReceiptLine } from "./memory-install-receipt.js";
|
|
8
9
|
import { DEFAULT_DASHBOARD_URL, getCollectorRuntimePaths, readLocalCollectorConfig, } from "../local-state.js";
|
|
9
10
|
export async function resolveDashboardUrl(command, deps) {
|
|
@@ -94,6 +95,10 @@ export function memoryOutcomeLines(outcome) {
|
|
|
94
95
|
? "BLI Memory was not registered and nothing was written: the bli-memory-mcp server is not on this machine yet."
|
|
95
96
|
: `BLI Memory is not fully registered: ${outcome.reason}.`;
|
|
96
97
|
const lines = [headline, ` ${memoryReceiptLine(outcome.receipt)}`];
|
|
98
|
+
// BLI-3884. Only `status` asks; an absent reading is not printed as "no
|
|
99
|
+
// daemon", because nothing looked.
|
|
100
|
+
if (outcome.daemon)
|
|
101
|
+
lines.push(` ${memoryDaemonLine(outcome.daemon)}`);
|
|
97
102
|
// The trust step is the one thing a person has to do by hand, so it is said
|
|
98
103
|
// here in words rather than left as a status word they have to look up.
|
|
99
104
|
if (outcome.receipt.codex.hooks === "needs_trust") {
|
|
@@ -48,6 +48,7 @@ import { defaultMemoryFileIo, } from "./memory-install-files.js";
|
|
|
48
48
|
import { installClaudeMemoryIntegration, inspectClaudeMemoryIntegration, } from "./memory-install-claude.js";
|
|
49
49
|
import { installCodexMemoryIntegration, inspectCodexMemoryIntegration, } from "./memory-install-codex.js";
|
|
50
50
|
import { resolveMemoryConfig } from "./memory-install-config.js";
|
|
51
|
+
import { probeMemoryDaemon } from "./memory-daemon-probe.js";
|
|
51
52
|
import { expectedCodexTrustRows, readCodexHookTrustFromDisk, } from "./memory-install-codex-hooks.js";
|
|
52
53
|
import { buildMemoryInstallReceipt, writeMemoryReceiptFile, } from "./memory-install-receipt.js";
|
|
53
54
|
import { getCollectorRuntimePaths } from "../local-state.js";
|
|
@@ -208,6 +209,10 @@ export async function inspectMemoryIntegration(command, io, deps = {}) {
|
|
|
208
209
|
})));
|
|
209
210
|
const outcome = {
|
|
210
211
|
action: "status",
|
|
212
|
+
// The one live question on this surface: is the daemon answering? It is
|
|
213
|
+
// bounded, it opens nothing but a local socket, and a machine without one
|
|
214
|
+
// is reported as exactly that rather than as a problem.
|
|
215
|
+
daemon: await probeMemoryDaemon({ ...(homeDir ? { homeDir } : {}) }),
|
|
211
216
|
...aggregate(targets),
|
|
212
217
|
config_source: resolved.source,
|
|
213
218
|
bin_found: resolved.config !== null,
|
|
@@ -103,6 +103,10 @@ export function renderMemoryHooks(hooks, dim) {
|
|
|
103
103
|
lines.push((device.timeouts ?? 0) > 0 ? line : dim(line));
|
|
104
104
|
if (device.performanceLine)
|
|
105
105
|
lines.push(` ${device.performanceLine}`);
|
|
106
|
+
// BLI-3884. Written on the server (`lib/ops/memory-hook-misses.ts`) and
|
|
107
|
+
// printed verbatim, like every other sentence in this section.
|
|
108
|
+
if (device.viaLine)
|
|
109
|
+
lines.push(dim(` ${device.viaLine}`));
|
|
106
110
|
}
|
|
107
111
|
return lines;
|
|
108
112
|
}
|
package/dist/commands/ops.js
CHANGED
|
@@ -144,7 +144,14 @@ async function runOpsStatus(command, io, tower) {
|
|
|
144
144
|
// misses.
|
|
145
145
|
memory_hook_runs_24h: memory?.hooks?.totals?.runs ?? null,
|
|
146
146
|
memory_hook_timeouts_24h: memory?.hooks?.totals?.timeouts ?? null,
|
|
147
|
+
// BLI-3881: runs the hook declined to search for. A SUBSET of runs, so
|
|
148
|
+
// `runs - skipped_trivial` is how many turns actually asked the door.
|
|
149
|
+
memory_hook_skipped_trivial_24h: memory?.hooks?.totals?.skippedTrivial ?? null,
|
|
147
150
|
memory_hook_devices_reporting: memory?.hooks?.reporting ?? null,
|
|
151
|
+
// BLI-3884: which route answered them, fleet-wide. Null while no machine
|
|
152
|
+
// names one — a rollout fact, not a count of zero.
|
|
153
|
+
memory_hook_via_daemon_24h: memory?.hooks?.via?.daemon ?? null,
|
|
154
|
+
memory_hook_via_direct_24h: memory?.hooks?.via?.direct ?? null,
|
|
148
155
|
})}`);
|
|
149
156
|
return unhealthy.length > 0 ? 1 : 0;
|
|
150
157
|
}
|
|
@@ -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.85");
|
|
19
19
|
return 0;
|
|
20
20
|
}
|
|
21
21
|
|
package/dist/commands/search.js
CHANGED
|
@@ -29,14 +29,21 @@ import { writeLine } from "./cli-io.js";
|
|
|
29
29
|
const TAG = "[search cli]";
|
|
30
30
|
const READ_DEADLINE_MS = 30_000;
|
|
31
31
|
/** Group headings, in the order a person reads them. Mirrors the overlay. */
|
|
32
|
+
// Mirrors `SEARCH_KIND_LABELS` / `SEARCH_KINDS` in the dashboard
|
|
33
|
+
// (BLI-3880 added mail and cal). A kind the door returns that is missing here
|
|
34
|
+
// still prints — upper-cased — rather than vanishing, which is why the map is
|
|
35
|
+
// keyed loosely: an older CLI against a newer door must show the group, not
|
|
36
|
+
// swallow it.
|
|
32
37
|
const KIND_LABELS = {
|
|
33
38
|
doc: "DOCUMENTS",
|
|
34
39
|
msg: "MESSAGES",
|
|
35
40
|
issue: "ISSUES",
|
|
36
41
|
note: "MEETING NOTES",
|
|
42
|
+
mail: "MAIL",
|
|
43
|
+
cal: "CALENDAR",
|
|
37
44
|
memory: "MEMORY",
|
|
38
45
|
};
|
|
39
|
-
const KIND_ORDER = ["doc", "msg", "issue", "note", "memory"];
|
|
46
|
+
const KIND_ORDER = ["doc", "msg", "issue", "note", "mail", "cal", "memory"];
|
|
40
47
|
export async function runSearch(command, io) {
|
|
41
48
|
const door = await openAgentDoor("search", command, io);
|
|
42
49
|
const params = new URLSearchParams({ q: command.query });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.85",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"test": "node dist/cli.js --help && node ../../scripts/assert-public-cli-routing.mjs && node ../../scripts/assert-public-cli-verb-help.mjs && node ../../scripts/assert-public-cli-runtime-files.mjs && node ../../scripts/assert-public-cli-no-fleet-posts.mjs && node ../../scripts/assert-public-package-pack.mjs --workspace=@bli-cockpit/cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@bli-cockpit/memory-mcp": "0.1.
|
|
31
|
-
"@bli-cockpit/mcp": "0.1.
|
|
32
|
-
"@bli-cockpit/telemetry-core": "0.1.
|
|
30
|
+
"@bli-cockpit/memory-mcp": "0.1.17",
|
|
31
|
+
"@bli-cockpit/mcp": "0.1.19",
|
|
32
|
+
"@bli-cockpit/telemetry-core": "0.1.35"
|
|
33
33
|
}
|
|
34
34
|
}
|