@wirecat/cli-core 0.409.0 → 0.410.0
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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ Notable changes to `@wirecat/cli-core` (`@wirecat/cli-messaging` up to 0.261.0,
|
|
|
5
5
|
minor version may break callers, and says how under "Changed — may break callers". `pnpm docs:check` checks the
|
|
6
6
|
shape of this file.
|
|
7
7
|
|
|
8
|
+
## 0.410.0 — 24.10.2026
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `buildDoctorReport`, `doctorReportFileName` and their report types are public through `/cli`.
|
|
13
|
+
Consumers can reuse run selection, identifier labelling and home-path redaction while retaining
|
|
14
|
+
typed diagnostic payloads and optional technical context. Existing reports keep their shape
|
|
15
|
+
when context is omitted. `eventFields: "recorded"` retains allowlisted recorded technical
|
|
16
|
+
fields for consumers that already include them; the default report projection is unchanged.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Meeting search continuation arguments match the generated MCP schema.** Stored numeric selector IDs are
|
|
21
|
+
serialized as strings in replay arguments, so an agent can pass the returned next step to the shared read tool
|
|
22
|
+
without converting IDs. Validated search requests still use numeric store IDs.
|
|
23
|
+
|
|
24
|
+
- Media archives skip call events alongside service events and keyboards. A message containing
|
|
25
|
+
both a file and a call event now archives the file and verifies its checksum; call-only
|
|
26
|
+
messages make no download request. Unknown attachment kinds still fail rather than being
|
|
27
|
+
silently dropped.
|
|
28
|
+
|
|
8
29
|
## 0.409.0 — 24.10.2026
|
|
9
30
|
|
|
10
31
|
### Added
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -112,6 +112,7 @@ export { type ModeProblem, type PrivateFiles, privateFiles, withSqliteSidecars }
|
|
|
112
112
|
export { reactionsCommand } from "./messenger/reactions-command.js";
|
|
113
113
|
export { callsCommand, stickersCommand } from "./messenger/records-command.js";
|
|
114
114
|
export { repliesCommand } from "./messenger/replies-command.js";
|
|
115
|
+
export { buildReport as buildDoctorReport, type Report as DoctorReport, type ReportContext as DoctorReportContext, reportFileName as doctorReportFileName, } from "./messenger/report.js";
|
|
115
116
|
export { reviewCommand } from "./messenger/review.js";
|
|
116
117
|
export { scheduleCommand } from "./messenger/schedule-command.js";
|
|
117
118
|
export { allAnswer, allHitItem, type HitItem, type MessageHitItem, messageHitItem, type NoteHitItem, noteHitItem, } from "./messenger/search-answers.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -106,6 +106,7 @@ export { privateFiles, withSqliteSidecars } from "./messenger/private-files.js";
|
|
|
106
106
|
export { reactionsCommand } from "./messenger/reactions-command.js";
|
|
107
107
|
export { callsCommand, stickersCommand } from "./messenger/records-command.js";
|
|
108
108
|
export { repliesCommand } from "./messenger/replies-command.js";
|
|
109
|
+
export { buildReport as buildDoctorReport, reportFileName as doctorReportFileName, } from "./messenger/report.js";
|
|
109
110
|
export { reviewCommand } from "./messenger/review.js";
|
|
110
111
|
export { scheduleCommand } from "./messenger/schedule-command.js";
|
|
111
112
|
export { allAnswer, allHitItem, messageHitItem, noteHitItem, } from "./messenger/search-answers.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type RunMetadata } from "../../runs/run.js";
|
|
2
2
|
import type { SendEntry } from "../../sends/journal.js";
|
|
3
|
-
export interface Report {
|
|
3
|
+
export interface Report<Doctor extends object = Record<string, unknown>> {
|
|
4
4
|
createdAt: string;
|
|
5
5
|
/** What `doctor --json` answers — versions, paths, counts and the messenger's own checks. */
|
|
6
|
-
doctor:
|
|
6
|
+
doctor: Doctor;
|
|
7
7
|
/** The run the report is about: the one asked for, or the newest that failed. */
|
|
8
8
|
run: {
|
|
9
9
|
metadata: RunMetadata;
|
|
@@ -12,13 +12,25 @@ export interface Report {
|
|
|
12
12
|
/** The newest write attempts — outcomes and ids, never text (`src/sends/journal.ts`). */
|
|
13
13
|
sends: SendEntry[];
|
|
14
14
|
}
|
|
15
|
+
/** Optional technical context supplied by a consumer; it is redacted with the rest of the report. */
|
|
16
|
+
export interface ReportContext {
|
|
17
|
+
createdAt?: never;
|
|
18
|
+
doctor?: never;
|
|
19
|
+
run?: never;
|
|
20
|
+
sends?: never;
|
|
21
|
+
version?: string;
|
|
22
|
+
runtime?: string;
|
|
23
|
+
platform?: string;
|
|
24
|
+
arch?: string;
|
|
25
|
+
profile?: string | null;
|
|
26
|
+
}
|
|
15
27
|
/**
|
|
16
28
|
* **Everything a report holds is already free of content** — the doctor, the run log and the send
|
|
17
29
|
* journal are built that way. Two things are added: every id becomes a label, and the home
|
|
18
|
-
* directory is hidden, since paths name the person's account on this machine.
|
|
30
|
+
* directory is hidden, since paths name the person's account on this machine.
|
|
19
31
|
*/
|
|
20
|
-
export declare const buildReport: ({ doctor, runsDir, runId, command, sends, now, home, }: {
|
|
21
|
-
doctor:
|
|
32
|
+
export declare const buildReport: <Doctor extends object, Context extends ReportContext = Record<never, never>>({ doctor, runsDir, runId, command, sends, now, home, context, eventFields, }: {
|
|
33
|
+
doctor: Doctor;
|
|
22
34
|
runsDir: string;
|
|
23
35
|
runId?: string;
|
|
24
36
|
/** What to type to list runs, for the error when `runId` names none. */
|
|
@@ -26,7 +38,10 @@ export declare const buildReport: ({ doctor, runsDir, runId, command, sends, now
|
|
|
26
38
|
sends: SendEntry[];
|
|
27
39
|
now?: Date;
|
|
28
40
|
home: string | undefined;
|
|
29
|
-
|
|
41
|
+
context?: Context;
|
|
42
|
+
/** Retain allowlisted recorded fields, including time and severity, for an existing consumer contract. */
|
|
43
|
+
eventFields?: "report" | "recorded";
|
|
44
|
+
}) => Report<Doctor> & Omit<Context, keyof Report>;
|
|
30
45
|
/**
|
|
31
46
|
* Every field of a journal line that names a chat, a message or a write. A send's `operationId` is
|
|
32
47
|
* its `sendId`, so one left raw would undo the other's label.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { CliError } from "@wirecat/cli-base";
|
|
4
|
+
import { LOG_ATTRIBUTES } from "../../runs/log-attributes.js";
|
|
4
5
|
import { findRun, listRuns, readEvents } from "../../runs/run.js";
|
|
5
6
|
const RECENT_SENDS = 20;
|
|
6
7
|
/** Only these fields of a run event travel; a field added later is left out until it is named here. */
|
|
@@ -18,21 +19,22 @@ const EVENT_FIELDS = [
|
|
|
18
19
|
/**
|
|
19
20
|
* **Everything a report holds is already free of content** — the doctor, the run log and the send
|
|
20
21
|
* journal are built that way. Two things are added: every id becomes a label, and the home
|
|
21
|
-
* directory is hidden, since paths name the person's account on this machine.
|
|
22
|
+
* directory is hidden, since paths name the person's account on this machine.
|
|
22
23
|
*/
|
|
23
|
-
export const buildReport = ({ doctor, runsDir, runId, command, sends, now = new Date(), home, }) => {
|
|
24
|
+
export const buildReport = ({ doctor, runsDir, runId, command, sends, now = new Date(), home, context, eventFields = "report", }) => {
|
|
24
25
|
const chosen = runId === undefined ? newestFailed(runsDir) : findRun(runsDir, runId);
|
|
25
26
|
if (runId !== undefined && !chosen)
|
|
26
27
|
throw new CliError("not_found", `no run ${runId} — \`${command} runs list\` names them`);
|
|
27
28
|
const label = labeller();
|
|
28
29
|
const account = doctor.account;
|
|
29
30
|
const report = {
|
|
31
|
+
...context,
|
|
30
32
|
createdAt: now.toISOString(),
|
|
31
33
|
doctor: {
|
|
32
34
|
...doctor,
|
|
33
35
|
...(account?.remembered ? { account: { ...account, remembered: label(account.remembered) } } : {}),
|
|
34
36
|
},
|
|
35
|
-
run: chosen ? { metadata: chosen.metadata, events: readEvents(chosen.dir).map(eventOf(label)) } : null,
|
|
37
|
+
run: chosen ? { metadata: chosen.metadata, events: readEvents(chosen.dir).map(eventOf(label, eventFields)) } : null,
|
|
36
38
|
sends: sends.slice(-RECENT_SENDS).map((entry) => labelled(entry, label)),
|
|
37
39
|
};
|
|
38
40
|
return JSON.parse(hideHome(JSON.stringify(report), home));
|
|
@@ -85,8 +87,21 @@ const labeller = () => {
|
|
|
85
87
|
const salt = randomBytes(16);
|
|
86
88
|
return (id) => `id:${createHash("sha256").update(salt).update(id).digest("hex").slice(0, 12)}`;
|
|
87
89
|
};
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
+
const RECORDED_FIELDS = new Set([...LOG_ATTRIBUTES, "time", "severity"]);
|
|
91
|
+
const RECORDED_ID_FIELDS = new Set([
|
|
92
|
+
...SEND_ID_FIELDS.map((key) => key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)),
|
|
93
|
+
"actor_id",
|
|
94
|
+
"target_id",
|
|
95
|
+
]);
|
|
96
|
+
const eventOf = (label, fields) => (event) => {
|
|
97
|
+
const kept = Object.fromEntries(Object.entries(event)
|
|
98
|
+
.filter(([key]) => (fields === "recorded" ? RECORDED_FIELDS.has(key) : EVENT_FIELDS.includes(key)))
|
|
99
|
+
.map(([key, value]) => [
|
|
100
|
+
key,
|
|
101
|
+
fields === "recorded" && RECORDED_ID_FIELDS.has(key) && value !== undefined && value !== null
|
|
102
|
+
? label(String(value))
|
|
103
|
+
: value,
|
|
104
|
+
]));
|
|
90
105
|
const ids = kept.ids;
|
|
91
106
|
if (typeof ids !== "object" || ids === null)
|
|
92
107
|
return kept;
|
|
@@ -81,11 +81,11 @@ export const meetingHitSearchRequest = (query, options, signal) => {
|
|
|
81
81
|
/** Parsed dates must become ISO values again so the CLI can replay its own continuation. */
|
|
82
82
|
export const meetingSearchArguments = (query, options) => Object.fromEntries(Object.entries({
|
|
83
83
|
query,
|
|
84
|
-
meeting_id: options.meetingId,
|
|
85
|
-
identity_id: options.identityId,
|
|
86
|
-
person_id: options.personId,
|
|
87
|
-
event_id: options.eventId,
|
|
88
|
-
series_id: options.seriesId,
|
|
84
|
+
meeting_id: options.meetingId?.toString(),
|
|
85
|
+
identity_id: options.identityId?.toString(),
|
|
86
|
+
person_id: options.personId?.toString(),
|
|
87
|
+
event_id: options.eventId?.toString(),
|
|
88
|
+
series_id: options.seriesId?.toString(),
|
|
89
89
|
project: options.project,
|
|
90
90
|
tag: options.tag,
|
|
91
91
|
topic: options.topic,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.410.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
"@types/node": "^22.20.4",
|
|
210
210
|
"@types/pngjs": "6.0.5",
|
|
211
211
|
"@vitest/coverage-v8": "^5.0.2",
|
|
212
|
-
"@wirecat/cli-base": "0.50.
|
|
212
|
+
"@wirecat/cli-base": "0.50.4",
|
|
213
213
|
"commander": "15.0.0",
|
|
214
214
|
"cspell": "10.3.6",
|
|
215
215
|
"drizzle-kit": "1.0.0-rc.4",
|