@sleds/cli 0.1.17 → 0.1.18

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 (2) hide show
  1. package/dist/sleds.mjs +339 -30
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -514,6 +514,8 @@ var init_meter = __esm(() => {
514
514
  // src/seat.ts
515
515
  var exports_seat = {};
516
516
  __export(exports_seat, {
517
+ APPROACHING_PCT: () => APPROACHING_PCT2,
518
+ CREDITS_PATTERN: () => CREDITS_PATTERN2,
517
519
  SEAT_POST_MIN_DELTA_PCT: () => SEAT_POST_MIN_DELTA_PCT2,
518
520
  SEAT_POST_MIN_INTERVAL_MS: () => SEAT_POST_MIN_INTERVAL_MS2,
519
521
  SEAT_POST_TIMEOUT_MS: () => SEAT_POST_TIMEOUT_MS2,
@@ -521,8 +523,10 @@ __export(exports_seat, {
521
523
  SEAT_WALL_LINE_NO_KEY: () => SEAT_WALL_LINE_NO_KEY2,
522
524
  STDIN_TIMEOUT_MS: () => STDIN_TIMEOUT_MS2,
523
525
  bindingResetAt: () => bindingResetAt2,
526
+ creditsHitIn: () => creditsHitIn2,
524
527
  formatSeatFragment: () => formatSeatFragment2,
525
528
  markPosted: () => markPosted2,
529
+ markWarned: () => markWarned2,
526
530
  parseResetsAt: () => parseResetsAt2,
527
531
  parseSeatState: () => parseSeatState2,
528
532
  parseSeatWindow: () => parseSeatWindow2,
@@ -530,16 +534,30 @@ __export(exports_seat, {
530
534
  postSeatWindow: () => postSeatWindow2,
531
535
  readSeatState: () => readSeatState2,
532
536
  readStdin: () => readStdin2,
537
+ scanTranscriptForCredits: () => scanTranscriptForCredits2,
533
538
  seatSessionFromStdin: () => seatSessionFromStdin2,
534
539
  seatStatePath: () => seatStatePath2,
535
- seatWallDue: () => seatWallDue2,
540
+ seatWallBanner: () => seatWallBanner2,
541
+ seatWallDue: () => seatWallDue,
542
+ seatWallEvents: () => seatWallEvents2,
536
543
  seatWallLine: () => seatWallLine2,
537
544
  seatWindowBody: () => seatWindowBody2,
538
545
  seatWindowFromStdin: () => seatWindowFromStdin2,
539
546
  shouldPostSeat: () => shouldPostSeat2,
547
+ transcriptPathFromStdin: () => transcriptPathFromStdin2,
540
548
  writeSeatState: () => writeSeatState2
541
549
  });
542
- import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync13 } from "node:fs";
550
+ import {
551
+ closeSync as closeSync2,
552
+ existsSync as existsSync16,
553
+ mkdirSync as mkdirSync13,
554
+ openSync as openSync2,
555
+ readFileSync as readFileSync13,
556
+ readSync as readSync2,
557
+ renameSync as renameSync6,
558
+ statSync as statSync3,
559
+ writeFileSync as writeFileSync13
560
+ } from "node:fs";
543
561
  import { join as join14 } from "node:path";
544
562
  var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_MS2 = 1500, STDIN_TIMEOUT_MS2 = 300, isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num6 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, EPOCH_MS_THRESHOLD2 = 100000000000, parseResetsAt2 = (v) => {
545
563
  const n = num6(v);
@@ -613,7 +631,7 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
613
631
  if (parts.length === 0)
614
632
  return null;
615
633
  return `seat ${parts.join(SEP)}`;
616
- }, seatStatePath2 = (home) => join14(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
634
+ }, APPROACHING_PCT2 = 90, seatStatePath2 = (home) => join14(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
617
635
  if (!isObject11(v))
618
636
  return;
619
637
  const capturedAt = num6(v.capturedAt);
@@ -646,6 +664,32 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
646
664
  const lastWallResetsAt = num6(raw.lastWallResetsAt);
647
665
  if (lastWallResetsAt !== undefined)
648
666
  out.lastWallResetsAt = lastWallResetsAt;
667
+ if (isObject11(raw.warned)) {
668
+ const warned = {};
669
+ for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
670
+ const w = raw.warned[bar];
671
+ if (!isObject11(w))
672
+ continue;
673
+ const entry = {};
674
+ const ap = num6(w.approaching);
675
+ const hit = num6(w.hit);
676
+ if (ap !== undefined)
677
+ entry.approaching = ap;
678
+ if (hit !== undefined)
679
+ entry.hit = hit;
680
+ warned[bar] = entry;
681
+ }
682
+ out.warned = warned;
683
+ }
684
+ if (isObject11(raw.transcriptScan) && typeof raw.transcriptScan.path === "string") {
685
+ const offset = num6(raw.transcriptScan.offset);
686
+ const hitAt = num6(raw.transcriptScan.hitAt);
687
+ out.transcriptScan = {
688
+ path: raw.transcriptScan.path,
689
+ offset: offset ?? 0,
690
+ ...hitAt !== undefined ? { hitAt } : {}
691
+ };
692
+ }
649
693
  const session = parseSeatSession2(raw.session);
650
694
  if (session)
651
695
  out.session = session;
@@ -737,10 +781,92 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
737
781
  if (w.spendLimit)
738
782
  lastPosted.spendLimit = w.spendLimit.usedPct;
739
783
  return { ...state, window: w, lastPostedAt: now, lastPosted };
740
- }, seatWallDue2 = (state, w) => {
784
+ }, seatWallDue = (state, w) => {
741
785
  if (!w?.sevenDay || w.sevenDay.usedPct < 100)
742
786
  return false;
743
787
  return state.lastWallResetsAt === undefined || state.lastWallResetsAt < w.sevenDay.resetsAt;
788
+ }, seatWallEvents2 = (state, w) => {
789
+ if (!w)
790
+ return [];
791
+ const out = [];
792
+ const bars = [
793
+ ["fiveHour", w.fiveHour],
794
+ ["sevenDay", w.sevenDay],
795
+ ["spendLimit", w.spendLimit]
796
+ ];
797
+ for (const [bar, b] of bars) {
798
+ if (!b)
799
+ continue;
800
+ const seen = state.warned?.[bar] ?? {};
801
+ const weeklyOld = bar === "sevenDay" && state.lastWallResetsAt !== undefined && state.lastWallResetsAt >= b.resetsAt;
802
+ if (b.usedPct >= APPROACHING_PCT2 && (seen.approaching ?? -1) < b.resetsAt && !weeklyOld)
803
+ out.push({ bar, stage: "approaching", usedPct: b.usedPct, resetsAt: b.resetsAt });
804
+ if (b.usedPct >= 100 && (seen.hit ?? -1) < b.resetsAt && !weeklyOld)
805
+ out.push({ bar, stage: "hit", usedPct: b.usedPct, resetsAt: b.resetsAt });
806
+ }
807
+ return out;
808
+ }, markWarned2 = (state, events) => {
809
+ if (events.length === 0)
810
+ return state;
811
+ const warned = { ...state.warned ?? {} };
812
+ let lastWallResetsAt = state.lastWallResetsAt;
813
+ for (const e of events) {
814
+ warned[e.bar] = { ...warned[e.bar] ?? {}, [e.stage]: e.resetsAt };
815
+ if (e.bar === "sevenDay" && e.stage === "hit")
816
+ lastWallResetsAt = e.resetsAt;
817
+ }
818
+ return { ...state, warned, ...lastWallResetsAt !== undefined ? { lastWallResetsAt } : {} };
819
+ }, CREDITS_PATTERN2, creditsHitIn2 = (text) => CREDITS_PATTERN2.test(text), transcriptPathFromStdin2 = (text) => {
820
+ try {
821
+ const raw = JSON.parse(text.trim());
822
+ return isObject11(raw) && typeof raw.transcript_path === "string" && raw.transcript_path ? raw.transcript_path : null;
823
+ } catch {
824
+ return null;
825
+ }
826
+ }, scanTranscriptForCredits2 = (path, prior) => {
827
+ const from = prior && prior.path === path ? prior.offset : 0;
828
+ try {
829
+ const size = statSync3(path).size;
830
+ if (size <= from)
831
+ return {
832
+ hit: false,
833
+ next: { path, offset: size, ...prior?.hitAt ? { hitAt: prior.hitAt } : {} }
834
+ };
835
+ const start = Math.max(from, size - 256 * 1024);
836
+ const fd = openSync2(path, "r");
837
+ try {
838
+ const buf = Buffer.alloc(size - start);
839
+ readSync2(fd, buf, 0, buf.length, start);
840
+ const hit = creditsHitIn2(buf.toString("utf8"));
841
+ return {
842
+ hit,
843
+ next: {
844
+ path,
845
+ offset: size,
846
+ ...hit ? { hitAt: Date.now() } : prior?.hitAt ? { hitAt: prior.hitAt } : {}
847
+ }
848
+ };
849
+ } finally {
850
+ closeSync2(fd);
851
+ }
852
+ } catch {
853
+ return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
854
+ }
855
+ }, seatWallBanner2 = (e, hasUpstreamKey, now) => {
856
+ const label = {
857
+ fiveHour: "5-hour",
858
+ sevenDay: "weekly",
859
+ spendLimit: "spend",
860
+ credits: "usage credits"
861
+ }[e.bar];
862
+ const mins = Math.max(1, Math.round((e.resetsAt - now) / 60000));
863
+ const reset = e.bar === "credits" ? "" : mins < 60 ? ` · resets in ${mins} min` : ` · resets in ${Math.floor(mins / 60)} h ${mins % 60 ? `${mins % 60} min` : ""}`.trimEnd();
864
+ const exits = hasUpstreamKey ? "/sled handoff · sleds defer · sleds on" : "/sled handoff · sleds defer (no platform key here)";
865
+ if (e.stage === "approaching")
866
+ return `Seat ${label} at ${Math.round(e.usedPct)}%${reset}. Plan the exit now: ${exits}`;
867
+ if (e.bar === "credits")
868
+ return `Out of usage credits. ${exits}`;
869
+ return `Seat ${label} limit hit${reset}. ${exits}`;
744
870
  }, seatWindowBody2 = (w) => ({
745
871
  capturedAt: w.capturedAt,
746
872
  ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
@@ -786,6 +912,7 @@ var init_seat = __esm(() => {
786
912
  init_config();
787
913
  init_meter();
788
914
  SEAT_POST_MIN_INTERVAL_MS2 = 5 * 60000;
915
+ CREDITS_PATTERN2 = /out of usage credits|cc_cli_limit_message/i;
789
916
  });
790
917
 
791
918
  // src/index.ts
@@ -1155,7 +1282,17 @@ var parsePayload = (payload) => {
1155
1282
  // src/seat.ts
1156
1283
  init_config();
1157
1284
  init_meter();
1158
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync4, writeFileSync as writeFileSync5 } from "node:fs";
1285
+ import {
1286
+ closeSync,
1287
+ existsSync as existsSync5,
1288
+ mkdirSync as mkdirSync5,
1289
+ openSync,
1290
+ readFileSync as readFileSync5,
1291
+ readSync,
1292
+ renameSync as renameSync4,
1293
+ statSync,
1294
+ writeFileSync as writeFileSync5
1295
+ } from "node:fs";
1159
1296
  import { join as join6 } from "node:path";
1160
1297
  var SEAT_POST_MIN_DELTA_PCT = 1;
1161
1298
  var SEAT_POST_MIN_INTERVAL_MS = 5 * 60000;
@@ -1241,6 +1378,7 @@ var formatSeatFragment = (w) => {
1241
1378
  return null;
1242
1379
  return `seat ${parts.join(SEP)}`;
1243
1380
  };
1381
+ var APPROACHING_PCT = 90;
1244
1382
  var seatStatePath = (home) => join6(configDir(home), "seat.json");
1245
1383
  var parseWindowStored = (v) => {
1246
1384
  if (!isObject5(v))
@@ -1276,6 +1414,32 @@ var parseSeatState = (raw) => {
1276
1414
  const lastWallResetsAt = num3(raw.lastWallResetsAt);
1277
1415
  if (lastWallResetsAt !== undefined)
1278
1416
  out.lastWallResetsAt = lastWallResetsAt;
1417
+ if (isObject5(raw.warned)) {
1418
+ const warned = {};
1419
+ for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
1420
+ const w = raw.warned[bar];
1421
+ if (!isObject5(w))
1422
+ continue;
1423
+ const entry = {};
1424
+ const ap = num3(w.approaching);
1425
+ const hit = num3(w.hit);
1426
+ if (ap !== undefined)
1427
+ entry.approaching = ap;
1428
+ if (hit !== undefined)
1429
+ entry.hit = hit;
1430
+ warned[bar] = entry;
1431
+ }
1432
+ out.warned = warned;
1433
+ }
1434
+ if (isObject5(raw.transcriptScan) && typeof raw.transcriptScan.path === "string") {
1435
+ const offset = num3(raw.transcriptScan.offset);
1436
+ const hitAt = num3(raw.transcriptScan.hitAt);
1437
+ out.transcriptScan = {
1438
+ path: raw.transcriptScan.path,
1439
+ offset: offset ?? 0,
1440
+ ...hitAt !== undefined ? { hitAt } : {}
1441
+ };
1442
+ }
1279
1443
  const session = parseSeatSession(raw.session);
1280
1444
  if (session)
1281
1445
  out.session = session;
@@ -1376,10 +1540,94 @@ var markPosted = (state, w, now) => {
1376
1540
  lastPosted.spendLimit = w.spendLimit.usedPct;
1377
1541
  return { ...state, window: w, lastPostedAt: now, lastPosted };
1378
1542
  };
1379
- var seatWallDue = (state, w) => {
1380
- if (!w?.sevenDay || w.sevenDay.usedPct < 100)
1381
- return false;
1382
- return state.lastWallResetsAt === undefined || state.lastWallResetsAt < w.sevenDay.resetsAt;
1543
+ var seatWallEvents = (state, w) => {
1544
+ if (!w)
1545
+ return [];
1546
+ const out = [];
1547
+ const bars = [
1548
+ ["fiveHour", w.fiveHour],
1549
+ ["sevenDay", w.sevenDay],
1550
+ ["spendLimit", w.spendLimit]
1551
+ ];
1552
+ for (const [bar, b] of bars) {
1553
+ if (!b)
1554
+ continue;
1555
+ const seen = state.warned?.[bar] ?? {};
1556
+ const weeklyOld = bar === "sevenDay" && state.lastWallResetsAt !== undefined && state.lastWallResetsAt >= b.resetsAt;
1557
+ if (b.usedPct >= APPROACHING_PCT && (seen.approaching ?? -1) < b.resetsAt && !weeklyOld)
1558
+ out.push({ bar, stage: "approaching", usedPct: b.usedPct, resetsAt: b.resetsAt });
1559
+ if (b.usedPct >= 100 && (seen.hit ?? -1) < b.resetsAt && !weeklyOld)
1560
+ out.push({ bar, stage: "hit", usedPct: b.usedPct, resetsAt: b.resetsAt });
1561
+ }
1562
+ return out;
1563
+ };
1564
+ var markWarned = (state, events) => {
1565
+ if (events.length === 0)
1566
+ return state;
1567
+ const warned = { ...state.warned ?? {} };
1568
+ let lastWallResetsAt = state.lastWallResetsAt;
1569
+ for (const e of events) {
1570
+ warned[e.bar] = { ...warned[e.bar] ?? {}, [e.stage]: e.resetsAt };
1571
+ if (e.bar === "sevenDay" && e.stage === "hit")
1572
+ lastWallResetsAt = e.resetsAt;
1573
+ }
1574
+ return { ...state, warned, ...lastWallResetsAt !== undefined ? { lastWallResetsAt } : {} };
1575
+ };
1576
+ var CREDITS_PATTERN = /out of usage credits|cc_cli_limit_message/i;
1577
+ var creditsHitIn = (text) => CREDITS_PATTERN.test(text);
1578
+ var transcriptPathFromStdin = (text) => {
1579
+ try {
1580
+ const raw = JSON.parse(text.trim());
1581
+ return isObject5(raw) && typeof raw.transcript_path === "string" && raw.transcript_path ? raw.transcript_path : null;
1582
+ } catch {
1583
+ return null;
1584
+ }
1585
+ };
1586
+ var scanTranscriptForCredits = (path, prior) => {
1587
+ const from = prior && prior.path === path ? prior.offset : 0;
1588
+ try {
1589
+ const size = statSync(path).size;
1590
+ if (size <= from)
1591
+ return {
1592
+ hit: false,
1593
+ next: { path, offset: size, ...prior?.hitAt ? { hitAt: prior.hitAt } : {} }
1594
+ };
1595
+ const start = Math.max(from, size - 256 * 1024);
1596
+ const fd = openSync(path, "r");
1597
+ try {
1598
+ const buf = Buffer.alloc(size - start);
1599
+ readSync(fd, buf, 0, buf.length, start);
1600
+ const hit = creditsHitIn(buf.toString("utf8"));
1601
+ return {
1602
+ hit,
1603
+ next: {
1604
+ path,
1605
+ offset: size,
1606
+ ...hit ? { hitAt: Date.now() } : prior?.hitAt ? { hitAt: prior.hitAt } : {}
1607
+ }
1608
+ };
1609
+ } finally {
1610
+ closeSync(fd);
1611
+ }
1612
+ } catch {
1613
+ return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
1614
+ }
1615
+ };
1616
+ var seatWallBanner = (e, hasUpstreamKey, now) => {
1617
+ const label = {
1618
+ fiveHour: "5-hour",
1619
+ sevenDay: "weekly",
1620
+ spendLimit: "spend",
1621
+ credits: "usage credits"
1622
+ }[e.bar];
1623
+ const mins = Math.max(1, Math.round((e.resetsAt - now) / 60000));
1624
+ const reset = e.bar === "credits" ? "" : mins < 60 ? ` · resets in ${mins} min` : ` · resets in ${Math.floor(mins / 60)} h ${mins % 60 ? `${mins % 60} min` : ""}`.trimEnd();
1625
+ const exits = hasUpstreamKey ? "/sled handoff · sleds defer · sleds on" : "/sled handoff · sleds defer (no platform key here)";
1626
+ if (e.stage === "approaching")
1627
+ return `Seat ${label} at ${Math.round(e.usedPct)}%${reset}. Plan the exit now: ${exits}`;
1628
+ if (e.bar === "credits")
1629
+ return `Out of usage credits. ${exits}`;
1630
+ return `Seat ${label} limit hit${reset}. ${exits}`;
1383
1631
  };
1384
1632
  var seatWindowBody = (w) => ({
1385
1633
  capturedAt: w.capturedAt,
@@ -4222,7 +4470,7 @@ async function postSeat(proxyUrl, token, body, fetchFn) {
4222
4470
 
4223
4471
  // src/commands/specEvent.ts
4224
4472
  init_meter();
4225
- import { readFileSync as readFileSync10, statSync } from "node:fs";
4473
+ import { readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
4226
4474
  import { basename as basename2, isAbsolute, relative } from "node:path";
4227
4475
  var HOOK_STDIN_TIMEOUT_MS2 = 1000;
4228
4476
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -4276,7 +4524,7 @@ function projectKey(filePath, cwd) {
4276
4524
  }
4277
4525
  function readFileOrNull(path) {
4278
4526
  try {
4279
- if (statSync(path).size > 2000000)
4527
+ if (statSync2(path).size > 2000000)
4280
4528
  return null;
4281
4529
  return readFileSync10(path, "utf8");
4282
4530
  } catch {
@@ -4453,7 +4701,25 @@ var status = async (o) => {
4453
4701
  };
4454
4702
 
4455
4703
  // src/commands/statusline.ts
4704
+ init_config();
4456
4705
  init_meter();
4706
+
4707
+ // src/commands/seatEvent.ts
4708
+ init_config();
4709
+ init_meter();
4710
+ var NOTIFICATION_TITLE2 = "sleds";
4711
+ var osascriptArgs2 = (title, body) => [
4712
+ "-e",
4713
+ "on run argv",
4714
+ "-e",
4715
+ "display notification (item 2 of argv) with title (item 1 of argv)",
4716
+ "-e",
4717
+ "end run",
4718
+ title,
4719
+ body
4720
+ ];
4721
+
4722
+ // src/commands/statusline.ts
4457
4723
  var statusline = async (o) => {
4458
4724
  const log = o.log ?? console.log;
4459
4725
  const now = (o.now ?? Date.now)();
@@ -4463,9 +4729,21 @@ var statusline = async (o) => {
4463
4729
  let posts = [];
4464
4730
  try {
4465
4731
  if (window)
4466
- posts = rememberSeat(o, window, session, now);
4467
- else if (session)
4468
- writeSeatState(o.home, { ...readSeatState(o.home), session });
4732
+ posts = rememberSeat(o, window, session, now, input);
4733
+ else {
4734
+ const st = readSeatState(o.home);
4735
+ const credits = creditsEvent(o, st, input, now);
4736
+ if (session || credits.scan) {
4737
+ const next = {
4738
+ ...st,
4739
+ ...session ? { session } : {},
4740
+ ...credits.scan ? { transcriptScan: credits.scan } : {}
4741
+ };
4742
+ writeSeatState(o.home, credits.event ? markWarned(next, [credits.event]) : next);
4743
+ }
4744
+ if (credits.event)
4745
+ posts = announce(o, [credits.event], now);
4746
+ }
4469
4747
  } catch {}
4470
4748
  const seatFragment = formatSeatFragment(window);
4471
4749
  let line;
@@ -4484,27 +4762,58 @@ var statusline = async (o) => {
4484
4762
  await Promise.allSettled(posts);
4485
4763
  return 0;
4486
4764
  };
4487
- var rememberSeat = (o, window, session, now) => {
4765
+ var rememberSeat = (o, window, session, now, input) => {
4488
4766
  const prior = readSeatState(o.home);
4489
4767
  let state = { ...prior, window, ...session ? { session } : {} };
4490
4768
  const src = meterSource(o.home);
4491
4769
  const posts = [];
4492
- if (src) {
4493
- const opts = { ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS };
4494
- if (shouldPostSeat(prior, window, now)) {
4495
- state = markPosted(state, window, now);
4496
- posts.push(postSeatWindow(opts, window));
4770
+ if (src && shouldPostSeat(prior, window, now)) {
4771
+ state = markPosted(state, window, now);
4772
+ posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
4773
+ }
4774
+ const events = seatWallEvents(prior, window);
4775
+ const credits = creditsEvent(o, prior, input, now);
4776
+ if (credits.scan)
4777
+ state = { ...state, transcriptScan: credits.scan };
4778
+ if (credits.event)
4779
+ events.push(credits.event);
4780
+ state = markWarned(state, events);
4781
+ posts.push(...announce(o, events, now));
4782
+ writeSeatState(o.home, state);
4783
+ return posts;
4784
+ };
4785
+ var creditsEvent = (o, prior, input, now) => {
4786
+ const path = transcriptPathFromStdin(input);
4787
+ if (!path)
4788
+ return { event: null, scan: undefined };
4789
+ const { hit, next } = scanTranscriptForCredits(path, prior.transcriptScan);
4790
+ const recent = prior.warned?.credits?.hit !== undefined && now - prior.warned.credits.hit < 6 * 3600000;
4791
+ if (!hit || recent)
4792
+ return { event: null, scan: next };
4793
+ return { event: { bar: "credits", stage: "hit", usedPct: 100, resetsAt: now }, scan: next };
4794
+ };
4795
+ var announce = (o, events, now) => {
4796
+ if (events.length === 0)
4797
+ return [];
4798
+ const src = meterSource(o.home);
4799
+ const hasKey = readConfig(o.home).org?.capabilities.upstreamKey !== false;
4800
+ const platform2 = o.platform ?? process.platform;
4801
+ const posts = [];
4802
+ for (const e of events) {
4803
+ if (platform2 === "darwin" && o.exec) {
4804
+ try {
4805
+ o.exec("osascript", osascriptArgs2(NOTIFICATION_TITLE2, seatWallBanner(e, hasKey, now)));
4806
+ } catch {}
4497
4807
  }
4498
- if (seatWallDue(prior, window) && window.sevenDay) {
4499
- state = { ...state, lastWallResetsAt: window.sevenDay.resetsAt };
4500
- posts.push(postSeatWall(opts, {
4501
- resetsAt: window.sevenDay.resetsAt,
4502
- usedPct: window.sevenDay.usedPct,
4503
- notification: "statusline"
4808
+ if (src)
4809
+ posts.push(postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
4810
+ bar: e.bar,
4811
+ stage: e.stage,
4812
+ usedPct: e.usedPct,
4813
+ ...e.bar === "credits" ? {} : { resetsAt: e.resetsAt },
4814
+ notification: e.bar === "credits" ? "transcript" : "statusline"
4504
4815
  }));
4505
- }
4506
4816
  }
4507
- writeSeatState(o.home, state);
4508
4817
  return posts;
4509
4818
  };
4510
4819
 
@@ -5043,7 +5352,7 @@ var main = async (argv) => {
5043
5352
  case "seat-sync":
5044
5353
  return seatSync({ home, argv: process.argv.slice(3) });
5045
5354
  case "statusline":
5046
- return statusline({ home });
5355
+ return statusline({ home, platform: platform2(), exec: defaultExec });
5047
5356
  case "help":
5048
5357
  case "--help":
5049
5358
  case "-h":
@@ -5052,7 +5361,7 @@ var main = async (argv) => {
5052
5361
  case "version":
5053
5362
  case "--version":
5054
5363
  case "-v":
5055
- console.log("sleds 0.1.17");
5364
+ console.log("sleds 0.1.18");
5056
5365
  return 0;
5057
5366
  default:
5058
5367
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "SLEDS CLI \u2014 the cost and limits of AI agent work, decided at the wall. Point Claude Code at a SLEDS proxy; estimate before you run; hold, switch model, or run overnight when you hit a limit.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",