@promptctl/cc-candybar 1.154.0 → 1.155.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.
@@ -1,2 +1,2 @@
1
- cc-extra ec94ee89583246d1b7ef577c71500a2151980ad5
1
+ cc-extra b6134fb6e35aae96fe984b37f63d9c25f67912f8
2
2
  Copied by scripts/vendor-cc-extra.mjs; do not edit these files here.
@@ -19,7 +19,8 @@
19
19
  // one as its chunk parses (MEASURED: `breakpointResolved` at the predicted line and column).
20
20
  // [LAW:no-ambient-temporal-coupling] the order is this sequence's, and two waits in it are on a clock,
21
21
  // each with one owner: the socket's appearance, which has a deadline, and the question asked of the
22
- // released process (WHY IT STAYS CONNECTED), which is asked once, at ASK_AFTER_MS.
22
+ // released process (WHY IT STAYS CONNECTED), which is asked again and again until it is a TUI or
23
+ // ASK_WITHIN_MS has passed.
23
24
  //
24
25
  // WHY A UNIX SOCKET. The inspector runs arbitrary code in the session. On TCP it is reachable by every
25
26
  // process on the machine; a socket in a directory only this user can enter is reachable by this user.
@@ -33,7 +34,8 @@
33
34
  // (MEASURED: `/exit` ended it, exit 0). The run it lets go is a one-shot: the inspector holds a process
34
35
  // that ends by draining its loop (`--version`) for as long as it is connected, and that process gives
35
36
  // no sign it is done (MEASURED: `beforeExit` never fires while it is held). Which of the two a run is,
36
- // the process is ASKED, ASK_AFTER_MS after release: a TUI has stdin in raw mode by then.
37
+ // the process is ASKED, every ASK_EVERY_MS after release: a TUI puts stdin in raw mode as it mounts,
38
+ // and the first answer that it has is the verdict.
37
39
  //
38
40
  // FAILURE. Every step before release happens while the binary is held at entry, where nothing the user
39
41
  // asked for has run, so a failure there is answered by killing the held process and exiting non-zero,
@@ -66,10 +68,15 @@ const SOCKET_DEADLINE_MS = 5000;
66
68
  // `--version` not raw, exiting as soon as it was let go — the asking does not hold it).
67
69
  const TUI_IS_UP = 'process.stdin.isTTY === true && process.stdin.isRaw === true';
68
70
 
69
- // When after release the process is asked TUI_IS_UP: about fifteen times the measured time to raw
70
- // mode, and the wait an interactive one-shot like `--version` pays before it is let go. A TUI that
71
- // mounts slower than this is taken for a one-shot and runs without the push; the log says which.
72
- const ASK_AFTER_MS = 2000;
71
+ // How often after release the process is asked TUI_IS_UP, and for how long. A TUI is attached the
72
+ // moment it answers raw, so the deadline is paid only by a run that never does — an interactive
73
+ // one-shot like `--version`, held that long before it is let go. Asking once, at 2s, took a TUI that
74
+ // mounted later for a one-shot: `--resume` restarts on 2.1.289 reached raw mode 2.0-3.3s after release,
75
+ // and 2 of 11 ran without the push. A real session (more plugins, a longer transcript) mounts slower
76
+ // still, so the deadline is three times the slowest measured mount. A TUI slower than that is taken for
77
+ // a one-shot and runs without the push; the log says which, and when the answer came.
78
+ const ASK_EVERY_MS = 100;
79
+ const ASK_WITHIN_MS = 10000;
73
80
  const SOCKET_NAME = 'inspector.sock';
74
81
 
75
82
  // Per user, never the shared temp dir: on Linux that is /tmp, where another user could plant the log
