@delorenj/pjangler 1.5.39 → 1.5.41

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/crank/tui.js CHANGED
@@ -213,6 +213,59 @@ var PJANGLER_VERSION = (() => {
213
213
  return "0.0.0";
214
214
  })();
215
215
 
216
+ // src/crank/engine.ts
217
+ var WINDOW_MS = 24 * 60 * 60 * 1e3;
218
+ var TICK_MS = 5 * 60 * 1e3;
219
+ var STALL_MS = 3 * TICK_MS;
220
+ var ENGINE_AUDIT_TAIL_BYTES = 1024 * 1024;
221
+ var FUTURE_SKEW_MS = 5 * 6e4;
222
+ function clockTime(ms) {
223
+ const d = new Date(ms);
224
+ return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
225
+ }
226
+ function ago(thenMs, nowMs) {
227
+ const s = Math.max(0, Math.round((nowMs - thenMs) / 1e3));
228
+ if (s < 60) return `${s}s ago`;
229
+ if (s < 3600) return `${Math.floor(s / 60)}m ago`;
230
+ if (s < 86400) return `${Math.floor(s / 3600)}h ${String(Math.floor(s % 3600 / 60)).padStart(2, "0")}m ago`;
231
+ return `${Math.floor(s / 86400)}d ${Math.floor(s % 86400 / 3600)}h ago`;
232
+ }
233
+ function compactTokens(n) {
234
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
235
+ if (n >= 1e3) return `${Math.round(n / 1e3)}k`;
236
+ return String(n);
237
+ }
238
+ function nextTickAtOrAfter(ms) {
239
+ return Math.ceil(ms / TICK_MS) * TICK_MS;
240
+ }
241
+ function engineStalled(status) {
242
+ return status.known && status.lastTickMs !== void 0 && status.readAtMs - status.lastTickMs > STALL_MS;
243
+ }
244
+ function engineBlockedLabel(status) {
245
+ if (!status.known) return void 0;
246
+ if (engineStalled(status) && status.lastTickMs !== void 0) return `blocked: engine not ticking (last tick ${ago(status.lastTickMs, status.readAtMs)})`;
247
+ if (status.outcome !== "refused") return void 0;
248
+ if (status.tokenBlocked) {
249
+ const resume = status.resumeMs !== void 0 ? ` \u2014 resumes \u2248 ${clockTime(nextTickAtOrAfter(status.resumeMs))}` : "";
250
+ return `blocked: daily token cap${resume}`;
251
+ }
252
+ return `blocked: ${status.reason ?? "engine refused the last tick"}`;
253
+ }
254
+ function engineSummary(status) {
255
+ if (!status.known || status.lastTickMs === void 0) return { text: "engine status unknown", tone: "unknown" };
256
+ const now = status.readAtMs;
257
+ const usage = `${status.beats24h} beat${status.beats24h === 1 ? "" : "s"} / ${compactTokens(status.tokens24h)} tokens in 24h${status.cap ? ` of ${compactTokens(status.cap)}` : ""}`;
258
+ const tick = `last tick ${ago(status.lastTickMs, now)}`;
259
+ const stalled = engineStalled(status) ? " - not ticking" : "";
260
+ if (status.outcome === "cranked") return { text: `${tick} (cranked)${stalled} \xB7 ${usage}`, tone: stalled ? "warn" : "ok" };
261
+ if (status.outcome === "refused") {
262
+ const since = status.refusedSinceMs !== void 0 && status.refusedSinceMs < status.lastTickMs ? ` since ${ago(status.refusedSinceMs, now)}` : "";
263
+ const resume = status.tokenBlocked && status.resumeMs !== void 0 ? ` \xB7 resumes \u2248 ${clockTime(nextTickAtOrAfter(status.resumeMs))}` : "";
264
+ return { text: `REFUSING${since}: ${status.reason ?? "refused"} \xB7 ${tick}${stalled} \xB7 ${usage}${resume}`, tone: "warn" };
265
+ }
266
+ return { text: `${tick} (${status.lastStatus ?? "other"}${status.reason ? `: ${status.reason}` : ""})${stalled} \xB7 ${usage}`, tone: "warn" };
267
+ }
268
+
216
269
  // src/crank/index.ts
