@wirecat/cli-core 0.360.0 → 0.361.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
@@ -7,6 +7,20 @@ shape of this file.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.361.0 — 19.10.2026
11
+
12
+ ### Fixed
13
+
14
+ - **A warning in `serve`'s log now says which warning it was.** Off a terminal, a re-key given up at exit, a write
15
+ the store did not take, a passive listen that failed and a stored-chats call that failed each wrote a bare
16
+ `serve.warning` record, so the log could not tell them apart. The record now carries the warning's `code`,
17
+ `operation` and `counts` (`rekeys_not_applied` with `counts.rekeys`), never its sentence, which can quote an
18
+ error or a chat. Warnings that have no code yet still log a bare record.
19
+ - **The adapter test example in [ADAPTERS.md](docs/dev/ADAPTERS.md#running-the-contract-cases) no longer gives up
20
+ re-keys.** It handed `run()` `rekeyPacing: { lockMs: 0 }`, one re-key per transaction, so a few hundred re-keys
21
+ outlasted `closeWaitMs: 10` and were given up. It now hands `{ lockMs: 1000, pauseMs: 0 }` and says what each
22
+ field does.
23
+
10
24
  ## 0.360.0 — 19.10.2026
11
25
 
12
26
  ### Added
@@ -37,7 +37,7 @@ export interface BaseEnvironment {
37
37
  storeLockWaitMs?: number;
38
38
  /** How long a connection's close waits on its saves and passive listens, 5 s unless a test hands one in. */
39
39
  closeWaitMs?: number;
40
- /** How long one pass of chat re-keys holds the store's write lock and the pause after it; tests hand in zeros. */
40
+ /** How long one pass of chat re-keys holds the store's write lock and the pause after it; tests hand in no pause. */
41
41
  rekeyPacing?: {
42
42
  lockMs: number;
43
43
  pauseMs: number;
@@ -126,8 +126,9 @@ const listened = ({ run, operation, what }, events, warn, waitMs) => {
126
126
  .catch((error) => {
127
127
  if (closing)
128
128
  return;
129
- events({ event: "warning", code: "listen_failed", operation });
130
- warn(`${what} arriving during this command are not saved: ${error instanceof Error ? error.message : String(error)}`);
129
+ const warning = { event: "warning", code: "listen_failed", operation };
130
+ events(warning);
131
+ warn(`${what} arriving during this command are not saved: ${error instanceof Error ? error.message : String(error)}`, warning);
131
132
  });
132
133
  return {
133
134
  operation,
@@ -230,22 +231,22 @@ export const connected = (connection, { app, provider, name, deletedWithoutChat,
230
231
  finally {
231
232
  try {
232
233
  const ended = await Promise.all(passives.map((passive) => passive.stop()));
234
+ const warn = renderer.warn;
235
+ const warned = (warning, text) => {
236
+ events(warning);
237
+ warn(text, warning);
238
+ };
233
239
  passives.forEach(({ operation, what }, at) => {
234
240
  if (ended[at])
235
241
  return;
236
- events({ event: "warning", code: "store_not_written", operation });
237
- renderer.warn(`not saved to the local store: the messenger was still handing on ${what} at exit`);
242
+ warned({ event: "warning", code: "store_not_written", operation }, `not saved to the local store: the messenger was still handing on ${what} at exit`);
238
243
  });
239
244
  await settled(new Set([...pending, ...announcing]), closeWaitMs);
240
245
  closing.abort();
241
- if (pending.size > 0) {
242
- events({ event: "warning", code: "store_not_written", operation: "close" });
243
- renderer.warn("not saved to the local store: the last messages were still being written at exit");
244
- }
245
- if (announcing.size > 0) {
246
- events({ event: "warning", code: "stored_chats_failed", operation: "close" });
247
- renderer.warn("the messenger's stored-chats hook did not finish and was abandoned at exit; nothing is lost");
248
- }
246
+ if (pending.size > 0)
247
+ warned({ event: "warning", code: "store_not_written", operation: "close" }, "not saved to the local store: the last messages were still being written at exit");
248
+ if (announcing.size > 0)
249
+ warned({ event: "warning", code: "stored_chats_failed", operation: "close" }, "the messenger's stored-chats hook did not finish and was abandoned at exit; nothing is lost");
249
250
  }
250
251
  finally {
251
252
  await store.close();
@@ -13,8 +13,9 @@ export declare const noticeFor: (context: MessengerContext, app: AppIdentity, se
13
13
  /**
14
14
  * A serve left running writes its diagnostics to a log file, so off a terminal each one is a log record with
15
15
  * its time and run. A sentence handed to the renderer may quote an error or a chat, so the record keeps only
16
- * which kind of line it was; serve's own reports go through `noticeFor` with a code. The renderer is changed
17
- * in place, so the send guard, bound to it before serve started, writes records too.
16
+ * which kind of line it was, and a warning's code, operation and counts when the warning event comes with it
17
+ * (`Warn`); serve's own reports go through `noticeFor` with a code. The renderer is changed in place, so the
18
+ * send guard, bound to it before serve started, writes records too.
18
19
  */
19
20
  export declare const recordDiagnostics: (context: MessengerContext, app: AppIdentity) => MessengerContext;
20
21
  export {};
@@ -19,16 +19,18 @@ export const noticeFor = (context, app, severity = "warn") => ({ text, ...attrib
19
19
  /**
20
20
  * A serve left running writes its diagnostics to a log file, so off a terminal each one is a log record with
21
21
  * its time and run. A sentence handed to the renderer may quote an error or a chat, so the record keeps only
22
- * which kind of line it was; serve's own reports go through `noticeFor` with a code. The renderer is changed
23
- * in place, so the send guard, bound to it before serve started, writes records too.
22
+ * which kind of line it was, and a warning's code, operation and counts when the warning event comes with it
23
+ * (`Warn`); serve's own reports go through `noticeFor` with a code. The renderer is changed in place, so the
24
+ * send guard, bound to it before serve started, writes records too.
24
25
  */
25
26
  export const recordDiagnostics = (context, app) => {
26
27
  if (context.format === "pretty")
27
28
  return context;
28
- const say = (severity, event, always = false) => (_text) => {
29
+ const say = (severity, event, always = false) => (_text, warning) => {
29
30
  if (context.settings.quiet && !always)
30
31
  return;
31
- record(context, app, severity, { event });
32
+ const { event: _warning, ...fields } = warning ?? {};
33
+ record(context, app, severity, { event, ...fields });
32
34
  };
33
35
  const renderer = context.renderer;
34
36
  renderer.note = say("info", "serve.note");
@@ -1,13 +1,13 @@
1
1
  import type { Id } from "../../domain/models.js";
2
2
  import { type MessengerAdapter } from "../../ports/messenger.js";
3
3
  import type { MessengerTraits } from "../../ports/traits.js";
4
- import type { EventSink } from "../../runs/events.js";
4
+ import type { EventSink, Warn } from "../../runs/events.js";
5
5
  import type { AccountKey, DeletionScope, MessageStore } from "../../store/store.js";
6
6
  export interface Saving {
7
7
  account: AccountKey;
8
8
  /** Opened on the first save, not before: a command that reads nothing has no reason to touch the file. */
9
9
  store: () => Promise<MessageStore | undefined>;
10
- warn: (message: string) => void;
10
+ warn: Warn;
11
11
  events: EventSink;
12
12
  /** Saves still writing, for the closer to wait on: `watch` does not hold the next event for one. */
13
13
  pending?: Set<Promise<void>>;
@@ -16,9 +16,10 @@ export const stored = (messenger, { account, store, warn, events, pending, annou
16
16
  let warned = false;
17
17
  let recorded = profile === undefined;
18
18
  const notWritten = (operation, error) => {
19
- events({ event: "warning", code: "store_not_written", operation });
19
+ const warning = { event: "warning", code: "store_not_written", operation };
20
+ events(warning);
20
21
  if (!warned)
21
- warn(`not saved to the local store: ${error instanceof Error ? error.message : String(error)}`);
22
+ warn(`not saved to the local store: ${error instanceof Error ? error.message : String(error)}`, warning);
22
23
  warned = true;
23
24
  };
24
25
  const attempt = async (operation, write) => {
@@ -92,8 +93,14 @@ export const stored = (messenger, { account, store, warn, events, pending, annou
92
93
  pending?.delete(saving);
93
94
  if (count === 0)
94
95
  return;
95
- events({ event: "warning", code: "rekeys_not_applied", operation: "history.rekey", counts: { rekeys: count } });
96
- warn(`${count} chat re-key${count === 1 ? "" : "s"} not applied yet; the next serve applies them`);
96
+ const warning = {
97
+ event: "warning",
98
+ code: "rekeys_not_applied",
99
+ operation: "history.rekey",
100
+ counts: { rekeys: count },
101
+ };
102
+ events(warning);
103
+ warn(`${count} chat re-key${count === 1 ? "" : "s"} not applied yet; the next serve applies them`, warning);
97
104
  };
98
105
  closing?.addEventListener("abort", giveUpRekeys, { once: true });
99
106
  const saveRekeys = async (pairs, round) => {
@@ -466,8 +473,9 @@ const announceStoredChats = async (messenger, store, account, events, warn, reci
466
473
  await messenger.storedChats?.(held);
467
474
  }
468
475
  catch (error) {
469
- events({ event: "warning", code: "stored_chats_failed", operation: "history.stored_chats" });
470
- warn(`the messenger was not told the stored chats: ${error instanceof Error ? error.message : String(error)}`);
476
+ const warning = { event: "warning", code: "stored_chats_failed", operation: "history.stored_chats" };
477
+ events(warning);
478
+ warn(`the messenger was not told the stored chats: ${error instanceof Error ? error.message : String(error)}`, warning);
471
479
  }
472
480
  };
473
481
  /** Read once when `watch` starts: a store read per event would let a later event overtake an earlier one. */
@@ -49,6 +49,8 @@ export interface CacheEvent {
49
49
  }
50
50
  export type DiagnosticEvent = RequestEvent | CacheEvent | WarningEvent;
51
51
  export type EventSink = (event: DiagnosticEvent) => void;
52
+ /** A sentence for a person, and the warning event it stands for when there is one: a log record keeps only the event. */
53
+ export type Warn = (message: string, warning?: WarningEvent) => void;
52
54
  /** `FLOOD_WAIT`, `PEER_ID_INVALID`, `login.token` — or nothing. A sentence is dropped, not trimmed. */
53
55
  export declare const providerErrorKey: (value: unknown) => string | undefined;
54
56
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.360.0",
3
+ "version": "0.361.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",