@@ -132,20 +139,45 @@ function socketAppears(dir, name, deadlineMs, exited) {
132
139
 
133
140
  const describeEnding = ({ code, signal, error }) => (error ? `could not start: ${error}` : `exited ${signal || code}`);
134
141
 
135
- // What the released run is, asked of it `askAfterMs` after release: `{ runs: 'tui' }`, `{ runs:
136
- // 'one-shot' }`, `{ runs: 'closed' }` when the inspector went away first (the run ended, or the
137
- // connection did), or `{ runs: 'unanswered', why }` when the question itself failed. A close resolves
142
+ // What the released run is, asked of it every `everyMs` after release, one question in flight at a
143
+ // time: `{ runs: 'tui' }` at the first answer that stdin is raw, `{ runs: 'one-shot' }` at the first
144
+ // answer that it is not once `withinMs` has passed, `{ runs: 'closed' }` when the inspector went away
145
+ // first (the run ended, or the connection did), or `{ runs: 'unanswered', why }` when the first answer
146
+ // after the deadline is a question that failed. A failure before then is asked again: one of a hundred
147
+ // questions failing while the run mounts says nothing about whether it will mount. A close resolves
138
148
  // before the rejection it causes reaches here, so a rejection that does is the question failing.
149
+ // [LAW:no-ambient-temporal-coupling] the verdict waits on the fact it is about: the deadline ends the
150
+ // ASKING, and the question in flight is always waited for, never decided over while it could still say
151
+ // otherwise — so a run that never answers at all is waited on for as long as it is connected.
139
152
  // `session` is anything with the session's `send` and `on`; `delay(ms, fn)` is a cancellable timer. A
140
153
  // pure seam, so the suite drives it with neither an inspector nor a clock. [LAW:effects-at-boundaries]
141
- function askWhatRuns(session, askAfterMs, delay) {
154
+ function askWhatRuns(session, { everyMs, withinMs }, delay) {
142
155
  return new Promise((resolve) => {
143
- const answered = (r) => (r.wasThrown
144
- ? { runs: 'unanswered', why: r.result.description ?? JSON.stringify(r.result.value) }
145
- : { runs: r.result.value === true ? 'tui' : 'one-shot' });
146
- const cancel = delay(askAfterMs, () => session.send('Runtime.evaluate', { expression: TUI_IS_UP, returnByValue: true })
147
- .then((r) => resolve(answered(r)), (e) => resolve({ runs: 'unanswered', why: e.message })));
148
- session.on('close', () => { cancel(); resolve({ runs: 'closed' }); });
156
+ let settled = false;
157
+ let expired = false;
158
+ let cancelNext = () => {};
159
+ const cancelDeadline = delay(withinMs, () => { expired = true; });
160
+ const settle = (verdict) => {
161
+ if (settled) return;
162
+ settled = true;
163
+ cancelNext();
164
+ cancelDeadline();
165
+ resolve(verdict);
166
+ };
167
+ // `why` is the failure of the question just answered, null when it answered not raw.
168
+ const notYet = (why) => {
169
+ if (expired) return settle(why === null ? { runs: 'one-shot' } : { runs: 'unanswered', why });
170
+ if (!settled) cancelNext = delay(everyMs, ask);
171
+ };
172
+ const answered = (r) => {
173
+ if (r.wasThrown) return notYet(r.result.description ?? JSON.stringify(r.result.value));
174
+ if (r.result.value === true) return settle({ runs: 'tui' });
175
+ return notYet(null);
176
+ };
177
+ const ask = () => session.send('Runtime.evaluate', { expression: TUI_IS_UP, returnByValue: true })
178
+ .then(answered, (e) => notYet(e.message));
179
+ cancelNext = delay(everyMs, ask);
180
+ session.on('close', () => settle({ runs: 'closed' }));
149
181
  });
150
182
  }
151
183
 
@@ -197,7 +229,7 @@ async function attachAndRun(binary, args, io = {}) {
197
229
  // Only a TUI has a status line to drive. A headless, piped or print-mode run (`-p`, at a terminal or
198
230
  // not) is a one-shot that would pay the wait below for nothing and wants to stay exactly as fast as
199
231
  // it shipped. [LAW:one-source-of-truth] whether a run renders a TUI is launch.js's one parse of it; an
200
- // interactive `--version` still reads as a TUI there, and is let go at ASK_AFTER_MS.
232
+ // interactive `--version` still reads as a TUI there, and is let go at ASK_WITHIN_MS.
201
233
  if (!rendersTui(args, io.terminal ?? attachedToTerminal())) return runStock(binary, args, 'not a TUI: no status line to drive', { stderr });
202
234
 
203
235
  const graph = readGraphFromFile(binary);
@@ -261,8 +293,9 @@ async function attachAndRun(binary, args, io = {}) {
261
293
  // Only a run that answered 'tui' keeps the launcher connected for the session (see WHY IT STAYS
262
294
  // CONNECTED). Anything else is let go, because staying is what holds a one-shot, and a question that
263
295
  // could not be answered is said why.
264
- const { runs, why } = await askWhatRuns(session, ASK_AFTER_MS, (ms, fn) => { const t = setTimeout(fn, ms); return () => clearTimeout(t); });
265
- log(`after release: ${runs}${why ? ` (${why})` : ''}`);
296
+ const releasedAt = Date.now();
297
+ const { runs, why } = await askWhatRuns(session, { everyMs: ASK_EVERY_MS, withinMs: ASK_WITHIN_MS }, (ms, fn) => { const t = setTimeout(fn, ms); return () => clearTimeout(t); });
298
+ log(`after release: ${runs} at ${Date.now() - releasedAt}ms${why ? ` (${why})` : ''}`);
266
299
  if (runs !== 'tui') session.close();
267
300
  if (runs === 'unanswered') troubles.add(`could not ask the session whether it is a TUI, so it ran without the push: ${why}`);
268
301
 
@@ -278,7 +311,7 @@ async function attachAndRun(binary, args, io = {}) {
278
311
  return exitStatus(code, signal);
279
312
  }
280
313
 
281
- module.exports = { SOCKET_DEADLINE_MS, ASK_AFTER_MS, TUI_IS_UP, LOG_PATH, inspectedEnv, attachCalls, readSample, socketAppears, askWhatRuns, attachAndRun };
314
+ module.exports = { SOCKET_DEADLINE_MS, ASK_EVERY_MS, ASK_WITHIN_MS, TUI_IS_UP, LOG_PATH, inspectedEnv, attachCalls, readSample, socketAppears, askWhatRuns, attachAndRun };
282
315
 
283
316
  if (require.main === module) (async function main() {
284
317
  const [binary, ...args] = process.argv.slice(2);