@ccmsg/cli 0.11.2 → 0.11.4

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.
Files changed (67) hide show
  1. package/README.md +8 -12
  2. package/package.json +1 -1
  3. package/src/auth/auth.ts +23 -23
  4. package/src/auth/http.ts +3 -3
  5. package/src/auth/records.ts +2 -2
  6. package/src/auth/webauthn.ts +1 -1
  7. package/src/cli.ts +78 -23
  8. package/src/daemon/control.ts +1 -1
  9. package/src/daemon/registry.ts +11 -11
  10. package/src/daemon/snapshot.ts +1 -1
  11. package/src/daemon/supervise.ts +4 -4
  12. package/src/dispatch/caller.ts +1 -1
  13. package/src/dispatch/dispatch.ts +4 -4
  14. package/src/dispatch/handler.ts +4 -4
  15. package/src/dispatch/identity.ts +1 -1
  16. package/src/dispatch/result.ts +2 -2
  17. package/src/files/containment.ts +2 -2
  18. package/src/harness/index.ts +1 -1
  19. package/src/instance/client.ts +1 -1
  20. package/src/instance/config.ts +13 -13
  21. package/src/instance/instance.ts +42 -42
  22. package/src/instance/lock.ts +2 -2
  23. package/src/instance/log.ts +1 -1
  24. package/src/instance/paths.ts +6 -6
  25. package/src/instance/socket.ts +1 -1
  26. package/src/kv/store.ts +1 -1
  27. package/src/mesh/instances.ts +2 -2
  28. package/src/mesh/keys.ts +7 -7
  29. package/src/mesh/mesh.ts +67 -67
  30. package/src/mesh/relay.ts +8 -8
  31. package/src/mesh/wire.ts +2 -2
  32. package/src/messaging/delivery.ts +24 -24
  33. package/src/messaging/direct.ts +23 -23
  34. package/src/messaging/inbox.ts +6 -6
  35. package/src/messaging/notify.ts +5 -5
  36. package/src/plugin/codex.ts +1 -1
  37. package/src/sessions/classify.ts +6 -6
  38. package/src/sessions/dump.ts +1 -1
  39. package/src/sessions/handlers.ts +2 -2
  40. package/src/sessions/harness.ts +12 -12
  41. package/src/sessions/last-live.ts +4 -4
  42. package/src/sessions/registry.ts +32 -32
  43. package/src/sessions/status.ts +7 -7
  44. package/src/sessions/terminals.ts +1 -1
  45. package/src/transcript/files.ts +4 -4
  46. package/src/transcript/fold.ts +9 -9
  47. package/src/transcript/items/classify.ts +56 -8
  48. package/src/transcript/read.ts +1 -1
  49. package/src/transcript/tail.ts +3 -3
  50. package/src/transcript/transcripts.ts +7 -7
  51. package/src/translate/helper.ts +2 -2
  52. package/src/transport/conn.ts +3 -3
  53. package/src/transport/dial.ts +1 -1
  54. package/src/transport/driver.ts +2 -2
  55. package/src/transport/entry.ts +1 -1
  56. package/src/transport/framing.ts +1 -1
  57. package/src/transport/listener.ts +3 -3
  58. package/src/transport/uds.ts +2 -2
  59. package/src/transport/ws.ts +2 -2
  60. package/src/upstream/events.ts +3 -3
  61. package/src/upstream/gateway.ts +6 -6
  62. package/src/upstream/json.ts +1 -1
  63. package/src/upstream/requests.ts +6 -6
  64. package/src/upstream/stats.ts +1 -1
  65. package/src/upstream/status.ts +4 -4
  66. package/src/upstream/usage.ts +1 -1
  67. package/src/upstream/webhook.ts +1 -1
