@promptctl/cc-candybar 1.42.0 → 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/README.md +4 -0
  2. package/dist/index.mjs +81 -80
  3. package/package.json +5 -6
  4. package/src/check.ts +0 -478
  5. package/src/cli-flags.ts +0 -8
  6. package/src/click/wire.ts +0 -158
  7. package/src/config/action.ts +0 -329
  8. package/src/config/cli.ts +0 -71
  9. package/src/config/default-dsl-config.ts +0 -1645
  10. package/src/config/disclosure.ts +0 -170
  11. package/src/config/dsl-loader.ts +0 -339
  12. package/src/config/dsl-types.ts +0 -581
  13. package/src/config/edit-chrome.ts +0 -559
  14. package/src/config/help.ts +0 -151
  15. package/src/config/ident.ts +0 -22
  16. package/src/config/layout-ops.ts +0 -177
  17. package/src/config/loader/actions.ts +0 -972
  18. package/src/config/loader/cache.ts +0 -206
  19. package/src/config/loader/cross-ref.ts +0 -714
  20. package/src/config/loader/cycles.ts +0 -148
  21. package/src/config/loader/diagnostics.ts +0 -99
  22. package/src/config/loader/discovery.ts +0 -182
  23. package/src/config/loader/edit-mode.ts +0 -137
  24. package/src/config/loader/emit-schema.ts +0 -68
  25. package/src/config/loader/globals.ts +0 -269
  26. package/src/config/loader/helpers.ts +0 -48
  27. package/src/config/loader/layout.ts +0 -693
  28. package/src/config/loader/looks.ts +0 -96
  29. package/src/config/loader/menu-synth.ts +0 -435
  30. package/src/config/loader/merge.ts +0 -115
  31. package/src/config/loader/persist-target.ts +0 -67
  32. package/src/config/loader/presets.ts +0 -119
  33. package/src/config/loader/refs.ts +0 -100
  34. package/src/config/loader/reserved-namespace.ts +0 -38
  35. package/src/config/loader/segments.ts +0 -120
  36. package/src/config/loader/validate-core.ts +0 -737
  37. package/src/config/loader/variables.ts +0 -260
  38. package/src/config/menu-keys.ts +0 -139
  39. package/src/config/option-domain.ts +0 -164
  40. package/src/config/presets.ts +0 -326
  41. package/src/config/settings-menu.ts +0 -775
  42. package/src/daemon/acquire.ts +0 -684
  43. package/src/daemon/cache/git.ts +0 -649
  44. package/src/daemon/cache/render.ts +0 -623
  45. package/src/daemon/cache/session-usage-store.ts +0 -720
  46. package/src/daemon/cache/watchers.ts +0 -249
  47. package/src/daemon/client-debug.ts +0 -120
  48. package/src/daemon/client-stats.ts +0 -130
  49. package/src/daemon/client-transport.ts +0 -273
  50. package/src/daemon/client.ts +0 -78
  51. package/src/daemon/config-overrides-store.ts +0 -663
  52. package/src/daemon/debug-types.ts +0 -91
  53. package/src/daemon/debug.ts +0 -264
  54. package/src/daemon/fork-bomb-breaker.ts +0 -351
  55. package/src/daemon/limits.ts +0 -211
  56. package/src/daemon/log.ts +0 -81
  57. package/src/daemon/parent-watchdog.ts +0 -87
  58. package/src/daemon/paths.ts +0 -211
  59. package/src/daemon/process-fingerprint.ts +0 -146
  60. package/src/daemon/protocol.ts +0 -292
  61. package/src/daemon/render-payload.ts +0 -1256
  62. package/src/daemon/server.ts +0 -1330
  63. package/src/daemon/session-state-file.ts +0 -108
  64. package/src/daemon/session-state.ts +0 -237
  65. package/src/daemon/socket-lease.ts +0 -209
  66. package/src/daemon/socket-ownership.ts +0 -209
  67. package/src/daemon/stats.ts +0 -235
  68. package/src/daemon/verbs/config-validators.ts +0 -250
  69. package/src/daemon/verbs/index.ts +0 -706
  70. package/src/daemon/verbs/state-validators.ts +0 -249
  71. package/src/daemon/verbs/validator-registry.ts +0 -457
  72. package/src/demo/dsl.ts +0 -143
  73. package/src/demo/mock-data.ts +0 -67
  74. package/src/demo/statusline.json5 +0 -94
  75. package/src/dsl/node-registry.ts +0 -374
  76. package/src/dsl/render.ts +0 -803
  77. package/src/help-text.ts +0 -90
  78. package/src/index.ts +0 -210
  79. package/src/install/index.ts +0 -541
  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,273 +0,0 @@
