@promptctl/cc-candybar 1.42.1 → 1.43.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.
Files changed (126) hide show
  1. package/dist/index.mjs +72 -71
  2. package/package.json +5 -6
  3. package/src/check.ts +0 -478
  4. package/src/cli-flags.ts +0 -8
  5. package/src/click/wire.ts +0 -158
  6. package/src/config/action.ts +0 -329
  7. package/src/config/cli.ts +0 -71
  8. package/src/config/default-dsl-config.ts +0 -1645
  9. package/src/config/disclosure.ts +0 -170
  10. package/src/config/dsl-loader.ts +0 -339
  11. package/src/config/dsl-types.ts +0 -581
  12. package/src/config/edit-chrome.ts +0 -559
  13. package/src/config/help.ts +0 -151
  14. package/src/config/ident.ts +0 -22
  15. package/src/config/layout-ops.ts +0 -177
  16. package/src/config/loader/actions.ts +0 -972
  17. package/src/config/loader/cache.ts +0 -206
  18. package/src/config/loader/cross-ref.ts +0 -714
  19. package/src/config/loader/cycles.ts +0 -148
  20. package/src/config/loader/diagnostics.ts +0 -99
  21. package/src/config/loader/discovery.ts +0 -182
  22. package/src/config/loader/edit-mode.ts +0 -137
  23. package/src/config/loader/emit-schema.ts +0 -68
  24. package/src/config/loader/globals.ts +0 -269
  25. package/src/config/loader/helpers.ts +0 -48
  26. package/src/config/loader/layout.ts +0 -693
  27. package/src/config/loader/looks.ts +0 -96
  28. package/src/config/loader/menu-synth.ts +0 -435
  29. package/src/config/loader/merge.ts +0 -115
  30. package/src/config/loader/persist-target.ts +0 -67
  31. package/src/config/loader/presets.ts +0 -119
  32. package/src/config/loader/refs.ts +0 -100
  33. package/src/config/loader/reserved-namespace.ts +0 -38
  34. package/src/config/loader/segments.ts +0 -120
  35. package/src/config/loader/validate-core.ts +0 -737
  36. package/src/config/loader/variables.ts +0 -260
  37. package/src/config/menu-keys.ts +0 -139
  38. package/src/config/option-domain.ts +0 -164
  39. package/src/config/presets.ts +0 -326
  40. package/src/config/settings-menu.ts +0 -775
  41. package/src/daemon/acquire.ts +0 -684
  42. package/src/daemon/cache/git.ts +0 -649
  43. package/src/daemon/cache/render.ts +0 -623
  44. package/src/daemon/cache/session-usage-store.ts +0 -720
  45. package/src/daemon/cache/watchers.ts +0 -249
  46. package/src/daemon/client-debug.ts +0 -120
  47. package/src/daemon/client-stats.ts +0 -130
  48. package/src/daemon/client-transport.ts +0 -273
  49. package/src/daemon/client.ts +0 -78
  50. package/src/daemon/config-overrides-store.ts +0 -663
  51. package/src/daemon/debug-types.ts +0 -91
  52. package/src/daemon/debug.ts +0 -264
  53. package/src/daemon/fork-bomb-breaker.ts +0 -351
  54. package/src/daemon/limits.ts +0 -211
  55. package/src/daemon/log.ts +0 -81
  56. package/src/daemon/parent-watchdog.ts +0 -87
  57. package/src/daemon/paths.ts +0 -211
  58. package/src/daemon/process-fingerprint.ts +0 -146
  59. package/src/daemon/protocol.ts +0 -292
  60. package/src/daemon/render-payload.ts +0 -1256
  61. package/src/daemon/server.ts +0 -1330
  62. package/src/daemon/session-state-file.ts +0 -108
  63. package/src/daemon/session-state.ts +0 -237
  64. package/src/daemon/socket-lease.ts +0 -209
  65. package/src/daemon/socket-ownership.ts +0 -209
  66. package/src/daemon/stats.ts +0 -235
  67. package/src/daemon/verbs/config-validators.ts +0 -250
  68. package/src/daemon/verbs/index.ts +0 -706
  69. package/src/daemon/verbs/state-validators.ts +0 -249
  70. package/src/daemon/verbs/validator-registry.ts +0 -457
  71. package/src/demo/dsl.ts +0 -143
  72. package/src/demo/mock-data.ts +0 -67
  73. package/src/demo/statusline.json5 +0 -94
  74. package/src/dsl/node-registry.ts +0 -374
  75. package/src/dsl/render.ts +0 -803
  76. package/src/help-text.ts +0 -90
  77. package/src/index.ts +0 -210
  78. package/src/install/currency.ts +0 -197
  79. package/src/install/index.ts +0 -557
  80. package/src/proc/launch.ts +0 -459
  81. package/src/proc/stats-handle.ts +0 -13
  82. package/src/render/action.ts +0 -883
  83. package/src/render/active-segment.ts +0 -78
  84. package/src/render/diagnostic-style.ts +0 -23
  85. package/src/render/diagnostic-text.ts +0 -77
  86. package/src/render/error-glyph.ts +0 -53
  87. package/src/render/menu.ts +0 -257
  88. package/src/render/outcome-plan.ts +0 -45
  89. package/src/render/picker.ts +0 -372
  90. package/src/render/segment-color.ts +0 -74
  91. package/src/render/split-lines.ts +0 -51
  92. package/src/render/strip.ts +0 -228
  93. package/src/segments/cache.ts +0 -131
  94. package/src/segments/context.ts +0 -190
  95. package/src/segments/git.ts +0 -1084
  96. package/src/segments/metrics.ts +0 -187
  97. package/src/segments/pricing.ts +0 -452
  98. package/src/segments/session.ts +0 -23
  99. package/src/segments/tmux.ts +0 -74
  100. package/src/template-engine/cells.ts +0 -90
  101. package/src/template-engine/colors.ts +0 -124
  102. package/src/template-engine/engine.ts +0 -108
  103. package/src/template-engine/funcs.ts +0 -232
  104. package/src/template-engine/index.ts +0 -11
  105. package/src/template-engine/layout.ts +0 -133
  106. package/src/template-engine/scope.ts +0 -62
  107. package/src/template-engine/sparkline.ts +0 -79
  108. package/src/themes/index.ts +0 -20
  109. package/src/themes/palette-resolvers.ts +0 -84
  110. package/src/themes/policy.ts +0 -393
  111. package/src/utils/cache.ts +0 -206
  112. package/src/utils/claude.ts +0 -683
  113. package/src/utils/color-support.ts +0 -118
  114. package/src/utils/formatters.ts +0 -99
  115. package/src/utils/logger.ts +0 -5
  116. package/src/utils/outcome.ts +0 -33
  117. package/src/utils/schema-validator.ts +0 -126
  118. package/src/utils/single-flight.ts +0 -57
  119. package/src/utils/terminal-width.ts +0 -51
  120. package/src/utils/terminal.ts +0 -11
  121. package/src/utils/transcript-fs.ts +0 -279
  122. package/src/var-system/index.ts +0 -24
  123. package/src/var-system/sources.ts +0 -1047
  124. package/src/var-system/store.ts +0 -223
  125. package/src/var-system/types.ts +0 -57
  126. package/src/version.ts +0 -17