@@ -8,7 +8,7 @@ import {
8
8
  /** One accepted connection, as the layers above transport see it.
9
9
  *
10
10
  * UDS and WS produce this same type, so nothing above transport can tell them
11
- * apart (daemon-v2 §3.1). Everything that differs between the two — how a line
11
+ * apart (DESIGN §2.1). Everything that differs between the two — how a line
12
12
  * reaches the peer, how a blocked write is retried — is settled behind `send`. */
13
13
  export interface Conn extends Requester {
14
14
  /** Distinguishes connections within one process run. It is not an identity:
@@ -34,7 +34,7 @@ export interface Conn extends Requester {
34
34
  * (mesh-peer-auth §8.1). Transports that carry no such code ignore it. */
35
35
  close(code?: number, reason?: string): void;
36
36
  /** Run when the connection is gone. Anything held per connection — the
37
- * subscriptions of §6.3 once they exist — is released here, because a closed
37
+ * subscriptions of DESIGN §6.3 once they exist — is released here, because a closed
38
38
  * connection is the only end a subscription has. */
39
39
  onClose(listener: () => void): void;
40
40
  }
@@ -101,7 +101,7 @@ export class BaseConn implements Conn {
101
101
 
102
102
  /** The connections one instance currently holds.
103
103
  *
104
- * It exists so shutdown (§8.5 step 3) can reach every connection before any
104
+ * It exists so shutdown (DESIGN §8.5 step 3) can reach every connection before any
105
105
  * listener is closed, and so tests can see that a closed connection is gone. */
106
106
  export class ConnRegistry {
107
107
  readonly #conns = new Set<Conn>();
@@ -16,7 +16,7 @@ export interface DialOptions {
16
16
 
17
17
  /** A connection this instance opened, as the same `Conn` an accepted one is.
18
18
  *
19
- * daemon-v2 §3.1 has one connection type above transport, and a mesh link is a
19
+ * DESIGN §2.1 has one connection type above transport, and a mesh link is a
20
20
  * connection of that layer whichever end dialled it: the two differ in who
21
21
  * opened the socket and in nothing the layers above can see. */
22
22
  export async function dialWs(options: DialOptions): Promise<Conn> {
@@ -9,7 +9,7 @@ export interface FrameHandler {
9
9
  }
10
10
 
11
11
  /** The ops whose reply settles the connection's identity. Transport knows these
12
- * three op names because binding the identity is its job (daemon-v2 §3.1);
12
+ * three op names because binding the identity is its job (DESIGN §2.1);
13
13
  * every other op is opaque to it. */
14
14
 
15
15
  /** Drive one connection: a line in, a frame answered on the same connection.
@@ -36,7 +36,7 @@ export function createDriver(conn: Conn, handle: FrameHandler) {
36
36
  settleIfHello(conn, frame, result);
37
37
  conn.send(responseOf(result));
38
38
  // Whatever the implementation queued for after its reply — the
39
- // snapshot of a fresh subscription (§6.1) — goes out here.
39
+ // snapshot of a fresh subscription (DESIGN §6.1) — goes out here.
40
40
  conn.flushDeferred();
41
41
  },
42
42
  (cause: unknown) => {
@@ -1,4 +1,4 @@
1
- /** Who is allowed to reach this instance at all (daemon-v2 §3.1, "入口の許可").
1
+ /** Who is allowed to reach this instance at all (DESIGN §2.1, "入口の許可").
2
2
  *
3
3
  * Two questions, because they are asked of different things. `allowRequest`
4
4
  * runs for every HTTP request the listener takes, the routed ones included, and
@@ -1,4 +1,4 @@
1
- /** Newline-delimited JSON, in one place for both transports (daemon-v2 §3.1).
1
+ /** Newline-delimited JSON, in one place for both transports (DESIGN §2.1).
2
2
  *
3
3
  * The limit and the backpressure handling live here rather than in the UDS and
4
4
  * WS listeners, so the two cannot drift into two framings. */
@@ -1,6 +1,6 @@
1
1
  /** One bound address, and the way to give it up.
2
2
  *
3
- * `kind` exists for the stop order of daemon-v2 §8.5: the UDS is released after
3
+ * `kind` exists for the stop order of DESIGN §8.5: the UDS is released after
4
4
  * everything else, because a client reads "the unix socket refuses" as the
5
5
  * instance having finished leaving, and a successor may take the resources it
6
6
  * sees freed before that. */
@@ -15,7 +15,7 @@ export interface Listener {
15
15
  close(): Promise<void>;
16
16
  }
17
17
 
18
- /** The listeners one instance holds, closed in the order §8.5 requires. */
18
+ /** The listeners one instance holds, closed in the order DESIGN §8.5 requires. */
19
19
  export class Transport {
20
20
  readonly #listeners: Listener[] = [];
21
21
 
@@ -29,7 +29,7 @@ export class Transport {
29
29
  }
30
30
 
31
31
  /** Release every address, the unix socket last. Callers do the steps that
32
- * come before this one (§8.5 1-4: refuse new work, stop upstream watches,
32
+ * come before this one (DESIGN §8.5 1-4: refuse new work, stop upstream watches,
33
33
  * tell the connections, settle what is persisted). */
34
34
  async close(): Promise<void> {
35
35
  const held = [...this.#listeners];
@@ -13,7 +13,7 @@ interface UdsState {
13
13
  export interface UdsOptions {
14
14
  /** The socket path.
15
15
  *
16
- * §8.5 asks that closing leave the path alone, because a successor's socket
16
+ * DESIGN §8.5 asks that closing leave the path alone, because a successor's socket
17
17
  * may already stand there. Bun's listener unlinks it in `stop()` regardless
18
18
  * (measured against Bun 1.3.13: the path is gone the moment `stop` returns),
19
19
  * so this layer cannot honour that on its own — what it can do is not add a
@@ -29,7 +29,7 @@ export interface UdsOptions {
29
29
  *
30
30
  * `socket.write` hands the bytes to sendto(2) and returns a short count when
31
31
  * the socket buffer is full, so the unsent tail is kept by the queue and
32
- * written again on `drain` — the difference from WS that §3.1 puts in this
32
+ * written again on `drain` — the difference from WS that DESIGN §2.1 puts in this
33
33
  * layer. */
34
34
  export function listenUds(options: UdsOptions): Listener {
35
35
  const server = Bun.listen<UdsState>({
@@ -37,7 +37,7 @@ export interface WsOptions {
37
37
  *
38
38
  * It shares this listener rather than opening a second one: a producer that
39
39
  * posts to this instance reaches it at the address it already has, and the
40
- * entry check of §3.1 runs before this is asked, so a route cannot be
40
+ * entry check of DESIGN §2.1 runs before this is asked, so a route cannot be
41
41
  * reached by anyone the WebSocket could not be. Answering `undefined` leaves
42
42
  * the request to the upgrade, which refuses it.
43
43
  *
@@ -54,7 +54,7 @@ export interface WsOptions {
54
54
  * newline-delimited framing as the unix socket: one line is one frame, whether
55
55
  * a message holds one line or several. Backpressure differs from UDS — a send
56
56
  * is either buffered whole by Bun or dropped whole — and the queue absorbs
57
- * that difference here (§3.1). */
57
+ * that difference here (DESIGN §2.1). */
58
58
  export function serveWs(options: WsOptions): Listener {
59
59
  const path = options.path ?? ENTRY_PATH;
60
60
  const entry = options.entry ?? OPEN;
@@ -4,7 +4,7 @@ import type { LlmRequestInfo, Sid, Timestamp } from "@ccmsg/protocol";
4
4
  * instance decides whether its series is the session's main one.
5
5
  *
6
6
  * `main` is a verdict about a session's several series, so it needs the other
7
- * series to be made and cannot be read off one event (§3.5 renames, it does not
7
+ * series to be made and cannot be read off one event (DESIGN §2.4 renames, it does not
8
8
  * derive). `instance` is stamped by whoever publishes, since an event says
9
9
  * nothing about which instance received it. */
10
10
  export type LlmRequestObservation = Omit<LlmRequestInfo, "main" | "instance">;
@@ -68,7 +68,7 @@ export type GatewayItem =
68
68
  /** The name of the lifetime this request promised, when it promised one.
69
69
  * Kept beside the observation rather than inside it: it is how two
70
70
  * notices of the gateway's are matched to each other, and nothing a
71
- * client reads (§3.5). */
71
+ * client reads (DESIGN §2.4). */
72
72
  readonly notice?: string;
73
73
  }
74
74
  | { readonly kind: "response"; readonly info: LlmResponseObservation }
@@ -95,7 +95,7 @@ const SAME_NAME_NUMBERS = [
95
95
 
96
96
  /** The instants the gateway names without the suffix this contract requires of
97
97
  * every field that is a point in time. Renamed here, at the boundary, so
98
- * nothing downstream sees the gateway's spelling (§3.5). */
98
+ * nothing downstream sees the gateway's spelling (DESIGN §2.4). */
99
99
  const RENAMED_INSTANTS = [
100
100
  ["cache_since", "cache_since_at"],
101
101
  ["cache_until", "cache_until_at"],
@@ -45,14 +45,14 @@ export interface GatewaySetup {
45
45
  readonly statsUrl?: string;
46
46
  }
47
47
 
48
- /** Read the upstream section (§8.2).
48
+ /** Read the upstream section (DESIGN §8.2).
49
49
  *
50
50
  * A setting that is there and cannot be honoured ends the start rather than
51
- * leaving the feature it asked for silently off (DV-Q9): an operator who named
51
+ * leaving the feature it asked for silently off (DR-0004): an operator who named
52
52
  * a webhook source wants the route, and an instance that came up without it
53
53
  * looks identical to one nobody is posting to. A section that is absent is not
54
54
  * broken — it states that this instance has no gateway, which costs the rows
55
- * one attribute and nothing else (§5.2). */
55
+ * one attribute and nothing else (DESIGN §4.3). */
56
56
  export function gatewaySetup(config: UpstreamConfig, file: string, env: Env): GatewaySetup {
57
57
  const name = config.gateway_webhook_source;
58
58
  const url = config.gateway_url;
@@ -112,7 +112,7 @@ export interface GatewayDeps {
112
112
  readonly setup: GatewaySetup;
113
113
  readonly publish: (topic: string, data: unknown) => void;
114
114
  /** The gateway saw something happen for a session, which is an input of the
115
- * sessions domain (§5.1) rather than of either topic. */
115
+ * sessions domain (DESIGN §4.2) rather than of either topic. */
116
116
  readonly onActivity?: () => void;
117
117
  /** A session already known to be running was seen again: its clock moved,
118
118
  * and the row that carries it is what says so. */
@@ -173,7 +173,7 @@ export class Gateway {
173
173
  return this.status ?? SILENT;
174
174
  }
175
175
 
176
- /** When the gateway last saw inference for a session (§5.1). */
176
+ /** When the gateway last saw inference for a session (DESIGN §4.2). */
177
177
  activeAt(sid: Sid, now?: Timestamp): Timestamp | undefined {
178
178
  return this.requests.activeAt(sid, now);
179
179
  }
@@ -184,7 +184,7 @@ export class Gateway {
184
184
  return handleWebhook(request, this.#source, this.deps.log);
185
185
  }
186
186
 
187
- /** Stop what is pending. Called from the stop order (§8.5). */
187
+ /** Stop what is pending. Called from the stop order (DESIGN §8.5). */
188
188
  close(): void {
189
189
  this.status?.stop();
190
190
  }
@@ -1,6 +1,6 @@
1
1
  import type { Timestamp } from "@ccmsg/protocol";
2
2
 
3
- /** What every read of an upstream document is built from (§3.5).
3
+ /** What every read of an upstream document is built from (DESIGN §2.4).
4
4
  *
5
5
  * The gateway's three documents are read the same way — a field is taken only
6
6
  * at the type this contract states for it, and anything else is absent — so the
@@ -17,10 +17,10 @@ import type {
17
17
 
18
18
  export interface LlmRequestsDeps {
19
19
  readonly self: InstanceId;
20
- /** The one way a value reaches subscribers (§6.1). */
20
+ /** The one way a value reaches subscribers (DESIGN §6.1). */
21
21
  readonly publish: (topic: string, data: unknown) => void;
22
22
  /** An event moved when a session was last seen running inference, which is
23
- * an input of the sessions domain (§5.1) and not of this topic. Told when
23
+ * an input of the sessions domain (DESIGN §4.2) and not of this topic. Told when
24
24
  * the window opened, which is the moment the classification can change. */
25
25
  readonly onActivity?: () => void;
26
26
  /** The same session seen again inside a window already open: one attribute
@@ -32,7 +32,7 @@ export interface LlmRequestsDeps {
32
32
 
33
33
  /** Which of a session's gateway facts moved.
34
34
  *
35
- * `live` is the moment the classification of §5.1 can change, because the
35
+ * `live` is the moment the classification of DESIGN §4.2 can change, because the
36
36
  * window either opened or closed, and the sessions domain recomputes for it.
37
37
  * `clock` is the same session seen again inside a window that was already open
38
38
  * — the value of an attribute, not a section anything is in — so what it asks
@@ -209,7 +209,7 @@ export class LlmRequests implements UpstreamResource {
209
209
  else if (move === "clock") this.deps.onMoved?.(sid);
210
210
  }
211
211
 
212
- /** When the gateway last saw inference for a session (§5.1). Undefined once
212
+ /** When the gateway last saw inference for a session (DESIGN §4.2). Undefined once
213
213
  * that is old enough to say nothing about whether the session is alive. */
214
214
  activeAt(sid: Sid, now: Timestamp = Date.now()): Timestamp | undefined {
215
215
  const at = this.#activeAt.get(sid);
@@ -237,7 +237,7 @@ export class LlmRequests implements UpstreamResource {
237
237
  }));
238
238
  }
239
239
 
240
- // --- UpstreamResource (§6.3). There is nothing to start: the events are
240
+ // --- UpstreamResource (DESIGN §6.3). There is nothing to start: the events are
241
241
  // pushed to this instance whether or not anyone is listening, because the
242
242
  // sessions domain reads the same arrivals for a value of its own.
243
243
 
@@ -258,7 +258,7 @@ export class LlmRequests implements UpstreamResource {
258
258
  * A session already inside its window moves its clock and nothing else, so
259
259
  * the row it lands on is restated on its own: inference is observed several
260
260
  * times a second, and recomputing the domain for each would spend the whole
261
- * of that work on one attribute of one row (§5.2). */
261
+ * of that work on one attribute of one row (DESIGN §4.3). */
262
262
  private active(sid: Sid, at: Timestamp): GatewayMove {
263
263
  const held = this.#activeAt.get(sid);
264
264
  if (held !== undefined && held >= at) return "none";
@@ -47,7 +47,7 @@ export async function readStats(
47
47
  return stats;
48
48
  }
49
49
 
50
- /** Read the gateway's document as this contract's answer (§3.5). The dates are
50
+ /** Read the gateway's document as this contract's answer (DESIGN §2.4). The dates are
51
51
  * the gateway's own keys and are not reinterpreted: a day here means whatever
52
52
  * it means there. */
53
53
  export function statsOf(value: unknown): LlmStatsReadResult | undefined {
@@ -59,7 +59,7 @@ export interface LlmStatusDeps {
59
59
 
60
60
  /** The gateway's report on the services behind it.
61
61
  *
62
- * `per_instance_whole` (§6.2): a frame replaces what this instance last said
62
+ * `per_instance_whole` (DESIGN §6.2): a frame replaces what this instance last said
63
63
  * and leaves other instances' reports alone, because the report is one document
64
64
  * the gateway behind this instance assembles and half of it means nothing on
65
65
  * its own.
@@ -85,7 +85,7 @@ export class LlmStatus implements UpstreamResource {
85
85
  this.#settling = undefined;
86
86
  void this.read();
87
87
  }, this.deps.settleMs ?? TROUBLE_SETTLE_MS);
88
- // The instance must be able to leave while this is pending (§8.5).
88
+ // The instance must be able to leave while this is pending (DESIGN §8.5).
89
89
  this.#settling.unref?.();
90
90
  }
91
91
 
@@ -99,7 +99,7 @@ export class LlmStatus implements UpstreamResource {
99
99
  await this.#reading;
100
100
  }
101
101
 
102
- // --- UpstreamResource (§6.3)
102
+ // --- UpstreamResource (DESIGN §6.3)
103
103
 
104
104
  start(): void {
105
105
  this.#listening = true;
@@ -156,7 +156,7 @@ const OFFICIAL_STATES: readonly LlmStatusOfficialState[] = [
156
156
  ];
157
157
  const OBSERVED_STATES: readonly LlmStatusObservedState[] = ["reachable", "failing", "unknown"];
158
158
 
159
- /** Read the gateway's document as this contract's report (§3.5).
159
+ /** Read the gateway's document as this contract's report (DESIGN §2.4).
160
160
  *
161
161
  * The gateway already answers in Unix ms under these names, so nothing is
162
162
  * converted — but nothing is passed through unread either: every field is
@@ -64,7 +64,7 @@ export async function readUsage(
64
64
  return usage;
65
65
  }
66
66
 
67
- /** Read the gateway's document as this contract's answer (§3.5).
67
+ /** Read the gateway's document as this contract's answer (DESIGN §2.4).
68
68
  *
69
69
  * The names that differ are the two the gateway spells its own way — `reset`
70
70
  * and `window_seconds` — and both are already Unix ms and seconds, so the
@@ -94,7 +94,7 @@ export async function handleWebhook(
94
94
  /** Whether the request presents exactly this source's token.
95
95
  *
96
96
  * Compared in constant time. The route is reachable only by whoever the entry
97
- * check of §3.1 already let through, but a comparison that leaks its prefix
97
+ * check of DESIGN §2.1 already let through, but a comparison that leaks its prefix
98
98
  * through timing is the kind of thing that quietly stops being enough once an
99
99
  * instance is bound past loopback. */
100
100
  function authorized(header: string | null, token: string): boolean {