1
- // The single client-side daemon socket round-trip: connect with a timeout,
2
- // send one framed request, await one framed response, always destroy the
3
- // socket, and classify every failure into the transient/permanent split.
4
- //
5
- // [LAW:one-type-per-behavior] This round-trip used to be implemented three
6
- // times (render/click in client.ts, stats in client-stats.ts, debug in
7
- // client-debug.ts), differing only in timeout values and which payload field
8
- // the caller wanted — configuration, not behavior. Callers now pass their
9
- // budgets and payload projector as VALUES through this one boundary; the
10
- // stats/debug paths inherited classification fixes (post-2l6) they had
11
- // silently drifted away from. [LAW:dataflow-not-control-flow] There is no
12
- // caller-identity flag here — a new caller is a new argument set, not a new
13
- // branch.
14
-
15
- import net from "node:net";
16
- import { socketPath } from "./paths";
17
- import { PROTOCOL_VERSION, ProtocolError, sendOne } from "./protocol";
18
- import type { Request, Response } from "./protocol";
19
-
20
- // Per-caller timeout policy, carried as data. The render hot path runs tight
21
- // budgets (a statusline refresh must not stall the host); operator-driven
22
- // CLIs (stats/debug) legitimately afford slower ones.
23
- export interface RoundTripBudgets {
24
- readonly connectMs: number;
25
- readonly budgetMs: number;
26
- }
27
-
28
- // [LAW:types-are-the-program] The outcome carries its own recovery
29
- // semantics. `transient` failures mean the daemon was unavailable/slow —
30
- // kicking a fresh daemon is the right response. `permanent` failures mean
31
- // the daemon refused our request semantically — kicking does NOT help
32
- // because the next daemon will refuse the same request the same way. The
33
- // caller's branch is no longer uniform: it matches on `kind` and routes
34
- // kick vs. show-error off the type, not off a stringified `reason`. This
35
- // asymmetry was missing in the previous shape and is the root of the
36
- // 452-corpse spiral (kz8.5).
37
- export type RoundTripOutcome<T> = { kind: "ok"; value: T } | FailureOutcome;
38
-
39
- export type FailureOutcome = TransientOutcome | PermanentOutcome;
40
-
41
- export interface TransientOutcome {
42
- kind: "transient";
43
- cause: "unreachable" | "timeout" | "io_error";
44
- message: string;
45
- }
46
-
47
- export type PermanentOutcome =
48
- | {
49
- kind: "permanent";
50
- cause: "version_mismatch";
51
- clientV: number;
52
- daemonV: number;
53
- }
54
- | { kind: "permanent"; cause: "bad_request"; message: string }
55
- | { kind: "permanent"; cause: "render_failed"; message: string }
56
- | { kind: "permanent"; cause: "malformed_response"; message: string };
57
-
58
- // [LAW:single-enforcer] The protocol version is stamped here, on every
59
- // outbound request — a caller cannot mis-stamp or omit it.
60
- type Unversioned<R> = R extends { v: number } ? Omit<R, "v"> : never;
61
- export type UnversionedRequest = Unversioned<Request>;
62
-
63
- type OkResponse = Extract<Response, { ok: true }>;
64
-
65
- // One round-trip, classified. `project` extracts the caller's payload from
66
- // an ok response and returns undefined when the response, though ok, does
67
- // not carry the expected shape (the daemon is up, it just answered the
68
- // wrong question) — that maps to permanent/malformed_response, not a kick.
69
- // The payload types themselves never enter this module [LAW:one-way-deps].
70
- export async function requestOutcome<T>(
71
- req: UnversionedRequest,
72
- budgets: RoundTripBudgets,
73
- project: (resp: OkResponse) => T | undefined,
74
- ): Promise<RoundTripOutcome<T>> {
75
- let sock: net.Socket | null = null;
76
- try {
77
- sock = await connectWithTimeout(socketPath(), budgets.connectMs);
78
- const resp: Response = await sendOne(
79
- sock,
80
- { v: PROTOCOL_VERSION, ...req },
81
- budgets.budgetMs,
82
- );
83
- return interpretResponse(req.kind, resp, project);
84
- } catch (e) {
85
- return interpretException(e);
86
- } finally {
87
- if (sock) sock.destroy();
88
- }
89
- }
90
-
91
- // [LAW:types-are-the-program] One place that turns a wire-level Response
92
- // into a typed Outcome. Every daemon client goes through this, so the
93
- // kick-vs-show-error decision has a single source of truth.
94
- //
95
- // [LAW:no-defensive-null-guards] This function sits AT the trust boundary —
96
- // `resp` is `frame as Response`, an unchecked cast from socket JSON. The
97
- // per-field type-narrowings and the default branch below are not defensive
98
- // guards against an internal bug; they are the explicit handling at the
99
- // wire edge for fields whose runtime types the JSON cast cannot enforce.
100
- // Every untrusted access flows through asString/asProtocolVersion so the
101
- // downstream Outcome shape carries values of the declared types only.
102
-
103
- // [LAW:single-enforcer] Narrowing primitives used everywhere we read a
104
- // field off the cast `resp`. Centralised so a future "validate the whole
105
- // frame" approach has one place to evolve from. Each helper expresses an
106
- // exact type predicate; mixing semantics (e.g. "any finite number" with
107
- // "non-negative integer") would weaken the type [LAW:types-are-the-program].
108
- function asString(v: unknown, fallback: string): string {
109
- return typeof v === "string" ? v : fallback;
110
- }
111
-
112
- // [LAW:one-type-per-behavior] Mirrors the Rust client's `as_u64()` semantics:
113
- // the only valid daemonV values are non-negative integers. Negatives and
114
- // fractional values fall back to 0 so both runtimes derive the same
115
- // PermanentOutcome from the same wire payload.
116
- function asProtocolVersion(v: unknown): number {
117
- return typeof v === "number" && Number.isInteger(v) && v >= 0 ? v : 0;
118
- }
119
-
120
- function interpretResponse<T>(
121
- kind: Request["kind"],
122
- resp: Response,
123
- project: (resp: OkResponse) => T | undefined,
124
- ): RoundTripOutcome<T> {
125
- // Treat the cast `resp` as a bag of unknowns; each access is narrowed
126
- // explicitly. The typed parameter still documents the *expected* shape
127
- // for readers, but the runtime trusts only what it can verify per field.
128
- const raw = resp as {
129
- ok?: unknown;
130
- error?: unknown;
131
- code?: unknown;
132
- daemonV?: unknown;
133
- };
134
- if (raw.ok === true) {
135
- const value = project(resp as OkResponse);
136
- if (value !== undefined) {
137
- return { kind: "ok", value };
138
- }
139
- // Ok response without the payload this request asked for — not our
140
- // shape. Treat as a permanent malformed-response so the caller does NOT
141
- // kick (the daemon is up, just answered the wrong question).
142
- return {
143
- kind: "permanent",
144
- cause: "malformed_response",
145
- message: `ok response without payload for "${kind}" request`,
146
- };
147
- }
148
- const errorMessage = asString(raw.error, "(no error message)");
149
- switch (raw.code) {
150
- case "VERSION_MISMATCH":
151
- return {
152
- kind: "permanent",
153
- cause: "version_mismatch",
154
- clientV: PROTOCOL_VERSION,
155
- // Older daemons may not echo daemonV; non-number values from a
156
- // misbehaving stub also fall back to 0 (the renderer maps 0 to
157
- // "unknown" in the visible glyph).
158
- daemonV: asProtocolVersion(raw.daemonV),
159
- };
160
- case "TIMEOUT":
161
- // Daemon is alive but didn't answer in time — same recovery as
162
- // unreachable: kick and emit stale/blank. This is the *only* non-ok
163
- // wire code that maps to transient.
164
- return { kind: "transient", cause: "timeout", message: errorMessage };
165
- case "BAD_REQUEST":
166
- return { kind: "permanent", cause: "bad_request", message: errorMessage };
167
- case "RENDER_FAILED":
168
- return {
169
- kind: "permanent",
170
- cause: "render_failed",
171
- message: errorMessage,
172
- };
173
- default:
174
- // Unknown wire code — mirrors rust-client's `_ => MalformedResponse(...)`
175
- // so both runtimes converge on the same observable behavior for any
176
- // code the client doesn't recognize. String() handles missing /
177
- // non-string values without crashing.
178
- return {
179
- kind: "permanent",
180
- cause: "malformed_response",
181
- message: `unknown error code: ${String(raw.code)}`,
182
- };
183
- }
184
- }
185
-
186
- // Exceptions reaching this function come from two distinct classes:
187
- // - Connect/IO/timeout failures — transient. A respawn or retry has a
188
- // real chance of recovering (daemon dead, socket vanished, slow link).
189
- // - Protocol violations from sendOne's reject path — permanent. The
190
- // daemon is alive but produced garbage (oversized frame, JSON parse
191
- // failure). Respawning would hit the same response identically; this
192
- // is the same recovery class as a wire-level VERSION_MISMATCH, so we
193
- // route through PermanentCause::MalformedResponse and the user sees
194
- // a glyph naming the failure rather than a blank line plus a kick.
195
- //
196
- // [LAW:types-are-the-program] The protocol-violation discriminator is
197
- // carried structurally by `ProtocolError` (exported from protocol.ts) —
198
- // not by substring-matching the error message, which would silently drift
199
- // if Node's JSON.parse wording changes across versions/locales. The
200
- // `e instanceof SyntaxError` fallback covers a residual case where a
201
- // JSON.parse happened to escape `makeFrameReader`'s wrapping (defense in
202
- // depth, not the primary path).
203
- //
204
- // [LAW:one-type-per-behavior] Mirrors rust-client's classify_io_error —
205
- // InvalidData/InvalidInput map to Permanent(MalformedResponse), everything
206
- // else stays transient. The two runtimes agree on the recovery class for
207
- // every observable wire failure.
208
- function interpretException(e: unknown): FailureOutcome {
209
- if (e instanceof ProtocolError || e instanceof SyntaxError) {
210
- const message = e instanceof Error ? e.message : String(e);
211
- return { kind: "permanent", cause: "malformed_response", message };
212
- }
213
- const message = e instanceof Error ? e.message : String(e);
214
- if (message === "CONNECT_TIMEOUT" || message === "TIMEOUT") {
215
- return { kind: "transient", cause: "timeout", message };
216
- }
217
- if (
218
- message.includes("ECONNREFUSED") ||
219
- message.includes("ENOENT") ||
220
- message.includes("ENOTSOCK")
221
- ) {
222
- return { kind: "transient", cause: "unreachable", message };
223
- }
224
- return { kind: "transient", cause: "io_error", message };
225
- }
226
-
227
- function connectWithTimeout(
228
- path: string,
229
- timeoutMs: number,
230
- ): Promise<net.Socket> {
231
- return new Promise((resolve, reject) => {
232
- const sock = net.createConnection({ path });
233
- const timer = setTimeout(() => {
234
- sock.destroy();
235
- reject(new Error("CONNECT_TIMEOUT"));
236
- }, timeoutMs);
237
- sock.once("connect", () => {
238
- clearTimeout(timer);
239
- resolve(sock);
240
- });
241
- sock.once("error", (err) => {
242
- clearTimeout(timer);
243
- reject(err);
244
- });
245
- });
246
- }
247
-
248
- // [LAW:single-enforcer] One plain-text rendering of a failed outcome for
249
- // operator-facing CLIs (daemon-stats, vars/segments/config). The statusline
250
- // glyph (src/render/error-glyph.ts) and the url-handle formatter
251
- // (src/install/index.ts) are deliberately separate presentations with their
252
- // own contracts (ANSI styling and Rust-parity truncation; per-cause click
253
- // diagnostics). The spawn hint appears only on transient failures — on a
254
- // permanent failure the daemon is demonstrably running, and suggesting a
255
- // respawn would send the operator down the wrong path.
256
- export function describeFailure(outcome: FailureOutcome): string {
257
- if (outcome.kind === "transient") {
258
- return (
259
- `daemon unavailable (${outcome.cause}: ${outcome.message})\n` +
260
- "Hint: daemon may not be running. Run `cc-candybar` once to spawn it."
261
- );
262
- }
263
- switch (outcome.cause) {
264
- case "version_mismatch": {
265
- const daemon = outcome.daemonV === 0 ? "unknown" : `v${outcome.daemonV}`;
266
- return `daemon protocol mismatch (client v${outcome.clientV} ≠ daemon ${daemon})`;
267
- }
268
- case "bad_request":
269
- case "render_failed":
270
- case "malformed_response":
271
- return `daemon error (${outcome.cause}): ${outcome.message}`;
272
- }
273
- }
@@ -1,78 +0,0 @@
1
- // The render/click client — the statusline hot path. The socket round-trip
2
- // and failure classification live in ./client-transport (the single
3
- // implementation shared with the stats/debug CLIs); this module contributes
4
- // only the render path's own data: tight timeout budgets and the
5
- // output-string payload projection. [LAW:dataflow-not-control-flow]
6
- //
7
- // [LAW:one-source-of-truth] These budget consts are mirrored by the Rust
8
- // client (rust-client/src/main.rs) and diffed by scripts/check-protocol.mjs,
9
- // which anchors on the declarations below — keep them named consts in this
10
- // file, or repoint the CHECKS rows in the same commit.
11
-
12
- import type { ClaudeHookData } from "../utils/claude";
13
- import { requestOutcome } from "./client-transport";
14
- import type { RoundTripBudgets, RoundTripOutcome } from "./client-transport";
15
- import type { ClientHints, Response } from "./protocol";
16
-
17
- const CONNECT_TIMEOUT_MS = 50;
18
- const TOTAL_BUDGET_MS = 150;
19
- const CLICK_BUDGET_MS = 200;
20
-
21
- const RENDER_BUDGETS: RoundTripBudgets = {
22
- connectMs: CONNECT_TIMEOUT_MS,
23
- budgetMs: TOTAL_BUDGET_MS,
24
- };
25
- const CLICK_BUDGETS: RoundTripBudgets = {
26
- connectMs: CONNECT_TIMEOUT_MS,
27
- budgetMs: CLICK_BUDGET_MS,
28
- };
29
-
30
- // The render/click outcome vocabulary, mirrored by the Rust client's outcome
31
- // enum. The ok payload is the rendered line (or click acknowledgement) to
32
- // print. See client-transport.ts for the transient/permanent semantics.
33
- export type ClientOutcome = RoundTripOutcome<string>;
34
-
35
- // [LAW:no-defensive-null-guards] exception: trust boundary. The ok response
36
- // is an unchecked cast from socket JSON; the typeof check is the explicit
37
- // narrowing at the wire edge, and its failure means "ok response without our
38
- // payload" (classified permanent/malformed_response by the transport).
39
- function projectOutput(
40
- resp: Extract<Response, { ok: true }>,
41
- ): string | undefined {
42
- const output = (resp as { output?: unknown }).output;
43
- return typeof output === "string" ? output : undefined;
44
- }
45
-
46
- // Try to render via the daemon. Returns a typed outcome — see ClientOutcome.
47
- // There is no inline render path; see src/index.ts. The caller is responsible
48
- // for branching on outcome.kind and deciding whether to kick, display an
49
- // error glyph, or print the rendered output.
50
- // [LAW:one-source-of-truth] `hints` carries every fact the daemon cannot
51
- // observe for itself; it is spread onto the request verbatim so this relay
52
- // never becomes a second place that decides what the client saw.
53
- export function tryRenderViaDaemon(
54
- hookData: ClaudeHookData,
55
- args: string[],
56
- cwd: string,
57
- hints: ClientHints,
58
- ): Promise<ClientOutcome> {
59
- return requestOutcome(
60
- { kind: "render", hookData, args, cwd, ...hints },
61
- RENDER_BUDGETS,
62
- projectOutput,
63
- );
64
- }
65
-
66
- // [LAW:single-enforcer] Same outcome translator for click as for render —
67
- // click failures decompose into the same transient/permanent split, so the
68
- // caller's "ok? done : kick + fallback" logic gets the same typed input.
69
- export function tryClickViaDaemon(
70
- verb: string,
71
- value: string,
72
- ): Promise<ClientOutcome> {
73
- return requestOutcome(
74
- { kind: "click", verb, value },
75
- CLICK_BUDGETS,
76
- projectOutput,
77
- );
78
- }