@wirecat/cli-core 0.585.0 → 0.587.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 +26 -1
- package/dist/calendar/cli/local-times.js +3 -13
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/local-times.d.ts +16 -0
- package/dist/cli/local-times.js +65 -0
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/runs/command.js +3 -2
- package/dist/mail/cli/index.js +12 -11
- package/dist/mail/cli/review.js +10 -7
- package/dist/mail/read.d.ts +1 -1
- package/dist/mail/read.js +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,32 @@ 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
|
-
##
|
|
8
|
+
## 0.587.0 — 06.11.2026
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`answerLocally` and `wallClock` in `@wirecat/cli-core/cli`.** `answerLocally` answers as `renderAnswer` does, and a
|
|
13
|
+
person reads each UTC time as `YYYY-MM-DD HH:MM` in the zone in use, with one `times in <zone>` line on stderr; a
|
|
14
|
+
`view` gets the formatter to build its text with. JSON, JSONL, `--compact` and agents keep UTC. `hitText` takes the
|
|
15
|
+
formatter as an optional third argument. mail can drop its own copy (`src/commands/times.ts`).
|
|
16
|
+
- **cli-core accepts `@wirecat/cli-base` 0.80 (peer range `>=0.76.0 <0.81.0`) and builds with 0.80.0**, which
|
|
17
|
+
adds the `--write <id>` flag mail's `messages restore` takes. The range was `<0.80.0`.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`search messages`, `messages show`, `conversations list|show`, mail `review`, `runs list` and `runs show` show a
|
|
22
|
+
person times in the local zone** (WC-TIME-4), with a last line `times in <zone>`. They printed UTC ISO strings
|
|
23
|
+
such as `2026-10-03T09:00:00.000Z`. `search messages --timezone` decides the zone shown. JSON and storage keep UTC.
|
|
24
|
+
`runs list|show` is shared by every tool on cli-core; the mail screens reach mail and memo at their next bump.
|
|
25
|
+
|
|
26
|
+
## 0.586.0 — 06.11.2026
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **cli-core accepts `@wirecat/cli-base` 0.78 and 0.79 (peer range `>=0.76.0 <0.80.0`) and builds with 0.79.0**,
|
|
31
|
+
whose vocabulary has the finance resources `records`, `invoices`, `orders`, `payments` and the verb `allocate`
|
|
32
|
+
(0.78.0), and the CRM resources `deals`, `leads`, `pipelines`, `stages`, `products` and `users` (0.79.0), for the
|
|
33
|
+
Bitrix24 CLI `b24`. The lower bound stays, so nothing changes for a CLI on cli-base 0.76 or 0.77.
|
|
9
34
|
|
|
10
35
|
## 0.585.0 — 06.11.2026
|
|
11
36
|
|
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
import { renderPretty } from "@wirecat/cli-base";
|
|
2
|
-
|
|
2
|
+
import { UTC_TIME, wallClock } from "../../cli/local-times.js";
|
|
3
3
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
4
|
/**
|
|
5
5
|
* A calendar answer as a person reads it at a terminal (WC-TIME-4): each UTC time on the wall clock of `zone`, an
|
|
6
6
|
* all-day event's times as its local dates, and a last line naming the zone. JSON keeps the UTC times.
|
|
7
7
|
*/
|
|
8
8
|
export const localTimesView = (value, zone, color) => {
|
|
9
|
-
const clock =
|
|
10
|
-
timeZone: zone,
|
|
11
|
-
year: "numeric",
|
|
12
|
-
month: "2-digit",
|
|
13
|
-
day: "2-digit",
|
|
14
|
-
hour: "2-digit",
|
|
15
|
-
minute: "2-digit",
|
|
16
|
-
hourCycle: "h23",
|
|
17
|
-
});
|
|
9
|
+
const clock = wallClock(zone);
|
|
18
10
|
let shown = false;
|
|
19
11
|
const local = (text, dateOnly) => {
|
|
20
|
-
const part = Object.fromEntries(clock.formatToParts(Date.parse(text)).map(({ type, value }) => [type, value]));
|
|
21
12
|
shown = true;
|
|
22
|
-
|
|
23
|
-
return dateOnly ? date : `${date} ${part.hour}:${part.minute}`;
|
|
13
|
+
return dateOnly ? clock.date(text) : clock.time(text);
|
|
24
14
|
};
|
|
25
15
|
const walk = (one, allDay = false) => {
|
|
26
16
|
if (typeof one === "string")
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -86,6 +86,7 @@ export { type HelpSection, inSection, SECTIONS } from "./help/sections.js";
|
|
|
86
86
|
export { bufferedInput, type InputPolicy, inputPolicy, MAX_BUFFERED_INPUT, MAX_SECRET_INPUT, provideInputPolicy, } from "./input-policy.js";
|
|
87
87
|
export { answerFields, checkJson, type EntityFields, entityFields, type JsonFieldsResolver, type JsonRequest, liftJson, } from "./json-fields.js";
|
|
88
88
|
export { commandPath, LIST_QUERY_GUIDE, listQuery, nativeField, nativeTermLists, nativeTerms, negatableOf, pagedList, pageOf, type QueryWords, queryArgument, queryHelp, queryTerms, searchText, termField, timeBounds, typedQuery, unfilteredQuery, withQuery, } from "./list-query.js";
|
|
89
|
+
export { answerLocally, type ShownTime, wallClock } from "./local-times.js";
|
|
89
90
|
export { MCP_HTTP_DEFAULT_PORT, type McpServerOptions, type McpServerTarget, mcpServerCommand, } from "./mcp-server-command.js";
|
|
90
91
|
export { formatMeetingBatch, prepareMeetingBatch } from "./meeting-batch.js";
|
|
91
92
|
export { meetingEvidenceItem, meetingPersonContextItem } from "./meeting-evidence-answer.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -77,6 +77,7 @@ export { inSection, SECTIONS } from "./help/sections.js";
|
|
|
77
77
|
export { bufferedInput, inputPolicy, MAX_BUFFERED_INPUT, MAX_SECRET_INPUT, provideInputPolicy, } from "./input-policy.js";
|
|
78
78
|
export { answerFields, checkJson, entityFields, liftJson, } from "./json-fields.js";
|
|
79
79
|
export { commandPath, LIST_QUERY_GUIDE, listQuery, nativeField, nativeTermLists, nativeTerms, negatableOf, pagedList, pageOf, queryArgument, queryHelp, queryTerms, searchText, termField, timeBounds, typedQuery, unfilteredQuery, withQuery, } from "./list-query.js";
|
|
80
|
+
export { answerLocally, wallClock } from "./local-times.js";
|
|
80
81
|
export { MCP_HTTP_DEFAULT_PORT, mcpServerCommand, } from "./mcp-server-command.js";
|
|
81
82
|
export { formatMeetingBatch, prepareMeetingBatch } from "./meeting-batch.js";
|
|
82
83
|
export { meetingEvidenceItem, meetingPersonContextItem } from "./meeting-evidence-answer.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { type AnswerOutput, type Body } from "./envelope.js";
|
|
3
|
+
export declare const UTC_TIME: RegExp;
|
|
4
|
+
/** `YYYY-MM-DD HH:MM` on the wall clock of `zone`, with no offset: the answer names the zone once. */
|
|
5
|
+
export declare const wallClock: (zone: string) => {
|
|
6
|
+
date: (iso: string) => string;
|
|
7
|
+
time: (iso: string) => string;
|
|
8
|
+
};
|
|
9
|
+
/** Turns a UTC time into the shown one; any other text comes back as it was. */
|
|
10
|
+
export type ShownTime = (value: string) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Answers as `renderAnswer` does, and a person reads every UTC time in the zone in use (`zone`, else the computer's),
|
|
13
|
+
* with one line naming it (WC-TIME-4). A `view` builds its text with the `shown` it is given. JSON, JSONL, compact
|
|
14
|
+
* rows and agents keep UTC (WC-TIME-2), and `next` is left alone: its args run.
|
|
15
|
+
*/
|
|
16
|
+
export declare const answerLocally: <T>(command: Command, output: AnswerOutput, body: Body<T>, view?: (shown: ShownTime) => string, zone?: string) => void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { timezoneOf } from "@wirecat/cli-base/query";
|
|
2
|
+
import { environmentOf } from "./context.js";
|
|
3
|
+
import { renderAnswer } from "./envelope.js";
|
|
4
|
+
export const UTC_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/u;
|
|
5
|
+
/** `YYYY-MM-DD HH:MM` on the wall clock of `zone`, with no offset: the answer names the zone once. */
|
|
6
|
+
export const wallClock = (zone) => {
|
|
7
|
+
const format = new Intl.DateTimeFormat("en-CA", {
|
|
8
|
+
timeZone: zone,
|
|
9
|
+
year: "numeric",
|
|
10
|
+
month: "2-digit",
|
|
11
|
+
day: "2-digit",
|
|
12
|
+
hour: "2-digit",
|
|
13
|
+
minute: "2-digit",
|
|
14
|
+
hourCycle: "h23",
|
|
15
|
+
});
|
|
16
|
+
const parts = (iso) => Object.fromEntries(format.formatToParts(Date.parse(iso)).map(({ type, value }) => [type, value]));
|
|
17
|
+
const date = (iso) => {
|
|
18
|
+
const part = parts(iso);
|
|
19
|
+
return `${part.year}-${part.month}-${part.day}`;
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
date,
|
|
23
|
+
time: (iso) => {
|
|
24
|
+
const part = parts(iso);
|
|
25
|
+
return `${part.year}-${part.month}-${part.day} ${part.hour}:${part.minute}`;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const localised = (value, shown) => {
|
|
30
|
+
if (typeof value === "string")
|
|
31
|
+
return shown(value);
|
|
32
|
+
if (Array.isArray(value))
|
|
33
|
+
return value.map((one) => localised(one, shown));
|
|
34
|
+
if (value !== null && typeof value === "object")
|
|
35
|
+
return Object.fromEntries(Object.entries(value).map(([key, one]) => [key, localised(one, shown)]));
|
|
36
|
+
return value;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Answers as `renderAnswer` does, and a person reads every UTC time in the zone in use (`zone`, else the computer's),
|
|
40
|
+
* with one line naming it (WC-TIME-4). A `view` builds its text with the `shown` it is given. JSON, JSONL, compact
|
|
41
|
+
* rows and agents keep UTC (WC-TIME-2), and `next` is left alone: its args run.
|
|
42
|
+
*/
|
|
43
|
+
export const answerLocally = (command, output, body, view, zone) => {
|
|
44
|
+
if (output.format !== "pretty" || output.compact || environmentOf(command).agent) {
|
|
45
|
+
renderAnswer(command, output, body, view && (() => view((value) => value)));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const inUse = timezoneOf(zone);
|
|
49
|
+
const clock = wallClock(inUse);
|
|
50
|
+
let seen = false;
|
|
51
|
+
const shown = (value) => {
|
|
52
|
+
if (!UTC_TIME.test(value))
|
|
53
|
+
return value;
|
|
54
|
+
seen = true;
|
|
55
|
+
return clock.time(value);
|
|
56
|
+
};
|
|
57
|
+
if (view)
|
|
58
|
+
renderAnswer(command, output, body, () => view(shown));
|
|
59
|
+
else
|
|
60
|
+
renderAnswer(command, output, ("items" in body
|
|
61
|
+
? { ...body, items: body.items.map((one) => localised(one, shown)) }
|
|
62
|
+
: { ...body, item: localised(body.item, shown) }));
|
|
63
|
+
if (seen)
|
|
64
|
+
output.renderer.note(`times in ${inUse}`);
|
|
65
|
+
};
|
|
@@ -3,9 +3,9 @@ import type { CacheDatabase } from "../../store/driver.js";
|
|
|
3
3
|
import type { Messenger } from "./context.js";
|
|
4
4
|
/** `doctor`'s short summary of the store. Must answer for a broken file, so an error is a field. */
|
|
5
5
|
export declare const storeSummary: (env: NodeJS.ProcessEnv) => Promise<{
|
|
6
|
-
error?: undefined;
|
|
7
6
|
path: string;
|
|
8
7
|
exists: boolean;
|
|
8
|
+
error?: undefined;
|
|
9
9
|
} | {
|
|
10
10
|
path: string;
|
|
11
11
|
exists: boolean;
|
package/dist/cli/runs/command.js
CHANGED
|
@@ -6,6 +6,7 @@ import { environmentOf, outputFor } from "../context.js";
|
|
|
6
6
|
import { LOCAL_READ, NO_EFFECTS } from "../effects.js";
|
|
7
7
|
import { itemOf, listOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
8
8
|
import { typedQuery, withQuery } from "../list-query.js";
|
|
9
|
+
import { answerLocally } from "../local-times.js";
|
|
9
10
|
import { positiveCount } from "../paging.js";
|
|
10
11
|
import { lineOf } from "../steps.js";
|
|
11
12
|
import { RUNS_QUERY, runListFilter } from "./query.js";
|
|
@@ -33,7 +34,7 @@ export const runsCommand = (app) => {
|
|
|
33
34
|
const { renderer } = output;
|
|
34
35
|
const runs = listRuns(dir(this), { atLeast: page * limit + 1, filter });
|
|
35
36
|
const shown = runs.slice((page - 1) * limit, page * limit);
|
|
36
|
-
|
|
37
|
+
answerLocally(this, output, listOf("runs list", {
|
|
37
38
|
items: shown.map(snakeKeys),
|
|
38
39
|
limit,
|
|
39
40
|
page,
|
|
@@ -60,7 +61,7 @@ export const runsCommand = (app) => {
|
|
|
60
61
|
const { problems } = this.opts();
|
|
61
62
|
const shown = problems ? events.filter(notable) : events;
|
|
62
63
|
// Events are log records as written; only the run's own fields are renamed here.
|
|
63
|
-
|
|
64
|
+
answerLocally(this, output, itemOf({
|
|
64
65
|
...snakeKeys(found.metadata),
|
|
65
66
|
directory: found.dir,
|
|
66
67
|
event_count: events.length,
|
package/dist/mail/cli/index.js
CHANGED
|
@@ -6,8 +6,9 @@ import * as v from "valibot";
|
|
|
6
6
|
import { answerEntity } from "../../cli/answer-entity.js";
|
|
7
7
|
import { environmentOf, outputFor } from "../../cli/context.js";
|
|
8
8
|
import { FETCH, LOCAL_READ, REMOTE_READ } from "../../cli/effects.js";
|
|
9
|
-
import { itemOf, listOf,
|
|
9
|
+
import { itemOf, listOf, snakeKeys } from "../../cli/envelope.js";
|
|
10
10
|
import { listQuery, typedQuery, withQuery } from "../../cli/list-query.js";
|
|
11
|
+
import { answerLocally } from "../../cli/local-times.js";
|
|
11
12
|
import { messagesAnswer, offsetOf, shownOf } from "../../cli/messenger/search-answers.js";
|
|
12
13
|
import { backendRequest, noteServer, serverSearchOptions } from "../../cli/messenger/search-backend-options.js";
|
|
13
14
|
import { positiveCount } from "../../cli/paging.js";
|
|
@@ -193,7 +194,7 @@ Search guide: https://github.com/WireCatLabs/cli-core/blob/main/docs/search/quer
|
|
|
193
194
|
const issues = serverIssues(found.server);
|
|
194
195
|
if (issues.length)
|
|
195
196
|
answer.meta = { ...answer.meta, complete: false, issues: [...(answer.meta?.issues ?? []), ...issues] };
|
|
196
|
-
|
|
197
|
+
answerLocally(this, output, answer, (local) => {
|
|
197
198
|
if (!found.wordsReady)
|
|
198
199
|
output.renderer.note(`the word index is still being built — \`${mail.app.command} store migrate\` finishes it`);
|
|
199
200
|
noteServer(output, found.server);
|
|
@@ -204,12 +205,12 @@ Search guide: https://github.com/WireCatLabs/cli-core/blob/main/docs/search/quer
|
|
|
204
205
|
const shown = shownOf(found.items.length, (page - 1) * limit, found.total);
|
|
205
206
|
if (shown)
|
|
206
207
|
output.renderer.note(shown);
|
|
207
|
-
return found.items.map((item) => `${hitText(item, level)}\n`).join("");
|
|
208
|
-
});
|
|
208
|
+
return found.items.map((item) => `${hitText(item, level, local)}\n`).join("");
|
|
209
|
+
}, options.timezone);
|
|
209
210
|
});
|
|
210
211
|
return new Command("search").summary("search the stored mail").description("find stored emails").addCommand(leaf);
|
|
211
212
|
};
|
|
212
|
-
const shownText = (emails) => `${emails.map((email) => [hitText(email, "full"), attachmentsText(email)].filter(Boolean).join("\n")).join("\n\n")}\n`;
|
|
213
|
+
const shownText = (emails, local) => `${emails.map((email) => [hitText(email, "full", local), attachmentsText(email)].filter(Boolean).join("\n")).join("\n\n")}\n`;
|
|
213
214
|
const messagesCommand = (mail) => {
|
|
214
215
|
const messages = new Command("messages").summary("read stored emails").description("read the emails sync stored");
|
|
215
216
|
annotate(messages.command("show"), {
|
|
@@ -249,9 +250,9 @@ const messagesCommand = (mail) => {
|
|
|
249
250
|
});
|
|
250
251
|
const output = outputFor(this);
|
|
251
252
|
const shown = emails.map((email) => snakeKeys(atDetail(email)));
|
|
252
|
-
|
|
253
|
+
answerLocally(this, output, beforeN || afterN
|
|
253
254
|
? listOf("messages show", { items: shown, limit: Math.max(1, shown.length), has_more: false })
|
|
254
|
-
: itemOf(shown.find((_, at) => emails[at]?.anchor) ?? shown[0], unread ? { meta: { complete: false, issues: [unread] } } : undefined), () => shownText(emails));
|
|
255
|
+
: itemOf(shown.find((_, at) => emails[at]?.anchor) ?? shown[0], unread ? { meta: { complete: false, issues: [unread] } } : undefined), (local) => shownText(emails, local));
|
|
255
256
|
});
|
|
256
257
|
messages.addCommand(acknowledgeCommand(mail));
|
|
257
258
|
messages.addCommand(resetMarkCommand(mail));
|
|
@@ -315,14 +316,14 @@ const conversationsCommand = (mail) => {
|
|
|
315
316
|
last_email_at: thread.lastEmailAt === null ? null : new Date(thread.lastEmailAt).toISOString(),
|
|
316
317
|
untrusted: ["subject"],
|
|
317
318
|
}));
|
|
318
|
-
|
|
319
|
+
answerLocally(this, outputFor(this), listOf("conversations list", {
|
|
319
320
|
items,
|
|
320
321
|
limit,
|
|
321
322
|
has_more: threads.length > limit,
|
|
322
323
|
page,
|
|
323
324
|
...(typed.trim() ? { args: { query: typed } } : {}),
|
|
324
|
-
}), () => items
|
|
325
|
-
.map((one) => `${one.ref}\n ${one.last_email_at
|
|
325
|
+
}), (local) => items
|
|
326
|
+
.map((one) => `${one.ref}\n ${one.last_email_at === null ? "time unknown" : local(one.last_email_at)} ${one.emails_count} ${one.subject ?? ""}\n`)
|
|
326
327
|
.join(""));
|
|
327
328
|
});
|
|
328
329
|
answerEntity(list, "mail_thread", mailThread);
|
|
@@ -349,7 +350,7 @@ const conversationsCommand = (mail) => {
|
|
|
349
350
|
: hit));
|
|
350
351
|
});
|
|
351
352
|
const items = emails.map((email) => snakeKeys(atDetail(email)));
|
|
352
|
-
|
|
353
|
+
answerLocally(this, outputFor(this), listOf("conversations show", { items, limit: Math.max(1, items.length), has_more: false }), (local) => shownText(emails, local));
|
|
353
354
|
});
|
|
354
355
|
return conversations;
|
|
355
356
|
};
|
package/dist/mail/cli/review.js
CHANGED
|
@@ -5,8 +5,9 @@ import { defineSteps, MAX_NEXT_STEPS, stepOf, } from "@wirecat/cli-base/standard
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { environmentOf, outputFor } from "../../cli/context.js";
|
|
7
7
|
import { LOCAL_READ } from "../../cli/effects.js";
|
|
8
|
-
import { listOf
|
|
8
|
+
import { listOf } from "../../cli/envelope.js";
|
|
9
9
|
import { listQuery, nativeField, nativeTerms, queryHelp, typedQuery } from "../../cli/list-query.js";
|
|
10
|
+
import { answerLocally } from "../../cli/local-times.js";
|
|
10
11
|
import { pointsOf } from "../../cli/messenger/catch-up.js";
|
|
11
12
|
import { catchUpQuery, SUMMARY_UNTRUSTED, summaryOf } from "../../cli/messenger/catch-up-answers.js";
|
|
12
13
|
import { messageItem } from "../../cli/messenger/message-items.js";
|
|
@@ -231,11 +232,13 @@ const holderReview = async (command, mail, holder, query) => {
|
|
|
231
232
|
: []),
|
|
232
233
|
];
|
|
233
234
|
const output = outputFor(command);
|
|
234
|
-
|
|
235
|
+
answerLocally(command, output, answer, (local) => {
|
|
235
236
|
output.renderer.note(read.mark
|
|
236
237
|
? `${holder}: past ${read.mark.throughId}${emails.length ? `; findings key ${read.findingsKey}` : ""}; acknowledge with --cursor ${read.cursor}${read.more ? "; more after these" : ""}`
|
|
237
238
|
: `${holder} has no mark yet: it starts from now — acknowledge with --cursor ${read.cursor}`);
|
|
238
|
-
return emails
|
|
239
|
+
return emails
|
|
240
|
+
.map((hit) => `${hitText(hit, undefined, local)}${markText(marks.get(hit.id), hit.excerptOnly)}\n`)
|
|
241
|
+
.join("");
|
|
239
242
|
});
|
|
240
243
|
};
|
|
241
244
|
/**
|
|
@@ -311,7 +314,7 @@ an agent over MCP, and each scheduled run.`)
|
|
|
311
314
|
const answer = reviewAnswer(found, mailbox, saved !== undefined, stepsFor(mail.mount, mailbox));
|
|
312
315
|
if (meta)
|
|
313
316
|
answer.meta = { ...meta, ...answer.meta };
|
|
314
|
-
|
|
317
|
+
answerLocally(this, output, answer, (local) => {
|
|
315
318
|
const read = found.chats.flatMap((chat) => chat.messages);
|
|
316
319
|
if (found.unanswered || found.waiting)
|
|
317
320
|
output.renderer.note(`${[
|
|
@@ -319,16 +322,16 @@ an agent over MCP, and each scheduled run.`)
|
|
|
319
322
|
found.waiting ? `waiting ${found.waiting.olderThanHours}h or more` : "",
|
|
320
323
|
]
|
|
321
324
|
.filter(Boolean)
|
|
322
|
-
.join(", ")}, threads active from ${found.since}`);
|
|
325
|
+
.join(", ")}, threads active from ${local(found.since)}`);
|
|
323
326
|
else
|
|
324
|
-
output.renderer.note(`from ${found.since} to ${found.until}${found.complete ? "" : ", incomplete"}`);
|
|
327
|
+
output.renderer.note(`from ${local(found.since)} to ${local(found.until)}${found.complete ? "" : ", incomplete"}`);
|
|
325
328
|
if (found.quiet > 0)
|
|
326
329
|
output.renderer.note(`bulk:false left out ${found.quiet} threads`);
|
|
327
330
|
if (read.length === 0)
|
|
328
331
|
output.renderer.note("nothing new in the stored mail");
|
|
329
332
|
return read
|
|
330
333
|
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp))
|
|
331
|
-
.map((hit) => `${hitText(hit)}${markText(hit.marks)}\n`)
|
|
334
|
+
.map((hit) => `${hitText(hit, undefined, local)}${markText(hit.marks)}\n`)
|
|
332
335
|
.join("");
|
|
333
336
|
});
|
|
334
337
|
saved?.save(found.checked ?? {}, found.complete ? { through: found.until } : {});
|
package/dist/mail/read.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export declare const showMail: (store: MessageStore, locator: string, { before,
|
|
|
54
54
|
*/
|
|
55
55
|
export declare const hitText: (item: StoredHit & {
|
|
56
56
|
excerpt?: string;
|
|
57
|
-
}, level?: "brief" | "full") => string;
|
|
57
|
+
}, level?: "brief" | "full", shown?: (time: string) => string) => string;
|
|
58
58
|
/** Each attachment of a shown email with its text indented under it, or why it has none. */
|
|
59
59
|
export declare const attachmentsText: (hit: ShownMail) => string;
|
|
60
60
|
export {};
|
package/dist/mail/read.js
CHANGED
|
@@ -82,7 +82,7 @@ const line = (text) => text.replace(/\s+/g, " ").trim();
|
|
|
82
82
|
* A found message or email for a reader: its reference first, then when and from whom, who else it went to and
|
|
83
83
|
* where it is filed, its subject or chat, and an excerpt — the whole text only at `full`.
|
|
84
84
|
*/
|
|
85
|
-
export const hitText = (item, level) => {
|
|
85
|
+
export const hitText = (item, level, shown = (time) => time) => {
|
|
86
86
|
const mail = item.email;
|
|
87
87
|
const names = (people) => people.map(({ name, address }) => line(name ?? address)).join(", ");
|
|
88
88
|
const between = mail
|
|
@@ -97,7 +97,7 @@ export const hitText = (item, level) => {
|
|
|
97
97
|
const who = line(item.senderName ?? item.senderId ?? "unknown");
|
|
98
98
|
return [
|
|
99
99
|
item.locator,
|
|
100
|
-
` ${item.timestamp} ${who}${level === "brief" || !between ? "" : ` ${between}`}`,
|
|
100
|
+
` ${shown(item.timestamp)} ${who}${level === "brief" || !between ? "" : ` ${between}`}`,
|
|
101
101
|
...(level === "brief" || !item.chatTitle ? [] : [` ${line(item.chatTitle)}`]),
|
|
102
102
|
` ${level === "full" ? item.text.replace(/\n/g, "\n ") : line(item.excerpt ?? item.text)}`,
|
|
103
103
|
].join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.587.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",
|
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
"@types/node": "^22.20.5",
|
|
259
259
|
"@types/pngjs": "6.0.5",
|
|
260
260
|
"@vitest/coverage-v8": "^5.0.5",
|
|
261
|
-
"@wirecat/cli-base": "0.
|
|
261
|
+
"@wirecat/cli-base": "0.80.0",
|
|
262
262
|
"@wirecat/cli-core-legacy-tags": "npm:@wirecat/cli-core@0.414.0",
|
|
263
263
|
"commander": "15.0.0",
|
|
264
264
|
"cspell": "10.4.0",
|
|
@@ -278,7 +278,7 @@
|
|
|
278
278
|
},
|
|
279
279
|
"peerDependencies": {
|
|
280
280
|
"@napi-rs/canvas": ">=0.1.69 <2",
|
|
281
|
-
"@wirecat/cli-base": ">=0.76.0 <0.
|
|
281
|
+
"@wirecat/cli-base": ">=0.76.0 <0.81.0",
|
|
282
282
|
"commander": ">=15"
|
|
283
283
|
},
|
|
284
284
|
"bin": {
|