@@ -1,292 +0,0 @@
1
- import type { Socket } from "node:net";
2
- import type { ClaudeHookData } from "../utils/claude";
3
- import type { StatsSnapshot } from "./stats";
4
- import type { DebugSnapshot, DebugWhat } from "./debug-types";
5
-
6
- // [LAW:types-are-the-program] PROTOCOL_VERSION encodes one thing:
7
- // "old-client × new-daemon (or vice versa) cannot communicate." It moves on
8
- // BREAKING changes only — never on additive ones. The two cases are
9
- // genuinely different theorems and the version field carries the stronger:
10
- // incompatibility, not growth.
11
- //
12
- // **Additive** (no bump):
13
- // - Adding a new request `kind`. Old daemons reject the unknown kind via
14
- // the existing BAD_REQUEST fallthrough; old clients never send it.
15
- // - Adding a new optional field that older parsers ignore safely.
16
- // - Adding a new response variant produced only in response to a new kind.
17
- //
18
- // **Breaking** (bump):
19
- // - Changing the semantics or required shape of an existing kind.
20
- // - Removing a kind or field old clients depend on.
21
- // - Renaming a wire field.
22
- //
23
- // The 452-corpse precedent (kz8.5) makes this discipline load-bearing: every
24
- // bump forces every running statusbar through VERSION_MISMATCH until its
25
- // session restarts, because the spiral-breaker contract refuses to kick on a
26
- // permanent error. A bump for an additive change taxes every user with
27
- // blank-statusbar minutes for a feature their session doesn't even use.
28
- // Don't bump for growth.
29
- export const PROTOCOL_VERSION = 3;
30
-
31
- export interface RenderRequest {
32
- v: number;
33
- kind: "render";
34
- hookData: ClaudeHookData;
35
- args: string[];
36
- cwd: string;
37
- // ─── Client hints ────────────────────────────────────────────────────────
38
- // [LAW:single-enforcer] Facts only the LIVE CLIENT can observe, captured at
39
- // the trust boundary and trusted by the daemon. The daemon is detached and
40
- // one-per-user, so its own env answers for whichever shell spawned it —
41
- // possibly a different session, possibly hours ago. Every field below is
42
- // typed here but arrives as untrusted JSON: callers MUST route the request
43
- // through parseClientHints at the receive boundary, never read these
44
- // directly. See the ClientHints doc block for the absence semantics.
45
- termCols?: number;
46
- ssh?: boolean;
47
- }
48
-
49
- // [LAW:locality-or-seam] The seam for "a fact the daemon cannot observe about
50
- // the session it is rendering for". `termCols` established the pattern; `ssh`
51
- // is the second member, and the documented-but-unbuilt client-aware
52
- // `colorCompatibility: "auto"` is the next. Naming the set as ONE type is what
53
- // keeps that third addition a field rather than another sanitizer, another
54
- // wire read, and another parameter threaded through the render path.
55
- //
56
- // [LAW:parse-dont-validate] This is the stamped type. `RenderRequest`'s
57
- // same-named fields are raw JSON of unknown provenance; a `ClientHints` has
58
- // crossed the checkpoint, so nothing downstream re-checks them.
59
- //
60
- // [LAW:types-are-the-program] Both fields are optional, but they mean
61
- // DIFFERENT things by absence, and each is the strongest true theorem for its
62
- // own fact:
63
- // • `termCols` absent — the client tried and could not determine a width
64
- // (no COLUMNS, no TTY on stderr). A genuine "unknown", reachable from any
65
- // client version.
66
- // • `ssh` absent — the client did not REPORT. A current client always knows
67
- // (its own env is total on this question) and so always sends `true` or
68
- // `false`; absence therefore means one thing only: a client too old to
69
- // carry the field — a real case, because `cc-candybar install` stages a
70
- // native binary that does not turn over with the npm package. Collapsing
71
- // that to `false` here would fuse "we know it's local" with "we don't
72
- // know" ([LAW:no-silent-failure]); instead it travels onward as an absent
73
- // payload field, where the DSL input-fallback chain emits the declared
74
- // default AND records a `last_error` that `cc-candybar debug vars`
75
- // surfaces.
76
- export interface ClientHints {
77
- readonly termCols?: number;
78
- readonly ssh?: boolean;
79
- }
80
-
81
- // [LAW:single-enforcer] The ONE checkpoint where wire-supplied client hints
82
- // become trusted values. Per-field sanitizers stay separate (each fact has its
83
- // own validity rule) but nothing outside this function calls them, so a new
84
- // hint cannot reach the render path un-sanitized.
85
- export function parseClientHints(req: RenderRequest): ClientHints {
86
- const termCols = sanitizeTermCols(req.termCols);
87
- const ssh = sanitizeSsh(req.ssh);
88
- return {
89
- ...(termCols !== undefined && { termCols }),
90
- ...(ssh !== undefined && { ssh }),
91
- };
92
- }
93
-
94
- // [LAW:no-defensive-null-guards] exception: trust boundary. The wire is
95
- // untrusted JSON; downstream code treats termCols as an integer in a sane
96
- // range. Validate once here so the type's promise is true.
97
- //
98
- // Pathologically large values are capped (not rejected) so a future
99
- // genuinely-huge terminal still renders — 10000 is two orders of magnitude
100
- // above the largest plausible real terminal.
101
- const MAX_TERM_COLS = 10000;
102
- export function sanitizeTermCols(v: unknown): number | undefined {
103
- if (typeof v !== "number") return undefined;
104
- if (!Number.isFinite(v)) return undefined;
105
- const n = Math.floor(v);
106
- if (n <= 0) return undefined;
107
- return n > MAX_TERM_COLS ? MAX_TERM_COLS : n;
108
- }
109
-
110
- // [LAW:no-defensive-null-guards] exception: trust boundary, same shape as
111
- // sanitizeTermCols. A non-boolean (absent, or a malformed/hostile frame) is
112
- // NOT coerced to `false` — the three wire states stay three
113
- // ([LAW:no-silent-failure]): true, false, and "no answer from this client".
114
- export function sanitizeSsh(v: unknown): boolean | undefined {
115
- return typeof v === "boolean" ? v : undefined;
116
- }
117
-
118
- export interface ShutdownRequest {
119
- v: number;
120
- kind: "shutdown";
121
- }
122
-
123
- export interface ClickRequest {
124
- v: number;
125
- kind: "click";
126
- verb: string;
127
- value: string;
128
- }
129
-
130
- export interface StatsRequest {
131
- v: number;
132
- kind: "stats";
133
- }
134
-
135
- // [LAW:types-are-the-program] The `what` discriminator carries the response
136
- // shape forward — a client requesting "vars" gets vars data, "segments" gets
137
- // segments data, "config" gets config data. No string-keyed lookup on the
138
- // client side; route by structure. See DebugSnapshot in ./debug-types.
139
- export interface DebugRequest {
140
- v: number;
141
- kind: "debug";
142
- what: DebugWhat;
143
- }
144
-
145
- export type Request =
146
- | RenderRequest
147
- | ShutdownRequest
148
- | StatsRequest
149
- | ClickRequest
150
- | DebugRequest;
151
-
152
- export type Response =
153
- | { ok: true; output: string }
154
- | { ok: true; stats: StatsSnapshot }
155
- // [LAW:types-are-the-program] DebugSnapshot is itself a discriminated union
156
- // on `what`, so the response type carries the requested kind through to the
157
- // client. A `{ ok: true; debug: { what: "vars"; vars: [...] } }` shape is
158
- // self-describing — the client doesn't need to remember which `what` it sent.
159
- | { ok: true; debug: DebugSnapshot }
160
- // [LAW:types-are-the-program] `daemonV` is the daemon's own
161
- // PROTOCOL_VERSION, echoed on every error response so the client can render
162
- // a meaningful diagnostic on VERSION_MISMATCH without parsing the human
163
- // message. Optional for back-compat: older daemons (or test stubs) that
164
- // omit it are still parseable by current clients.
165
- | { ok: false; error: string; code: ErrorCode; daemonV?: number };
166
-
167
- // [LAW:types-are-the-program] The wire-level discriminator splits failures
168
- // into two recovery classes: TIMEOUT is transient — the daemon is alive but
169
- // slow, so a respawn or retry has a real chance of recovering. Every other
170
- // code is permanent — respawning would hit the same response identically
171
- // (the daemon refuses the request for VERSION_MISMATCH or BAD_REQUEST, or
172
- // fails internally for RENDER_FAILED in a way the spawn loop cannot cure),
173
- // so the spiral-breaker contract requires the client NOT to kick. The
174
- // kick-vs-no-kick decision is encoded off this code, not off the error
175
- // string.
176
- export type ErrorCode =
177
- | "VERSION_MISMATCH"
178
- | "TIMEOUT"
179
- | "RENDER_FAILED"
180
- | "BAD_REQUEST";
181
-
182
- // [LAW:types-are-the-program] A typed error class for failures that originate
183
- // inside the wire-protocol layer (oversized frame, JSON decode failure).
184
- // Callers in src/daemon/client-transport.ts branch on `e instanceof ProtocolError`
185
- // to classify these as `permanent/malformed_response` — far more robust than
186
- // substring-matching against the message, which would silently drift if
187
- // Node's JSON.parse error wording changes. The class is small but
188
- // load-bearing: it makes the kick-vs-show-error decision a structural
189
- // property of the thrown value, not a property of its english string.
190
- export class ProtocolError extends Error {
191
- constructor(message: string) {
192
- super(message);
193
- this.name = "ProtocolError";
194
- }
195
- }
196
-
197
- // 4-byte big-endian length prefix + UTF-8 JSON body. Length-prefix beats
198
- // newline-delimited because error messages may contain embedded newlines and
199
- // we'd rather not parse them out of-band.
200
- //
201
- // [LAW:one-source-of-truth] FRAME_HEADER_BYTES and MAX_FRAME_BYTES are part
202
- // of the wire contract the Rust client mirrors (rust-client/src/main.rs);
203
- // scripts/check-protocol.mjs diffs them, so they must stay named consts.
204
- export const FRAME_HEADER_BYTES = 4;
205
- export const MAX_FRAME_BYTES = 16 * 1024 * 1024;
206
-
207
- export function encodeFrame(value: unknown): Buffer {
208
- const body = Buffer.from(JSON.stringify(value), "utf8");
209
- const header = Buffer.alloc(FRAME_HEADER_BYTES);
210
- header.writeUInt32BE(body.length, 0);
211
- return Buffer.concat([header, body]);
212
- }
213
-
214
- // Streaming frame reader. Calls `onFrame` for each complete frame. Caller
215
- // owns lifecycle — call `feed` with each chunk; reader keeps a buffer.
216
- export function makeFrameReader(
217
- onFrame: (frame: unknown) => void,
218
- onError: (err: Error) => void,
219
- ) {
220
- let buf = Buffer.alloc(0);
221
- return function feed(chunk: Buffer): void {
222
- buf = Buffer.concat([buf, chunk]);
223
- while (buf.length >= FRAME_HEADER_BYTES) {
224
- const len = buf.readUInt32BE(0);
225
- // Hard cap to defend against a runaway sender allocating gigabytes.
226
- // [LAW:types-are-the-program] ProtocolError carries the discriminator
227
- // structurally — interpretException routes on `instanceof`, not on a
228
- // brittle string match against the message body.
229
- if (len > MAX_FRAME_BYTES) {
230
- onError(new ProtocolError(`frame too large: ${len}`));
231
- return;
232
- }
233
- if (buf.length < FRAME_HEADER_BYTES + len) return;
234
- const body = buf.subarray(FRAME_HEADER_BYTES, FRAME_HEADER_BYTES + len);
235
- buf = buf.subarray(FRAME_HEADER_BYTES + len);
236
- try {
237
- onFrame(JSON.parse(body.toString("utf8")));
238
- } catch (e) {
239
- // JSON.parse throws SyntaxError; wrap as ProtocolError so the
240
- // recovery class is structurally typed (not message-matched).
241
- // Preserve the original cause for diagnostic logging.
242
- const wrapped = new ProtocolError(
243
- e instanceof Error ? e.message : String(e),
244
- );
245
- if (e instanceof Error) {
246
- wrapped.cause = e;
247
- }
248
- onError(wrapped);
249
- return;
250
- }
251
- }
252
- };
253
- }
254
-
255
- // Send one frame and await one response, with a hard total budget. Resolves
256
- // to the parsed response or rejects on timeout / parse error / socket error.
257
- export function sendOne(
258
- sock: Socket,
259
- req: Request,
260
- totalBudgetMs: number,
261
- ): Promise<Response> {
262
- return new Promise((resolve, reject) => {
263
- let settled = false;
264
- const finish = (fn: () => void) => {
265
- if (settled) return;
266
- settled = true;
267
- clearTimeout(timer);
268
- sock.removeAllListeners();
269
- try {
270
- fn();
271
- } catch {}
272
- };
273
- const timer = setTimeout(() => {
274
- finish(() => {
275
- sock.destroy();
276
- reject(new Error("TIMEOUT"));
277
- });
278
- }, totalBudgetMs);
279
-
280
- const reader = makeFrameReader(
281
- (frame) => finish(() => resolve(frame as Response)),
282
- (err) => finish(() => reject(err)),
283
- );
284
- sock.on("data", reader);
285
- sock.on("error", (err) => finish(() => reject(err)));
286
- sock.on("close", () =>
287
- finish(() => reject(new Error("socket closed before response"))),
288
- );
289
-
290
- sock.write(encodeFrame(req));
291
- });
292
- }