217
270
  var ZCRANK_INTERVALS = ["5m", "1h", "6h", "1d"];
218
271
  var INTERVAL_SECONDS = { "5m": 300, "1h": 3600, "6h": 21600, "1d": 86400 };
@@ -239,6 +292,7 @@ function formatAge(thenMs, nowMs) {
239
292
  if (s < 60) return `${s}s ago`;
240
293
  return `${formatDuration(s)} ago`;
241
294
  }
295
+ var PANE_CWD_TTL_SECONDS = 6 * 3600;
242
296
  function renderLogRow(raw) {
243
297
  try {
244
298
  const parsed = JSON.parse(raw);
@@ -294,10 +348,6 @@ async function pickInterval(props, run = {}) {
294
348
  const choice = await runInk((finish) => /* @__PURE__ */ jsx3(IntervalPicker, { ...props, onDone: finish }), run);
295
349
  return choice ?? null;
296
350
  }
297
- function clockTime(ms) {
298
- const d = new Date(ms);
299
- return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
300
- }
301
351
  function describeMatch(view) {
302
352
  const { resolution } = view;
303
353
  if (resolution.storedAsIs) return `no live tab or key matches "${view.name}" - stored under the project name`;
@@ -346,6 +396,9 @@ function CrankDashboard({ controller, refreshMs = 5e3, clock = Date.now, onQuit
346
396
  });
347
397
  const onInterval = (interval) => act(`interval ${interval}`, () => controller.setInterval(interval));
348
398
  const next = nextRunMs({ enabled: isOn, interval: snap.interval, lastRun: snap.lastRunMs, nowMs: now });
399
+ const engineLine = engineSummary(view.engine);
400
+ const blocked = isOn ? engineBlockedLabel(view.engine) : void 0;
401
+ const engineColor = engineLine.tone === "ok" ? themeColor("ok") : engineLine.tone === "warn" ? themeColor("warn") : themeColor("muted");
349
402
  const logRows = Math.max(3, (rows || 24) - 19);
350
403
  const tail = snap.log.slice(-logRows);
351
404
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
@@ -390,7 +443,7 @@ function CrankDashboard({ controller, refreshMs = 5e3, clock = Date.now, onQuit
390
443
  ] }),
391
444
  /* @__PURE__ */ jsxs3(Text3, { children: [
392
445
  /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: "Next run " }),
393
- next === void 0 ? /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: "off" }) : /* @__PURE__ */ jsxs3(Text3, { children: [
446
+ next === void 0 ? /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: "off" }) : blocked ? /* @__PURE__ */ jsx3(Text3, { color: themeColor("warn"), children: blocked }) : /* @__PURE__ */ jsxs3(Text3, { children: [
394
447
  next <= now ? "now" : `in ${formatDuration((next - now) / 1e3)}`,
395
448
  " ",
396
449
  /* @__PURE__ */ jsxs3(Text3, { color: themeColor("muted"), children: [
@@ -400,8 +453,12 @@ function CrankDashboard({ controller, refreshMs = 5e3, clock = Date.now, onQuit
400
453
  ] })
401
454
  ] })
402
455
  ] }),
456
+ /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate-end", children: [
457
+ /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: "Engine " }),
458
+ /* @__PURE__ */ jsx3(Text3, { color: engineColor, children: engineLine.text })
459
+ ] }),
403
460
  /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
404
- /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: "Last runs" }),
461
+ /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: "Last runs of this tab (beats that touched it, not engine ticks)" }),
405
462
  snap.beats.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: themeColor("muted"), children: " none yet" }) : snap.beats.map((beat, i) => /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate-end", children: [
406
463
  " ",
407
464
  formatAge(beat.tsMs, now).padEnd(10),