@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.
- package/dist/index.mjs +72 -71
- package/package.json +5 -6
- package/src/check.ts +0 -478
- package/src/cli-flags.ts +0 -8
- package/src/click/wire.ts +0 -158
- package/src/config/action.ts +0 -329
- package/src/config/cli.ts +0 -71
- package/src/config/default-dsl-config.ts +0 -1645
- package/src/config/disclosure.ts +0 -170
- package/src/config/dsl-loader.ts +0 -339
- package/src/config/dsl-types.ts +0 -581
- package/src/config/edit-chrome.ts +0 -559
- package/src/config/help.ts +0 -151
- package/src/config/ident.ts +0 -22
- package/src/config/layout-ops.ts +0 -177
- package/src/config/loader/actions.ts +0 -972
- package/src/config/loader/cache.ts +0 -206
- package/src/config/loader/cross-ref.ts +0 -714
- package/src/config/loader/cycles.ts +0 -148
- package/src/config/loader/diagnostics.ts +0 -99
- package/src/config/loader/discovery.ts +0 -182
- package/src/config/loader/edit-mode.ts +0 -137
- package/src/config/loader/emit-schema.ts +0 -68
- package/src/config/loader/globals.ts +0 -269
- package/src/config/loader/helpers.ts +0 -48
- package/src/config/loader/layout.ts +0 -693
- package/src/config/loader/looks.ts +0 -96
- package/src/config/loader/menu-synth.ts +0 -435
- package/src/config/loader/merge.ts +0 -115
- package/src/config/loader/persist-target.ts +0 -67
- package/src/config/loader/presets.ts +0 -119
- package/src/config/loader/refs.ts +0 -100
- package/src/config/loader/reserved-namespace.ts +0 -38
- package/src/config/loader/segments.ts +0 -120
- package/src/config/loader/validate-core.ts +0 -737
- package/src/config/loader/variables.ts +0 -260
- package/src/config/menu-keys.ts +0 -139
- package/src/config/option-domain.ts +0 -164
- package/src/config/presets.ts +0 -326
- package/src/config/settings-menu.ts +0 -775
- package/src/daemon/acquire.ts +0 -684
- package/src/daemon/cache/git.ts +0 -649
- package/src/daemon/cache/render.ts +0 -623
- package/src/daemon/cache/session-usage-store.ts +0 -720
- package/src/daemon/cache/watchers.ts +0 -249
- package/src/daemon/client-debug.ts +0 -120
- package/src/daemon/client-stats.ts +0 -130
- package/src/daemon/client-transport.ts +0 -273
- package/src/daemon/client.ts +0 -78
- package/src/daemon/config-overrides-store.ts +0 -663
- package/src/daemon/debug-types.ts +0 -91
- package/src/daemon/debug.ts +0 -264
- package/src/daemon/fork-bomb-breaker.ts +0 -351
- package/src/daemon/limits.ts +0 -211
- package/src/daemon/log.ts +0 -81
- package/src/daemon/parent-watchdog.ts +0 -87
- package/src/daemon/paths.ts +0 -211
- package/src/daemon/process-fingerprint.ts +0 -146
- package/src/daemon/protocol.ts +0 -292
- package/src/daemon/render-payload.ts +0 -1256
- package/src/daemon/server.ts +0 -1330
- package/src/daemon/session-state-file.ts +0 -108
- package/src/daemon/session-state.ts +0 -237
- package/src/daemon/socket-lease.ts +0 -209
- package/src/daemon/socket-ownership.ts +0 -209
- package/src/daemon/stats.ts +0 -235
- package/src/daemon/verbs/config-validators.ts +0 -250
- package/src/daemon/verbs/index.ts +0 -706
- package/src/daemon/verbs/state-validators.ts +0 -249
- package/src/daemon/verbs/validator-registry.ts +0 -457
- package/src/demo/dsl.ts +0 -143
- package/src/demo/mock-data.ts +0 -67
- package/src/demo/statusline.json5 +0 -94
- package/src/dsl/node-registry.ts +0 -374
- package/src/dsl/render.ts +0 -803
- package/src/help-text.ts +0 -90
- package/src/index.ts +0 -210
- package/src/install/currency.ts +0 -197
- package/src/install/index.ts +0 -557
- package/src/proc/launch.ts +0 -459
- package/src/proc/stats-handle.ts +0 -13
- package/src/render/action.ts +0 -883
- package/src/render/active-segment.ts +0 -78
- package/src/render/diagnostic-style.ts +0 -23
- package/src/render/diagnostic-text.ts +0 -77
- package/src/render/error-glyph.ts +0 -53
- package/src/render/menu.ts +0 -257
- package/src/render/outcome-plan.ts +0 -45
- package/src/render/picker.ts +0 -372
- package/src/render/segment-color.ts +0 -74
- package/src/render/split-lines.ts +0 -51
- package/src/render/strip.ts +0 -228
- package/src/segments/cache.ts +0 -131
- package/src/segments/context.ts +0 -190
- package/src/segments/git.ts +0 -1084
- package/src/segments/metrics.ts +0 -187
- package/src/segments/pricing.ts +0 -452
- package/src/segments/session.ts +0 -23
- package/src/segments/tmux.ts +0 -74
- package/src/template-engine/cells.ts +0 -90
- package/src/template-engine/colors.ts +0 -124
- package/src/template-engine/engine.ts +0 -108
- package/src/template-engine/funcs.ts +0 -232
- package/src/template-engine/index.ts +0 -11
- package/src/template-engine/layout.ts +0 -133
- package/src/template-engine/scope.ts +0 -62
- package/src/template-engine/sparkline.ts +0 -79
- package/src/themes/index.ts +0 -20
- package/src/themes/palette-resolvers.ts +0 -84
- package/src/themes/policy.ts +0 -393
- package/src/utils/cache.ts +0 -206
- package/src/utils/claude.ts +0 -683
- package/src/utils/color-support.ts +0 -118
- package/src/utils/formatters.ts +0 -99
- package/src/utils/logger.ts +0 -5
- package/src/utils/outcome.ts +0 -33
- package/src/utils/schema-validator.ts +0 -126
- package/src/utils/single-flight.ts +0 -57
- package/src/utils/terminal-width.ts +0 -51
- package/src/utils/terminal.ts +0 -11
- package/src/utils/transcript-fs.ts +0 -279
- package/src/var-system/index.ts +0 -24
- package/src/var-system/sources.ts +0 -1047
- package/src/var-system/store.ts +0 -223
- package/src/var-system/types.ts +0 -57
- package/src/version.ts +0 -17
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
open as fsOpen,
|
|
3
|
-
readdir as fsReaddir,
|
|
4
|
-
readFile as fsReadFile,
|
|
5
|
-
stat as fsStat,
|
|
6
|
-
} from "node:fs/promises";
|
|
7
|
-
import type { FileHandle } from "node:fs/promises";
|
|
8
|
-
import { statSync } from "node:fs";
|
|
9
|
-
|
|
10
|
-
import { ABSENT, failed, ok, type Outcome } from "./outcome";
|
|
11
|
-
import { debug } from "./logger";
|
|
12
|
-
|
|
13
|
-
// [LAW:single-enforcer] The one per-render freshness probe: a SYNC single-file
|
|
14
|
-
// stat's mtimeMs (0 when the file is absent or unreadable). Sync by design — the
|
|
15
|
-
// mtime gate must resolve before deciding whether to read, and a sync stat
|
|
16
|
-
// consumes no gate slot (the async `readAppended`/`readFile` bulk reads are what
|
|
17
|
-
// the limiter bounds). Both the usage store and the metrics provider key their
|
|
18
|
-
// incremental fold off this one helper so the ENOENT→0 policy lives in one place.
|
|
19
|
-
export function statMtimeMs(filePath: string | undefined): number {
|
|
20
|
-
if (!filePath) return 0;
|
|
21
|
-
try {
|
|
22
|
-
return statSync(filePath).mtimeMs;
|
|
23
|
-
} catch {
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// [LAW:single-enforcer] One owner of the transcript-scanning in-flight-I/O
|
|
29
|
-
// budget. Every readdir/stat/readFile over the ~/.claude/projects tree passes
|
|
30
|
-
// through this module's limiter, so the number of concurrent libuv fs requests
|
|
31
|
-
// is bounded by a constant no matter how many renders fan out at once. The OOM
|
|
32
|
-
// heap proved the illegal state this forbids: ~3046 FSReqPromise pending at
|
|
33
|
-
// once, each pinning a parked await-stack.
|
|
34
|
-
//
|
|
35
|
-
// [LAW:types-are-the-program] The bound is enforced by ROUTING, not by a
|
|
36
|
-
// post-hoc guard: the transcript path imports these gated primitives instead of
|
|
37
|
-
// node:fs/promises, so "thousands of stats/reads in flight" is unrepresentable
|
|
38
|
-
// rather than merely checked. There is no `if (tooMany)` anywhere — the same
|
|
39
|
-
// fan-out runs every render; the limiter only decides *when* each op dispatches.
|
|
40
|
-
|
|
41
|
-
// 8 = 2× the libuv default UV_THREADPOOL_SIZE (4). Two dispatched ops per worker
|
|
42
|
-
// keeps every threadpool thread fed without a queue-drain stall between syscalls,
|
|
43
|
-
// while peak in-flight memory stays O(threadpool) rather than O(transcript count).
|
|
44
|
-
const TRANSCRIPT_FS_CONCURRENCY = 8;
|
|
45
|
-
|
|
46
|
-
interface Waiter {
|
|
47
|
-
readonly wake: () => void;
|
|
48
|
-
next: Waiter | null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// A counting semaphore that runs at most `max` thunks concurrently. Slots are
|
|
52
|
-
// handed off directly to the next waiter on release (never incremented while a
|
|
53
|
-
// waiter is parked), so admission can never exceed `max` — the over-admission
|
|
54
|
-
// race of an increment-then-wake design is structurally absent.
|
|
55
|
-
class Limiter {
|
|
56
|
-
private slots: number;
|
|
57
|
-
// FIFO wait queue as a singly-linked list: enqueue at `tail`, dequeue at
|
|
58
|
-
// `head`, both O(1) with no array reindexing (Array.shift would be O(n), so a
|
|
59
|
-
// drain of the thousands-of-queued-ops burst this limiter exists to absorb
|
|
60
|
-
// would be O(n²) on the render hot path). A dequeued node is immediately
|
|
61
|
-
// unreferenced, so a continuously-saturated queue retains only the waiters
|
|
62
|
-
// currently parked — no consumed-prefix accumulates, unlike a head-index
|
|
63
|
-
// array that only reclaims on full drain.
|
|
64
|
-
private head: Waiter | null = null;
|
|
65
|
-
private tail: Waiter | null = null;
|
|
66
|
-
|
|
67
|
-
constructor(max: number) {
|
|
68
|
-
this.slots = max;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async run<T>(task: () => Promise<T>): Promise<T> {
|
|
72
|
-
await this.acquire();
|
|
73
|
-
try {
|
|
74
|
-
return await task();
|
|
75
|
-
} finally {
|
|
76
|
-
this.release();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private async acquire(): Promise<void> {
|
|
81
|
-
if (this.slots > 0) {
|
|
82
|
-
this.slots--;
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
// No slot free — park until release() hands one to us. The slot is
|
|
86
|
-
// transferred directly, so we must NOT decrement again on resume.
|
|
87
|
-
await new Promise<void>((wake) => {
|
|
88
|
-
const node: Waiter = { wake, next: null };
|
|
89
|
-
if (this.tail) this.tail.next = node;
|
|
90
|
-
else this.head = node;
|
|
91
|
-
this.tail = node;
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private release(): void {
|
|
96
|
-
const node = this.head;
|
|
97
|
-
if (!node) {
|
|
98
|
-
// No one waiting — return the slot to the pool.
|
|
99
|
-
this.slots++;
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
// Hand the slot directly to the next waiter; the dequeued node is dropped.
|
|
103
|
-
this.head = node.next;
|
|
104
|
-
if (!this.head) this.tail = null;
|
|
105
|
-
node.wake();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const gate = new Limiter(TRANSCRIPT_FS_CONCURRENCY);
|
|
110
|
-
|
|
111
|
-
// Wrap an async fs primitive so every call flows through the shared gate. The
|
|
112
|
-
// `(...args: never[])` bound is the maximally-permissive function constraint
|
|
113
|
-
// (parameters are contravariant, so `never[]` accepts any arg list) — it admits
|
|
114
|
-
// every fs/promises overload, not rejects them. Callers see the original
|
|
115
|
-
// overloaded type `F`; the cast is needed because TS can't prove a generic
|
|
116
|
-
// wrapper preserves an overload set, but each fs overload is a valid `fn(...)`
|
|
117
|
-
// call, so the wrap is sound (verified: `tsc --noEmit` passes against the
|
|
118
|
-
// multi-overload call sites in claude.ts/cache.ts).
|
|
119
|
-
function gated<F extends (...args: never[]) => Promise<unknown>>(fn: F): F {
|
|
120
|
-
return ((...args: Parameters<F>) =>
|
|
121
|
-
gate.run(() => fn(...args))) as unknown as F;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export const readdir = gated(fsReaddir);
|
|
125
|
-
export const readFile = gated(fsReadFile);
|
|
126
|
-
export const stat = gated(fsStat);
|
|
127
|
-
|
|
128
|
-
// [LAW:single-enforcer] A bounded tail read through the SAME gate: the last
|
|
129
|
-
// `maxBytes` of a file (the whole file when smaller), plus whether that window
|
|
130
|
-
// reaches the file start. The open→stat→read→close runs under one gate slot, so
|
|
131
|
-
// a tail read counts as one in-flight op exactly like a readFile — a transcript
|
|
132
|
-
// scanner that grows its window backward must use THIS, not raw node:fs, or it
|
|
133
|
-
// reintroduces the unbounded-fs state the gate forbids.
|
|
134
|
-
//
|
|
135
|
-
// [LAW:no-silent-failure] The file not existing is the expected, every-render
|
|
136
|
-
// case for a fresh session (no transcript yet) — `absent`. Any other error
|
|
137
|
-
// (permissions, I/O) is a real read failure — `failed`, carrying its reason
|
|
138
|
-
// to whichever boundary owns the log effect. Folding both into one null made
|
|
139
|
-
// a broken transcript indistinguishable from a missing one.
|
|
140
|
-
// [LAW:single-enforcer] The forward complement of readTail, through the SAME
|
|
141
|
-
// gate: read the bytes of an append-only file from `priorOffset` to EOF (the
|
|
142
|
-
// whole file when `priorOffset` is undefined). One open→stat→read→close per
|
|
143
|
-
// gate slot, exactly like readTail. A transcript scanner that maintains a byte
|
|
144
|
-
// cursor to re-read only what was appended MUST use this, not raw node:fs, or it
|
|
145
|
-
// reintroduces the unbounded-fs state the gate forbids.
|
|
146
|
-
//
|
|
147
|
-
// [LAW:one-source-of-truth] `reset` is the single signal that the file shrank
|
|
148
|
-
// below the caller's cursor — a truncation or /compact rewrite — so the caller
|
|
149
|
-
// discards its prior fold for this file and re-folds from the returned bytes
|
|
150
|
-
// (which then start at offset 0). Append-only monotonicity makes every other
|
|
151
|
-
// case a pure suffix read: `start..size` is exactly what's new.
|
|
152
|
-
//
|
|
153
|
-
// [LAW:no-silent-failure] ENOENT (fresh session, no transcript yet) is `absent`;
|
|
154
|
-
// any other error is `failed`, carrying its reason to the boundary that logs.
|
|
155
|
-
export async function readAppended(
|
|
156
|
-
path: string,
|
|
157
|
-
prior: { offset: number; ino: number } | undefined,
|
|
158
|
-
): Promise<
|
|
159
|
-
Outcome<{
|
|
160
|
-
buf: Buffer;
|
|
161
|
-
start: number;
|
|
162
|
-
size: number;
|
|
163
|
-
mtimeMs: number;
|
|
164
|
-
ino: number;
|
|
165
|
-
reset: boolean;
|
|
166
|
-
}>
|
|
167
|
-
> {
|
|
168
|
-
return gate.run(async () => {
|
|
169
|
-
let fh: FileHandle | null = null;
|
|
170
|
-
try {
|
|
171
|
-
fh = await fsOpen(path, "r");
|
|
172
|
-
const { size, mtimeMs, ino } = await fh.stat();
|
|
173
|
-
// [LAW:one-source-of-truth] The prior fold is a valid PREFIX of this file
|
|
174
|
-
// only while the file stays append-only. Two independent signals break
|
|
175
|
-
// that: the inode changed (a rename-based /compact or log rotation swapped
|
|
176
|
-
// the file under the path — the realistic rewrite mechanism), or the file
|
|
177
|
-
// shrank below our cursor (an in-place truncate). Either ⇒ re-read from 0;
|
|
178
|
-
// the suffix-only read would otherwise splice new bytes onto a fold of a
|
|
179
|
-
// file that no longer exists. (A cursor-only size check missed a rewrite
|
|
180
|
-
// that grew back to ≥ the cursor.)
|
|
181
|
-
const reset =
|
|
182
|
-
prior !== undefined && (ino !== prior.ino || size < prior.offset);
|
|
183
|
-
const start = reset || prior === undefined ? 0 : prior.offset;
|
|
184
|
-
if (start >= size) {
|
|
185
|
-
return ok({ buf: Buffer.alloc(0), start, size, mtimeMs, ino, reset });
|
|
186
|
-
}
|
|
187
|
-
const buf = Buffer.alloc(size - start);
|
|
188
|
-
// [LAW:no-silent-fallbacks] A single read may return short — parsing a
|
|
189
|
-
// zero-padded tail would fabricate entries. Loop until the window fills or
|
|
190
|
-
// EOF; on a short final read (the file shrank under us) return only the
|
|
191
|
-
// bytes actually read, never the zero padding.
|
|
192
|
-
let off = 0;
|
|
193
|
-
while (off < buf.length) {
|
|
194
|
-
const { bytesRead } = await fh.read(
|
|
195
|
-
buf,
|
|
196
|
-
off,
|
|
197
|
-
buf.length - off,
|
|
198
|
-
start + off,
|
|
199
|
-
);
|
|
200
|
-
if (bytesRead === 0) break;
|
|
201
|
-
off += bytesRead;
|
|
202
|
-
}
|
|
203
|
-
return ok({
|
|
204
|
-
buf: off === buf.length ? buf : buf.subarray(0, off),
|
|
205
|
-
start,
|
|
206
|
-
size,
|
|
207
|
-
mtimeMs,
|
|
208
|
-
ino,
|
|
209
|
-
reset,
|
|
210
|
-
});
|
|
211
|
-
} catch (e) {
|
|
212
|
-
if ((e as NodeJS.ErrnoException).code === "ENOENT") return ABSENT;
|
|
213
|
-
return failed(
|
|
214
|
-
`readAppended ${path}: ${e instanceof Error ? e.message : String(e)}`,
|
|
215
|
-
);
|
|
216
|
-
} finally {
|
|
217
|
-
// [LAW:no-silent-failure] A close() rejection must NOT override the typed
|
|
218
|
-
// Outcome the try returned — callers await this and read `.kind`, so a raw
|
|
219
|
-
// rejection would escape every failed-guard. A failed close after a good
|
|
220
|
-
// read doesn't invalidate the read, but it can signal a real fs problem
|
|
221
|
-
// (ENOSPC on fsync), so log it at debug rather than swallowing it blind.
|
|
222
|
-
await fh?.close().catch((e: unknown) => {
|
|
223
|
-
debug(
|
|
224
|
-
`transcript-fs: close failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
225
|
-
);
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export async function readTail(
|
|
232
|
-
path: string,
|
|
233
|
-
maxBytes: number,
|
|
234
|
-
): Promise<Outcome<{ buf: Buffer; fromStart: boolean }>> {
|
|
235
|
-
return gate.run(async () => {
|
|
236
|
-
let fh: FileHandle | null = null;
|
|
237
|
-
try {
|
|
238
|
-
fh = await fsOpen(path, "r");
|
|
239
|
-
const { size } = await fh.stat();
|
|
240
|
-
const start = Math.max(0, size - maxBytes);
|
|
241
|
-
const buf = Buffer.alloc(size - start);
|
|
242
|
-
// [LAW:no-silent-fallbacks] A single read may return short — the scanner
|
|
243
|
-
// would then parse a zero-padded tail and miss cache activity. Loop until
|
|
244
|
-
// the window is filled or EOF; on a short final read (the file shrank
|
|
245
|
-
// under us) return only the bytes actually read, never the zero padding.
|
|
246
|
-
let off = 0;
|
|
247
|
-
while (off < buf.length) {
|
|
248
|
-
const { bytesRead } = await fh.read(
|
|
249
|
-
buf,
|
|
250
|
-
off,
|
|
251
|
-
buf.length - off,
|
|
252
|
-
start + off,
|
|
253
|
-
);
|
|
254
|
-
if (bytesRead === 0) break;
|
|
255
|
-
off += bytesRead;
|
|
256
|
-
}
|
|
257
|
-
return ok({
|
|
258
|
-
buf: off === buf.length ? buf : buf.subarray(0, off),
|
|
259
|
-
fromStart: start === 0,
|
|
260
|
-
});
|
|
261
|
-
} catch (e) {
|
|
262
|
-
if ((e as NodeJS.ErrnoException).code === "ENOENT") return ABSENT;
|
|
263
|
-
return failed(
|
|
264
|
-
`readTail ${path}: ${e instanceof Error ? e.message : String(e)}`,
|
|
265
|
-
);
|
|
266
|
-
} finally {
|
|
267
|
-
// [LAW:no-silent-failure] A close() rejection must NOT override the typed
|
|
268
|
-
// Outcome the try returned — callers await this and read `.kind`, so a raw
|
|
269
|
-
// rejection would escape every failed-guard. A failed close after a good
|
|
270
|
-
// read doesn't invalidate the read, but it can signal a real fs problem
|
|
271
|
-
// (ENOSPC on fsync), so log it at debug rather than swallowing it blind.
|
|
272
|
-
await fh?.close().catch((e: unknown) => {
|
|
273
|
-
debug(
|
|
274
|
-
`transcript-fs: close failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
275
|
-
);
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
}
|
package/src/var-system/index.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export { VariableStore, type VarNode } from "./store";
|
|
2
|
-
export {
|
|
3
|
-
type VarType,
|
|
4
|
-
type VarValue,
|
|
5
|
-
typeOf,
|
|
6
|
-
toString,
|
|
7
|
-
toNumber,
|
|
8
|
-
toBool,
|
|
9
|
-
} from "./types";
|
|
10
|
-
export {
|
|
11
|
-
SourceRegistry,
|
|
12
|
-
parseDuration,
|
|
13
|
-
formatGoTime,
|
|
14
|
-
MIN_SHELL_TTL_MS,
|
|
15
|
-
type CachePolicy,
|
|
16
|
-
type ShellOptions,
|
|
17
|
-
type FileOptions,
|
|
18
|
-
type TemplateOptions,
|
|
19
|
-
type TimeOptions,
|
|
20
|
-
type GitField,
|
|
21
|
-
type GitOptions,
|
|
22
|
-
type StateOptions,
|
|
23
|
-
type LastError,
|
|
24
|
-
} from "./sources";
|