@sleds/cli 0.1.17 → 0.1.19
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/sleds.mjs +471 -60
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -152,7 +152,24 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
152
152
|
if (!list.some(isSledsSpecHook))
|
|
153
153
|
return { ...settings };
|
|
154
154
|
return withEventHooks(settings, POST_TOOL_EVENT, list.filter((e) => !isSledsSpecHook(e)));
|
|
155
|
-
}, hasSpecHook = (settings) => eventHooks(settings, POST_TOOL_EVENT).some(isSledsSpecHook),
|
|
155
|
+
}, hasSpecHook = (settings) => eventHooks(settings, POST_TOOL_EVENT).some(isSledsSpecHook), PROMPT_EVENT = "UserPromptSubmit", PROMPT_EVENT_TIMEOUT_S = 3, isSledsPromptHook = (entry) => {
|
|
156
|
+
if (!isObject(entry) || !Array.isArray(entry.hooks))
|
|
157
|
+
return false;
|
|
158
|
+
return entry.hooks.some((h) => isObject(h) && h.type === "command" && typeof h.command === "string" && /\bprompt-event\b/.test(h.command));
|
|
159
|
+
}, promptHookEntry = (command) => ({
|
|
160
|
+
hooks: [{ type: "command", command, timeout: PROMPT_EVENT_TIMEOUT_S }]
|
|
161
|
+
}), applyPromptHook = (settings, command) => {
|
|
162
|
+
const list = eventHooks(settings, PROMPT_EVENT);
|
|
163
|
+
const idx = list.findIndex(isSledsPromptHook);
|
|
164
|
+
const entry = promptHookEntry(command);
|
|
165
|
+
const next = idx === -1 ? [...list, entry] : list.map((e, i) => i === idx ? entry : e);
|
|
166
|
+
return withEventHooks(settings, PROMPT_EVENT, next);
|
|
167
|
+
}, removePromptHook = (settings) => {
|
|
168
|
+
const list = eventHooks(settings, PROMPT_EVENT);
|
|
169
|
+
if (!list.some(isSledsPromptHook))
|
|
170
|
+
return { ...settings };
|
|
171
|
+
return withEventHooks(settings, PROMPT_EVENT, list.filter((e) => !isSledsPromptHook(e)));
|
|
172
|
+
}, hasPromptHook = (settings) => eventHooks(settings, PROMPT_EVENT).some(isSledsPromptHook), isSeatToken = (token) => /^sled_[A-Za-z0-9_-]+$/.test(token), maskToken = (token) => token.length <= 9 ? `${token.slice(0, 5)}…` : `${token.slice(0, 9)}…`, serialize = (settings) => `${JSON.stringify(settings, null, 2)}
|
|
156
173
|
`, hasClaudeCode = (home) => existsSync(claudeDir(home)), readSettings = (home) => {
|
|
157
174
|
const p = settingsPath(home);
|
|
158
175
|
if (!existsSync(p))
|
|
@@ -514,6 +531,8 @@ var init_meter = __esm(() => {
|
|
|
514
531
|
// src/seat.ts
|
|
515
532
|
var exports_seat = {};
|
|
516
533
|
__export(exports_seat, {
|
|
534
|
+
APPROACHING_PCT: () => APPROACHING_PCT2,
|
|
535
|
+
CREDITS_PATTERN: () => CREDITS_PATTERN2,
|
|
517
536
|
SEAT_POST_MIN_DELTA_PCT: () => SEAT_POST_MIN_DELTA_PCT2,
|
|
518
537
|
SEAT_POST_MIN_INTERVAL_MS: () => SEAT_POST_MIN_INTERVAL_MS2,
|
|
519
538
|
SEAT_POST_TIMEOUT_MS: () => SEAT_POST_TIMEOUT_MS2,
|
|
@@ -521,8 +540,10 @@ __export(exports_seat, {
|
|
|
521
540
|
SEAT_WALL_LINE_NO_KEY: () => SEAT_WALL_LINE_NO_KEY2,
|
|
522
541
|
STDIN_TIMEOUT_MS: () => STDIN_TIMEOUT_MS2,
|
|
523
542
|
bindingResetAt: () => bindingResetAt2,
|
|
543
|
+
creditsHitIn: () => creditsHitIn2,
|
|
524
544
|
formatSeatFragment: () => formatSeatFragment2,
|
|
525
545
|
markPosted: () => markPosted2,
|
|
546
|
+
markWarned: () => markWarned2,
|
|
526
547
|
parseResetsAt: () => parseResetsAt2,
|
|
527
548
|
parseSeatState: () => parseSeatState2,
|
|
528
549
|
parseSeatWindow: () => parseSeatWindow2,
|
|
@@ -530,16 +551,29 @@ __export(exports_seat, {
|
|
|
530
551
|
postSeatWindow: () => postSeatWindow2,
|
|
531
552
|
readSeatState: () => readSeatState2,
|
|
532
553
|
readStdin: () => readStdin2,
|
|
554
|
+
scanTranscriptForCredits: () => scanTranscriptForCredits2,
|
|
533
555
|
seatSessionFromStdin: () => seatSessionFromStdin2,
|
|
534
556
|
seatStatePath: () => seatStatePath2,
|
|
535
|
-
seatWallDue: () =>
|
|
557
|
+
seatWallDue: () => seatWallDue,
|
|
558
|
+
seatWallEvents: () => seatWallEvents2,
|
|
536
559
|
seatWallLine: () => seatWallLine2,
|
|
537
560
|
seatWindowBody: () => seatWindowBody2,
|
|
538
561
|
seatWindowFromStdin: () => seatWindowFromStdin2,
|
|
539
562
|
shouldPostSeat: () => shouldPostSeat2,
|
|
563
|
+
transcriptPathFromStdin: () => transcriptPathFromStdin2,
|
|
540
564
|
writeSeatState: () => writeSeatState2
|
|
541
565
|
});
|
|
542
|
-
import {
|
|
566
|
+
import {
|
|
567
|
+
closeSync as closeSync2,
|
|
568
|
+
existsSync as existsSync16,
|
|
569
|
+
mkdirSync as mkdirSync13,
|
|
570
|
+
openSync as openSync2,
|
|
571
|
+
readFileSync as readFileSync13,
|
|
572
|
+
readSync as readSync2,
|
|
573
|
+
renameSync as renameSync6,
|
|
574
|
+
statSync as statSync3,
|
|
575
|
+
writeFileSync as writeFileSync13
|
|
576
|
+
} from "node:fs";
|
|
543
577
|
import { join as join14 } from "node:path";
|
|
544
578
|
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
579
|
const n = num6(v);
|
|
@@ -600,7 +634,7 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
600
634
|
} catch {
|
|
601
635
|
return null;
|
|
602
636
|
}
|
|
603
|
-
}, formatSeatFragment2 = (w) => {
|
|
637
|
+
}, formatSeatFragment2 = (w, now = Date.now()) => {
|
|
604
638
|
if (!w)
|
|
605
639
|
return null;
|
|
606
640
|
const parts = [];
|
|
@@ -612,8 +646,19 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
612
646
|
parts.push(`spend ${Math.round(w.spendLimit.usedPct)}%`);
|
|
613
647
|
if (parts.length === 0)
|
|
614
648
|
return null;
|
|
649
|
+
const hot = [
|
|
650
|
+
["wk", w.sevenDay],
|
|
651
|
+
["5h", w.fiveHour],
|
|
652
|
+
["spend", w.spendLimit]
|
|
653
|
+
].filter((x) => x[1] !== undefined && x[1].usedPct >= APPROACHING_PCT2).sort((a, b) => b[1].usedPct - a[1].usedPct)[0];
|
|
654
|
+
if (hot) {
|
|
655
|
+
const mins = Math.max(0, Math.round((hot[1].resetsAt - now) / 60000));
|
|
656
|
+
const reset = mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? `${mins % 60}m` : ""}` : `${mins}m`;
|
|
657
|
+
const verb = hot[1].usedPct >= 100 ? "at the wall" : `${Math.round(hot[1].usedPct)}%`;
|
|
658
|
+
return `⚠ seat ${hot[0]} ${verb}${SEP}resets ${reset}${SEP}/sled handoff · sleds defer`;
|
|
659
|
+
}
|
|
615
660
|
return `seat ${parts.join(SEP)}`;
|
|
616
|
-
}, seatStatePath2 = (home) => join14(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
|
|
661
|
+
}, APPROACHING_PCT2 = 90, seatStatePath2 = (home) => join14(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
|
|
617
662
|
if (!isObject11(v))
|
|
618
663
|
return;
|
|
619
664
|
const capturedAt = num6(v.capturedAt);
|
|
@@ -646,6 +691,49 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
646
691
|
const lastWallResetsAt = num6(raw.lastWallResetsAt);
|
|
647
692
|
if (lastWallResetsAt !== undefined)
|
|
648
693
|
out.lastWallResetsAt = lastWallResetsAt;
|
|
694
|
+
if (isObject11(raw.warned)) {
|
|
695
|
+
const warned = {};
|
|
696
|
+
for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
|
|
697
|
+
const w = raw.warned[bar];
|
|
698
|
+
if (!isObject11(w))
|
|
699
|
+
continue;
|
|
700
|
+
const entry = {};
|
|
701
|
+
const ap = num6(w.approaching);
|
|
702
|
+
const hit = num6(w.hit);
|
|
703
|
+
if (ap !== undefined)
|
|
704
|
+
entry.approaching = ap;
|
|
705
|
+
if (hit !== undefined)
|
|
706
|
+
entry.hit = hit;
|
|
707
|
+
warned[bar] = entry;
|
|
708
|
+
}
|
|
709
|
+
out.warned = warned;
|
|
710
|
+
}
|
|
711
|
+
if (isObject11(raw.told)) {
|
|
712
|
+
const told = {};
|
|
713
|
+
for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
|
|
714
|
+
const w = raw.told[bar];
|
|
715
|
+
if (!isObject11(w))
|
|
716
|
+
continue;
|
|
717
|
+
const entry = {};
|
|
718
|
+
const ap = num6(w.approaching);
|
|
719
|
+
const hit = num6(w.hit);
|
|
720
|
+
if (ap !== undefined)
|
|
721
|
+
entry.approaching = ap;
|
|
722
|
+
if (hit !== undefined)
|
|
723
|
+
entry.hit = hit;
|
|
724
|
+
told[bar] = entry;
|
|
725
|
+
}
|
|
726
|
+
out.told = told;
|
|
727
|
+
}
|
|
728
|
+
if (isObject11(raw.transcriptScan) && typeof raw.transcriptScan.path === "string") {
|
|
729
|
+
const offset = num6(raw.transcriptScan.offset);
|
|
730
|
+
const hitAt = num6(raw.transcriptScan.hitAt);
|
|
731
|
+
out.transcriptScan = {
|
|
732
|
+
path: raw.transcriptScan.path,
|
|
733
|
+
offset: offset ?? 0,
|
|
734
|
+
...hitAt !== undefined ? { hitAt } : {}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
649
737
|
const session = parseSeatSession2(raw.session);
|
|
650
738
|
if (session)
|
|
651
739
|
out.session = session;
|
|
@@ -737,10 +825,77 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
737
825
|
if (w.spendLimit)
|
|
738
826
|
lastPosted.spendLimit = w.spendLimit.usedPct;
|
|
739
827
|
return { ...state, window: w, lastPostedAt: now, lastPosted };
|
|
740
|
-
},
|
|
828
|
+
}, seatWallDue = (state, w) => {
|
|
741
829
|
if (!w?.sevenDay || w.sevenDay.usedPct < 100)
|
|
742
830
|
return false;
|
|
743
831
|
return state.lastWallResetsAt === undefined || state.lastWallResetsAt < w.sevenDay.resetsAt;
|
|
832
|
+
}, seatWallEvents2 = (state, w) => {
|
|
833
|
+
if (!w)
|
|
834
|
+
return [];
|
|
835
|
+
const out = [];
|
|
836
|
+
const bars = [
|
|
837
|
+
["fiveHour", w.fiveHour],
|
|
838
|
+
["sevenDay", w.sevenDay],
|
|
839
|
+
["spendLimit", w.spendLimit]
|
|
840
|
+
];
|
|
841
|
+
for (const [bar, b] of bars) {
|
|
842
|
+
if (!b)
|
|
843
|
+
continue;
|
|
844
|
+
const seen = state.warned?.[bar] ?? {};
|
|
845
|
+
const weeklyOld = bar === "sevenDay" && state.lastWallResetsAt !== undefined && state.lastWallResetsAt >= b.resetsAt;
|
|
846
|
+
if (b.usedPct >= APPROACHING_PCT2 && (seen.approaching ?? -1) < b.resetsAt && !weeklyOld)
|
|
847
|
+
out.push({ bar, stage: "approaching", usedPct: b.usedPct, resetsAt: b.resetsAt });
|
|
848
|
+
if (b.usedPct >= 100 && (seen.hit ?? -1) < b.resetsAt && !weeklyOld)
|
|
849
|
+
out.push({ bar, stage: "hit", usedPct: b.usedPct, resetsAt: b.resetsAt });
|
|
850
|
+
}
|
|
851
|
+
return out;
|
|
852
|
+
}, markWarned2 = (state, events) => {
|
|
853
|
+
if (events.length === 0)
|
|
854
|
+
return state;
|
|
855
|
+
const warned = { ...state.warned ?? {} };
|
|
856
|
+
let lastWallResetsAt = state.lastWallResetsAt;
|
|
857
|
+
for (const e of events) {
|
|
858
|
+
warned[e.bar] = { ...warned[e.bar] ?? {}, [e.stage]: e.resetsAt };
|
|
859
|
+
if (e.bar === "sevenDay" && e.stage === "hit")
|
|
860
|
+
lastWallResetsAt = e.resetsAt;
|
|
861
|
+
}
|
|
862
|
+
return { ...state, warned, ...lastWallResetsAt !== undefined ? { lastWallResetsAt } : {} };
|
|
863
|
+
}, CREDITS_PATTERN2, creditsHitIn2 = (text) => CREDITS_PATTERN2.test(text), transcriptPathFromStdin2 = (text) => {
|
|
864
|
+
try {
|
|
865
|
+
const raw = JSON.parse(text.trim());
|
|
866
|
+
return isObject11(raw) && typeof raw.transcript_path === "string" && raw.transcript_path ? raw.transcript_path : null;
|
|
867
|
+
} catch {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
}, scanTranscriptForCredits2 = (path, prior) => {
|
|
871
|
+
const from = prior && prior.path === path ? prior.offset : 0;
|
|
872
|
+
try {
|
|
873
|
+
const size = statSync3(path).size;
|
|
874
|
+
if (size <= from)
|
|
875
|
+
return {
|
|
876
|
+
hit: false,
|
|
877
|
+
next: { path, offset: size, ...prior?.hitAt ? { hitAt: prior.hitAt } : {} }
|
|
878
|
+
};
|
|
879
|
+
const start = Math.max(from, size - 256 * 1024);
|
|
880
|
+
const fd = openSync2(path, "r");
|
|
881
|
+
try {
|
|
882
|
+
const buf = Buffer.alloc(size - start);
|
|
883
|
+
readSync2(fd, buf, 0, buf.length, start);
|
|
884
|
+
const hit = creditsHitIn2(buf.toString("utf8"));
|
|
885
|
+
return {
|
|
886
|
+
hit,
|
|
887
|
+
next: {
|
|
888
|
+
path,
|
|
889
|
+
offset: size,
|
|
890
|
+
...hit ? { hitAt: Date.now() } : prior?.hitAt ? { hitAt: prior.hitAt } : {}
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
} finally {
|
|
894
|
+
closeSync2(fd);
|
|
895
|
+
}
|
|
896
|
+
} catch {
|
|
897
|
+
return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
|
|
898
|
+
}
|
|
744
899
|
}, seatWindowBody2 = (w) => ({
|
|
745
900
|
capturedAt: w.capturedAt,
|
|
746
901
|
...w.fiveHour ? { fiveHour: w.fiveHour } : {},
|
|
@@ -786,6 +941,7 @@ var init_seat = __esm(() => {
|
|
|
786
941
|
init_config();
|
|
787
942
|
init_meter();
|
|
788
943
|
SEAT_POST_MIN_INTERVAL_MS2 = 5 * 60000;
|
|
944
|
+
CREDITS_PATTERN2 = /out of usage credits|cc_cli_limit_message/i;
|
|
789
945
|
});
|
|
790
946
|
|
|
791
947
|
// src/index.ts
|
|
@@ -1155,7 +1311,17 @@ var parsePayload = (payload) => {
|
|
|
1155
1311
|
// src/seat.ts
|
|
1156
1312
|
init_config();
|
|
1157
1313
|
init_meter();
|
|
1158
|
-
import {
|
|
1314
|
+
import {
|
|
1315
|
+
closeSync,
|
|
1316
|
+
existsSync as existsSync5,
|
|
1317
|
+
mkdirSync as mkdirSync5,
|
|
1318
|
+
openSync,
|
|
1319
|
+
readFileSync as readFileSync5,
|
|
1320
|
+
readSync,
|
|
1321
|
+
renameSync as renameSync4,
|
|
1322
|
+
statSync,
|
|
1323
|
+
writeFileSync as writeFileSync5
|
|
1324
|
+
} from "node:fs";
|
|
1159
1325
|
import { join as join6 } from "node:path";
|
|
1160
1326
|
var SEAT_POST_MIN_DELTA_PCT = 1;
|
|
1161
1327
|
var SEAT_POST_MIN_INTERVAL_MS = 5 * 60000;
|
|
@@ -1227,7 +1393,7 @@ var seatWindowFromStdin = (text, capturedAt) => {
|
|
|
1227
1393
|
return null;
|
|
1228
1394
|
}
|
|
1229
1395
|
};
|
|
1230
|
-
var formatSeatFragment = (w) => {
|
|
1396
|
+
var formatSeatFragment = (w, now = Date.now()) => {
|
|
1231
1397
|
if (!w)
|
|
1232
1398
|
return null;
|
|
1233
1399
|
const parts = [];
|
|
@@ -1239,8 +1405,20 @@ var formatSeatFragment = (w) => {
|
|
|
1239
1405
|
parts.push(`spend ${Math.round(w.spendLimit.usedPct)}%`);
|
|
1240
1406
|
if (parts.length === 0)
|
|
1241
1407
|
return null;
|
|
1408
|
+
const hot = [
|
|
1409
|
+
["wk", w.sevenDay],
|
|
1410
|
+
["5h", w.fiveHour],
|
|
1411
|
+
["spend", w.spendLimit]
|
|
1412
|
+
].filter((x) => x[1] !== undefined && x[1].usedPct >= APPROACHING_PCT).sort((a, b) => b[1].usedPct - a[1].usedPct)[0];
|
|
1413
|
+
if (hot) {
|
|
1414
|
+
const mins = Math.max(0, Math.round((hot[1].resetsAt - now) / 60000));
|
|
1415
|
+
const reset = mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? `${mins % 60}m` : ""}` : `${mins}m`;
|
|
1416
|
+
const verb = hot[1].usedPct >= 100 ? "at the wall" : `${Math.round(hot[1].usedPct)}%`;
|
|
1417
|
+
return `⚠ seat ${hot[0]} ${verb}${SEP}resets ${reset}${SEP}/sled handoff · sleds defer`;
|
|
1418
|
+
}
|
|
1242
1419
|
return `seat ${parts.join(SEP)}`;
|
|
1243
1420
|
};
|
|
1421
|
+
var APPROACHING_PCT = 90;
|
|
1244
1422
|
var seatStatePath = (home) => join6(configDir(home), "seat.json");
|
|
1245
1423
|
var parseWindowStored = (v) => {
|
|
1246
1424
|
if (!isObject5(v))
|
|
@@ -1276,6 +1454,49 @@ var parseSeatState = (raw) => {
|
|
|
1276
1454
|
const lastWallResetsAt = num3(raw.lastWallResetsAt);
|
|
1277
1455
|
if (lastWallResetsAt !== undefined)
|
|
1278
1456
|
out.lastWallResetsAt = lastWallResetsAt;
|
|
1457
|
+
if (isObject5(raw.warned)) {
|
|
1458
|
+
const warned = {};
|
|
1459
|
+
for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
|
|
1460
|
+
const w = raw.warned[bar];
|
|
1461
|
+
if (!isObject5(w))
|
|
1462
|
+
continue;
|
|
1463
|
+
const entry = {};
|
|
1464
|
+
const ap = num3(w.approaching);
|
|
1465
|
+
const hit = num3(w.hit);
|
|
1466
|
+
if (ap !== undefined)
|
|
1467
|
+
entry.approaching = ap;
|
|
1468
|
+
if (hit !== undefined)
|
|
1469
|
+
entry.hit = hit;
|
|
1470
|
+
warned[bar] = entry;
|
|
1471
|
+
}
|
|
1472
|
+
out.warned = warned;
|
|
1473
|
+
}
|
|
1474
|
+
if (isObject5(raw.told)) {
|
|
1475
|
+
const told = {};
|
|
1476
|
+
for (const bar of ["fiveHour", "sevenDay", "spendLimit", "credits"]) {
|
|
1477
|
+
const w = raw.told[bar];
|
|
1478
|
+
if (!isObject5(w))
|
|
1479
|
+
continue;
|
|
1480
|
+
const entry = {};
|
|
1481
|
+
const ap = num3(w.approaching);
|
|
1482
|
+
const hit = num3(w.hit);
|
|
1483
|
+
if (ap !== undefined)
|
|
1484
|
+
entry.approaching = ap;
|
|
1485
|
+
if (hit !== undefined)
|
|
1486
|
+
entry.hit = hit;
|
|
1487
|
+
told[bar] = entry;
|
|
1488
|
+
}
|
|
1489
|
+
out.told = told;
|
|
1490
|
+
}
|
|
1491
|
+
if (isObject5(raw.transcriptScan) && typeof raw.transcriptScan.path === "string") {
|
|
1492
|
+
const offset = num3(raw.transcriptScan.offset);
|
|
1493
|
+
const hitAt = num3(raw.transcriptScan.hitAt);
|
|
1494
|
+
out.transcriptScan = {
|
|
1495
|
+
path: raw.transcriptScan.path,
|
|
1496
|
+
offset: offset ?? 0,
|
|
1497
|
+
...hitAt !== undefined ? { hitAt } : {}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1279
1500
|
const session = parseSeatSession(raw.session);
|
|
1280
1501
|
if (session)
|
|
1281
1502
|
out.session = session;
|
|
@@ -1376,10 +1597,78 @@ var markPosted = (state, w, now) => {
|
|
|
1376
1597
|
lastPosted.spendLimit = w.spendLimit.usedPct;
|
|
1377
1598
|
return { ...state, window: w, lastPostedAt: now, lastPosted };
|
|
1378
1599
|
};
|
|
1379
|
-
var
|
|
1380
|
-
if (!w
|
|
1381
|
-
return
|
|
1382
|
-
|
|
1600
|
+
var seatWallEvents = (state, w) => {
|
|
1601
|
+
if (!w)
|
|
1602
|
+
return [];
|
|
1603
|
+
const out = [];
|
|
1604
|
+
const bars = [
|
|
1605
|
+
["fiveHour", w.fiveHour],
|
|
1606
|
+
["sevenDay", w.sevenDay],
|
|
1607
|
+
["spendLimit", w.spendLimit]
|
|
1608
|
+
];
|
|
1609
|
+
for (const [bar, b] of bars) {
|
|
1610
|
+
if (!b)
|
|
1611
|
+
continue;
|
|
1612
|
+
const seen = state.warned?.[bar] ?? {};
|
|
1613
|
+
const weeklyOld = bar === "sevenDay" && state.lastWallResetsAt !== undefined && state.lastWallResetsAt >= b.resetsAt;
|
|
1614
|
+
if (b.usedPct >= APPROACHING_PCT && (seen.approaching ?? -1) < b.resetsAt && !weeklyOld)
|
|
1615
|
+
out.push({ bar, stage: "approaching", usedPct: b.usedPct, resetsAt: b.resetsAt });
|
|
1616
|
+
if (b.usedPct >= 100 && (seen.hit ?? -1) < b.resetsAt && !weeklyOld)
|
|
1617
|
+
out.push({ bar, stage: "hit", usedPct: b.usedPct, resetsAt: b.resetsAt });
|
|
1618
|
+
}
|
|
1619
|
+
return out;
|
|
1620
|
+
};
|
|
1621
|
+
var markWarned = (state, events) => {
|
|
1622
|
+
if (events.length === 0)
|
|
1623
|
+
return state;
|
|
1624
|
+
const warned = { ...state.warned ?? {} };
|
|
1625
|
+
let lastWallResetsAt = state.lastWallResetsAt;
|
|
1626
|
+
for (const e of events) {
|
|
1627
|
+
warned[e.bar] = { ...warned[e.bar] ?? {}, [e.stage]: e.resetsAt };
|
|
1628
|
+
if (e.bar === "sevenDay" && e.stage === "hit")
|
|
1629
|
+
lastWallResetsAt = e.resetsAt;
|
|
1630
|
+
}
|
|
1631
|
+
return { ...state, warned, ...lastWallResetsAt !== undefined ? { lastWallResetsAt } : {} };
|
|
1632
|
+
};
|
|
1633
|
+
var CREDITS_PATTERN = /out of usage credits|cc_cli_limit_message/i;
|
|
1634
|
+
var creditsHitIn = (text) => CREDITS_PATTERN.test(text);
|
|
1635
|
+
var transcriptPathFromStdin = (text) => {
|
|
1636
|
+
try {
|
|
1637
|
+
const raw = JSON.parse(text.trim());
|
|
1638
|
+
return isObject5(raw) && typeof raw.transcript_path === "string" && raw.transcript_path ? raw.transcript_path : null;
|
|
1639
|
+
} catch {
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
var scanTranscriptForCredits = (path, prior) => {
|
|
1644
|
+
const from = prior && prior.path === path ? prior.offset : 0;
|
|
1645
|
+
try {
|
|
1646
|
+
const size = statSync(path).size;
|
|
1647
|
+
if (size <= from)
|
|
1648
|
+
return {
|
|
1649
|
+
hit: false,
|
|
1650
|
+
next: { path, offset: size, ...prior?.hitAt ? { hitAt: prior.hitAt } : {} }
|
|
1651
|
+
};
|
|
1652
|
+
const start = Math.max(from, size - 256 * 1024);
|
|
1653
|
+
const fd = openSync(path, "r");
|
|
1654
|
+
try {
|
|
1655
|
+
const buf = Buffer.alloc(size - start);
|
|
1656
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
1657
|
+
const hit = creditsHitIn(buf.toString("utf8"));
|
|
1658
|
+
return {
|
|
1659
|
+
hit,
|
|
1660
|
+
next: {
|
|
1661
|
+
path,
|
|
1662
|
+
offset: size,
|
|
1663
|
+
...hit ? { hitAt: Date.now() } : prior?.hitAt ? { hitAt: prior.hitAt } : {}
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
} finally {
|
|
1667
|
+
closeSync(fd);
|
|
1668
|
+
}
|
|
1669
|
+
} catch {
|
|
1670
|
+
return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
|
|
1671
|
+
}
|
|
1383
1672
|
};
|
|
1384
1673
|
var seatWindowBody = (w) => ({
|
|
1385
1674
|
capturedAt: w.capturedAt,
|
|
@@ -2671,13 +2960,15 @@ var hooks = (o) => {
|
|
|
2671
2960
|
const had = hasSeatHook(settings);
|
|
2672
2961
|
const hadSpec = hasSpecHook(settings);
|
|
2673
2962
|
const withSeat = applySeatHook(settings, o.seatEventCommand ?? sledsCommand("seat-event", o.home, o.env));
|
|
2674
|
-
const
|
|
2963
|
+
const hadPrompt = hasPromptHook(settings);
|
|
2964
|
+
const withSpec = applyPromptHook(applySpecHook(withSeat, o.specEventCommand ?? sledsCommand("spec-event", o.home, o.env)), o.promptEventCommand ?? sledsCommand("prompt-event", o.home, o.env));
|
|
2675
2965
|
const hadLine = o.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
|
|
2676
2966
|
writeSettings(o.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o.home, o.env)).settings : withSpec);
|
|
2677
2967
|
log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o.home)}`);
|
|
2678
2968
|
if (hadLine)
|
|
2679
2969
|
log(`refreshed statusLine in ${settingsPath(o.home)}`);
|
|
2680
2970
|
log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o.home)}`);
|
|
2971
|
+
log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o.home)}`);
|
|
2681
2972
|
return 0;
|
|
2682
2973
|
};
|
|
2683
2974
|
|
|
@@ -2832,13 +3123,15 @@ var hooks2 = (o) => {
|
|
|
2832
3123
|
const had = hasSeatHook(settings);
|
|
2833
3124
|
const hadSpec = hasSpecHook(settings);
|
|
2834
3125
|
const withSeat = applySeatHook(settings, o.seatEventCommand ?? sledsCommand("seat-event", o.home, o.env));
|
|
2835
|
-
const
|
|
3126
|
+
const hadPrompt = hasPromptHook(settings);
|
|
3127
|
+
const withSpec = applyPromptHook(applySpecHook(withSeat, o.specEventCommand ?? sledsCommand("spec-event", o.home, o.env)), o.promptEventCommand ?? sledsCommand("prompt-event", o.home, o.env));
|
|
2836
3128
|
const hadLine = o.refreshStatusLine !== false && isSledsStatusLine(withSpec[STATUS_LINE_KEY]);
|
|
2837
3129
|
writeSettings(o.home, hadLine ? applyStatusLine(withSpec, sledsCommand("statusline", o.home, o.env)).settings : withSpec);
|
|
2838
3130
|
log(`${had ? "refreshed" : "set"} hooks.Notification (${SEAT_EVENT_MATCHER}) in ${settingsPath(o.home)}`);
|
|
2839
3131
|
if (hadLine)
|
|
2840
3132
|
log(`refreshed statusLine in ${settingsPath(o.home)}`);
|
|
2841
3133
|
log(`${hadSpec ? "refreshed" : "set"} hooks.PostToolUse (${SPEC_EVENT_MATCHER} → estimate spec files) in ${settingsPath(o.home)}`);
|
|
3134
|
+
log(`${hadPrompt ? "refreshed" : "set"} hooks.UserPromptSubmit (the seat speaks up in the session at 90 % and at a wall) in ${settingsPath(o.home)}`);
|
|
2842
3135
|
return 0;
|
|
2843
3136
|
};
|
|
2844
3137
|
|
|
@@ -3050,6 +3343,7 @@ var init = async (o) => {
|
|
|
3050
3343
|
refreshStatusLine: false,
|
|
3051
3344
|
seatEventCommand: o.seatEventCommand,
|
|
3052
3345
|
specEventCommand: o.specEventCommand,
|
|
3346
|
+
promptEventCommand: o.promptEventCommand,
|
|
3053
3347
|
log: (line) => wrote.push(line),
|
|
3054
3348
|
error
|
|
3055
3349
|
});
|
|
@@ -3570,6 +3864,93 @@ var on = (o) => {
|
|
|
3570
3864
|
return 0;
|
|
3571
3865
|
};
|
|
3572
3866
|
|
|
3867
|
+
// src/commands/promptEvent.ts
|
|
3868
|
+
init_config();
|
|
3869
|
+
|
|
3870
|
+
// src/commands/seatEvent.ts
|
|
3871
|
+
init_config();
|
|
3872
|
+
init_meter();
|
|
3873
|
+
var HOOK_STDIN_TIMEOUT_MS = 1000;
|
|
3874
|
+
|
|
3875
|
+
// src/commands/promptEvent.ts
|
|
3876
|
+
var LABEL = {
|
|
3877
|
+
fiveHour: "5-hour",
|
|
3878
|
+
sevenDay: "weekly",
|
|
3879
|
+
spendLimit: "spend",
|
|
3880
|
+
credits: "usage credits"
|
|
3881
|
+
};
|
|
3882
|
+
var fmtIn = (ms) => {
|
|
3883
|
+
const m = Math.max(1, Math.round(ms / 60000));
|
|
3884
|
+
if (m < 60)
|
|
3885
|
+
return `${m} min`;
|
|
3886
|
+
const h = Math.floor(m / 60);
|
|
3887
|
+
const r = m % 60;
|
|
3888
|
+
return r ? `${h} h ${r} min` : `${h} h`;
|
|
3889
|
+
};
|
|
3890
|
+
var untold = (state) => {
|
|
3891
|
+
const out = [];
|
|
3892
|
+
for (const bar of ["credits", "spendLimit", "sevenDay", "fiveHour"]) {
|
|
3893
|
+
const w = state.warned?.[bar];
|
|
3894
|
+
if (!w)
|
|
3895
|
+
continue;
|
|
3896
|
+
for (const stage of ["hit", "approaching"]) {
|
|
3897
|
+
const at = w[stage];
|
|
3898
|
+
if (at === undefined)
|
|
3899
|
+
continue;
|
|
3900
|
+
if ((state.told?.[bar]?.[stage] ?? -1) >= at)
|
|
3901
|
+
continue;
|
|
3902
|
+
out.push({ bar, stage, resetsAt: at });
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
return out;
|
|
3906
|
+
};
|
|
3907
|
+
var exitsLine = (hasUpstreamKey) => hasUpstreamKey ? "`/sled handoff <what you were doing>` hands the work to a teammate or buddy; `sleds defer` resumes this session at the reset; `sleds on` continues now on the workspace's platform key." : "`/sled handoff <what you were doing>` hands the work to a teammate or buddy; `sleds defer` resumes this session at the reset (this workspace has no platform key to switch to).";
|
|
3908
|
+
var approachingContext = (bar, usedPct, resetsAt, hasUpstreamKey, now) => `[SLEDS] This seat is at ${usedPct !== undefined ? `${Math.round(usedPct)}%` : "90%+"} of its ${LABEL[bar]} limit; it resets in ${fmtIn(resetsAt - now)}. Tell the user in one short line before you answer, and offer the exits: ${exitsLine(hasUpstreamKey)} Do not mention this again unless asked.`;
|
|
3909
|
+
var hitContext = (bar, resetsAt, hasUpstreamKey, now) => `[SLEDS] This seat hit its ${LABEL[bar]} limit; it resets in ${fmtIn(resetsAt - now)}. If this reply fails or the user is stuck, the exits are: ${exitsLine(hasUpstreamKey)} Say so in one line.`;
|
|
3910
|
+
var creditsBlockText = (hasUpstreamKey) => `SLEDS: this seat is out of usage credits — the next reply would fail, so this prompt was not sent. ${hasUpstreamKey ? "Exits: /sled handoff <task> (a buddy picks it up), sleds on (continue on the workspace's platform key), or /model to a cheaper model." : "Exits: /sled handoff <task> (a buddy picks it up), sleds defer (resume when credits are topped up), or /model to a cheaper model."} Re-send your prompt after choosing; SLEDS will not block again.`;
|
|
3911
|
+
var promptEvent = async (o) => {
|
|
3912
|
+
const log = o.log ?? console.log;
|
|
3913
|
+
const error = o.error ?? console.error;
|
|
3914
|
+
const now = (o.now ?? Date.now)();
|
|
3915
|
+
if (o.stdin === undefined)
|
|
3916
|
+
await readStdin(HOOK_STDIN_TIMEOUT_MS);
|
|
3917
|
+
let state;
|
|
3918
|
+
try {
|
|
3919
|
+
state = readSeatState(o.home);
|
|
3920
|
+
} catch {
|
|
3921
|
+
return 0;
|
|
3922
|
+
}
|
|
3923
|
+
const pending = untold(state);
|
|
3924
|
+
if (pending.length === 0)
|
|
3925
|
+
return 0;
|
|
3926
|
+
const hasKey = readConfig(o.home).org?.capabilities.upstreamKey !== false;
|
|
3927
|
+
const told = { ...state.told ?? {} };
|
|
3928
|
+
const mark = (bar, stage, at) => {
|
|
3929
|
+
told[bar] = { ...told[bar] ?? {}, [stage]: at };
|
|
3930
|
+
};
|
|
3931
|
+
let code = 0;
|
|
3932
|
+
const lines = [];
|
|
3933
|
+
for (const e of pending) {
|
|
3934
|
+
mark(e.bar, e.stage, e.resetsAt);
|
|
3935
|
+
if (e.bar === "credits") {
|
|
3936
|
+
if (code !== 2) {
|
|
3937
|
+
error(creditsBlockText(hasKey));
|
|
3938
|
+
code = 2;
|
|
3939
|
+
}
|
|
3940
|
+
continue;
|
|
3941
|
+
}
|
|
3942
|
+
const pct = state.window?.[e.bar]?.usedPct;
|
|
3943
|
+
lines.push(e.stage === "approaching" ? approachingContext(e.bar, pct, e.resetsAt, hasKey, now) : hitContext(e.bar, e.resetsAt, hasKey, now));
|
|
3944
|
+
}
|
|
3945
|
+
try {
|
|
3946
|
+
writeSeatState(o.home, { ...state, told });
|
|
3947
|
+
} catch {}
|
|
3948
|
+
if (code === 0 && lines.length > 0)
|
|
3949
|
+
log(lines.join(`
|
|
3950
|
+
`));
|
|
3951
|
+
return code;
|
|
3952
|
+
};
|
|
3953
|
+
|
|
3573
3954
|
// src/commands/run.ts
|
|
3574
3955
|
init_config();
|
|
3575
3956
|
init_meter();
|
|
@@ -3634,8 +4015,7 @@ var run = async (o) => {
|
|
|
3634
4015
|
// src/commands/seatEvent.ts
|
|
3635
4016
|
init_config();
|
|
3636
4017
|
init_meter();
|
|
3637
|
-
var
|
|
3638
|
-
var NOTIFICATION_TITLE = "sleds";
|
|
4018
|
+
var HOOK_STDIN_TIMEOUT_MS2 = 1000;
|
|
3639
4019
|
var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3640
4020
|
var notificationType = (text) => {
|
|
3641
4021
|
try {
|
|
@@ -3647,20 +4027,10 @@ var notificationType = (text) => {
|
|
|
3647
4027
|
return;
|
|
3648
4028
|
}
|
|
3649
4029
|
};
|
|
3650
|
-
var osascriptArgs = (title, body) => [
|
|
3651
|
-
"-e",
|
|
3652
|
-
"on run argv",
|
|
3653
|
-
"-e",
|
|
3654
|
-
"display notification (item 2 of argv) with title (item 1 of argv)",
|
|
3655
|
-
"-e",
|
|
3656
|
-
"end run",
|
|
3657
|
-
title,
|
|
3658
|
-
body
|
|
3659
|
-
];
|
|
3660
4030
|
var seatEvent = async (o) => {
|
|
3661
4031
|
const log = o.log ?? console.log;
|
|
3662
4032
|
const now = (o.now ?? Date.now)();
|
|
3663
|
-
const input = o.stdin ?? await readStdin(
|
|
4033
|
+
const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
|
|
3664
4034
|
const kind = notificationType(input);
|
|
3665
4035
|
try {
|
|
3666
4036
|
const state = readSeatState(o.home);
|
|
@@ -3677,11 +4047,6 @@ var seatEvent = async (o) => {
|
|
|
3677
4047
|
} catch {}
|
|
3678
4048
|
const line = seatWallLine(readConfig(o.home).org?.capabilities.upstreamKey !== false);
|
|
3679
4049
|
log(line);
|
|
3680
|
-
if ((o.platform ?? process.platform) === "darwin" && o.exec) {
|
|
3681
|
-
try {
|
|
3682
|
-
o.exec("osascript", osascriptArgs(NOTIFICATION_TITLE, line));
|
|
3683
|
-
} catch {}
|
|
3684
|
-
}
|
|
3685
4050
|
return 0;
|
|
3686
4051
|
};
|
|
3687
4052
|
|
|
@@ -4222,9 +4587,9 @@ async function postSeat(proxyUrl, token, body, fetchFn) {
|
|
|
4222
4587
|
|
|
4223
4588
|
// src/commands/specEvent.ts
|
|
4224
4589
|
init_meter();
|
|
4225
|
-
import { readFileSync as readFileSync10, statSync } from "node:fs";
|
|
4590
|
+
import { readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
|
|
4226
4591
|
import { basename as basename2, isAbsolute, relative } from "node:path";
|
|
4227
|
-
var
|
|
4592
|
+
var HOOK_STDIN_TIMEOUT_MS3 = 1000;
|
|
4228
4593
|
var ESTIMATE_TIMEOUT_MS = 4000;
|
|
4229
4594
|
var MIN_SPEC_CHARS = 200;
|
|
4230
4595
|
var MAX_SPEC_CHARS = 12000;
|
|
@@ -4276,7 +4641,7 @@ function projectKey(filePath, cwd) {
|
|
|
4276
4641
|
}
|
|
4277
4642
|
function readFileOrNull(path) {
|
|
4278
4643
|
try {
|
|
4279
|
-
if (
|
|
4644
|
+
if (statSync2(path).size > 2000000)
|
|
4280
4645
|
return null;
|
|
4281
4646
|
return readFileSync10(path, "utf8");
|
|
4282
4647
|
} catch {
|
|
@@ -4285,7 +4650,7 @@ function readFileOrNull(path) {
|
|
|
4285
4650
|
}
|
|
4286
4651
|
var specEvent = async (o) => {
|
|
4287
4652
|
const log = o.log ?? console.log;
|
|
4288
|
-
const raw = o.stdin ?? await readStdin(
|
|
4653
|
+
const raw = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS3);
|
|
4289
4654
|
const input = raw ? parseHookInput(raw) : null;
|
|
4290
4655
|
if (!input || !isSpecPath(input.filePath))
|
|
4291
4656
|
return 0;
|
|
@@ -4463,11 +4828,23 @@ var statusline = async (o) => {
|
|
|
4463
4828
|
let posts = [];
|
|
4464
4829
|
try {
|
|
4465
4830
|
if (window)
|
|
4466
|
-
posts = rememberSeat(o, window, session, now);
|
|
4467
|
-
else
|
|
4468
|
-
|
|
4831
|
+
posts = rememberSeat(o, window, session, now, input);
|
|
4832
|
+
else {
|
|
4833
|
+
const st = readSeatState(o.home);
|
|
4834
|
+
const credits = creditsEvent(o, st, input, now);
|
|
4835
|
+
if (session || credits.scan) {
|
|
4836
|
+
const next = {
|
|
4837
|
+
...st,
|
|
4838
|
+
...session ? { session } : {},
|
|
4839
|
+
...credits.scan ? { transcriptScan: credits.scan } : {}
|
|
4840
|
+
};
|
|
4841
|
+
writeSeatState(o.home, credits.event ? markWarned(next, [credits.event]) : next);
|
|
4842
|
+
}
|
|
4843
|
+
if (credits.event)
|
|
4844
|
+
posts = announce(o, [credits.event]);
|
|
4845
|
+
}
|
|
4469
4846
|
} catch {}
|
|
4470
|
-
const seatFragment = formatSeatFragment(window);
|
|
4847
|
+
const seatFragment = formatSeatFragment(window, now);
|
|
4471
4848
|
let line;
|
|
4472
4849
|
try {
|
|
4473
4850
|
const meter2 = await loadMeter({
|
|
@@ -4484,29 +4861,50 @@ var statusline = async (o) => {
|
|
|
4484
4861
|
await Promise.allSettled(posts);
|
|
4485
4862
|
return 0;
|
|
4486
4863
|
};
|
|
4487
|
-
var rememberSeat = (o, window, session, now) => {
|
|
4864
|
+
var rememberSeat = (o, window, session, now, input) => {
|
|
4488
4865
|
const prior = readSeatState(o.home);
|
|
4489
4866
|
let state = { ...prior, window, ...session ? { session } : {} };
|
|
4490
4867
|
const src = meterSource(o.home);
|
|
4491
4868
|
const posts = [];
|
|
4492
|
-
if (src) {
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
}));
|
|
4505
|
-
}
|
|
4506
|
-
}
|
|
4869
|
+
if (src && shouldPostSeat(prior, window, now)) {
|
|
4870
|
+
state = markPosted(state, window, now);
|
|
4871
|
+
posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
|
|
4872
|
+
}
|
|
4873
|
+
const events = seatWallEvents(prior, window);
|
|
4874
|
+
const credits = creditsEvent(o, prior, input, now);
|
|
4875
|
+
if (credits.scan)
|
|
4876
|
+
state = { ...state, transcriptScan: credits.scan };
|
|
4877
|
+
if (credits.event)
|
|
4878
|
+
events.push(credits.event);
|
|
4879
|
+
state = markWarned(state, events);
|
|
4880
|
+
posts.push(...announce(o, events));
|
|
4507
4881
|
writeSeatState(o.home, state);
|
|
4508
4882
|
return posts;
|
|
4509
4883
|
};
|
|
4884
|
+
var creditsEvent = (o, prior, input, now) => {
|
|
4885
|
+
const path = transcriptPathFromStdin(input);
|
|
4886
|
+
if (!path)
|
|
4887
|
+
return { event: null, scan: undefined };
|
|
4888
|
+
const { hit, next } = scanTranscriptForCredits(path, prior.transcriptScan);
|
|
4889
|
+
const recent = prior.warned?.credits?.hit !== undefined && now - prior.warned.credits.hit < 6 * 3600000;
|
|
4890
|
+
if (!hit || recent)
|
|
4891
|
+
return { event: null, scan: next };
|
|
4892
|
+
return { event: { bar: "credits", stage: "hit", usedPct: 100, resetsAt: now }, scan: next };
|
|
4893
|
+
};
|
|
4894
|
+
var announce = (o, events) => {
|
|
4895
|
+
if (events.length === 0)
|
|
4896
|
+
return [];
|
|
4897
|
+
const src = meterSource(o.home);
|
|
4898
|
+
if (!src)
|
|
4899
|
+
return [];
|
|
4900
|
+
return events.map((e) => postSeatWall({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, {
|
|
4901
|
+
bar: e.bar,
|
|
4902
|
+
stage: e.stage,
|
|
4903
|
+
usedPct: e.usedPct,
|
|
4904
|
+
...e.bar === "credits" ? {} : { resetsAt: e.resetsAt },
|
|
4905
|
+
notification: e.bar === "credits" ? "transcript" : "statusline"
|
|
4906
|
+
}));
|
|
4907
|
+
};
|
|
4510
4908
|
|
|
4511
4909
|
// src/commands/uninstall.ts
|
|
4512
4910
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
@@ -4593,6 +4991,11 @@ var uninstall2 = (o) => {
|
|
|
4593
4991
|
changed = true;
|
|
4594
4992
|
removed.push(`hooks.Notification seat-event entry from ${settingsPath(o.home)}`);
|
|
4595
4993
|
}
|
|
4994
|
+
if (hasPromptHook(settings)) {
|
|
4995
|
+
settings = removePromptHook(settings);
|
|
4996
|
+
changed = true;
|
|
4997
|
+
removed.push(`hooks.UserPromptSubmit prompt-event entry from ${settingsPath(o.home)}`);
|
|
4998
|
+
}
|
|
4596
4999
|
if (hasSpecHook(settings)) {
|
|
4597
5000
|
settings = removeSpecHook(settings);
|
|
4598
5001
|
changed = true;
|
|
@@ -4875,6 +5278,8 @@ commands:
|
|
|
4875
5278
|
statusline Print the one-line meter Claude Code shows (installed by init; 10s cache).
|
|
4876
5279
|
hooks Register the Claude Code Notification hook for seat limits (init does this too).
|
|
4877
5280
|
seat-event Claude Code hook: record a seat wall and say how to continue. Reads hook JSON on stdin.
|
|
5281
|
+
prompt-event Claude Code UserPromptSubmit hook: at 90 % of a seat bar the seat speaks up in the
|
|
5282
|
+
session (once per bar per reset); out of usage credits blocks the prompt once with the exits.
|
|
4878
5283
|
spec-event Claude Code PostToolUse hook: when a spec-shaped markdown file is written, size it
|
|
4879
5284
|
(POST /proxy/estimate with the file path as the project). Silent unless it estimates.
|
|
4880
5285
|
--version Print the CLI version.
|
|
@@ -4927,6 +5332,7 @@ var sledsArgv = () => {
|
|
|
4927
5332
|
var shellQuote = (s) => /^[\w./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
|
|
4928
5333
|
var statusLineCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "statusline"].map(shellQuote).join(" "), home);
|
|
4929
5334
|
var seatEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "seat-event"].map(shellQuote).join(" "), home);
|
|
5335
|
+
var promptEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "prompt-event"].map(shellQuote).join(" "), home);
|
|
4930
5336
|
var specEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "spec-event"].map(shellQuote).join(" "), home);
|
|
4931
5337
|
var main = async (argv) => {
|
|
4932
5338
|
const { cmd, flags } = parseArgs(argv);
|
|
@@ -4998,19 +5404,23 @@ var main = async (argv) => {
|
|
|
4998
5404
|
prompt: promptStdin,
|
|
4999
5405
|
statusLineCommand: statusLineCommand(sledsArgv()),
|
|
5000
5406
|
seatEventCommand: seatEventCommand(sledsArgv()),
|
|
5001
|
-
specEventCommand: specEventCommand(sledsArgv())
|
|
5407
|
+
specEventCommand: specEventCommand(sledsArgv()),
|
|
5408
|
+
promptEventCommand: promptEventCommand(sledsArgv())
|
|
5002
5409
|
});
|
|
5003
5410
|
}
|
|
5004
5411
|
case "hooks":
|
|
5005
5412
|
return hooks({
|
|
5006
5413
|
home,
|
|
5007
5414
|
seatEventCommand: seatEventCommand(sledsArgv()),
|
|
5008
|
-
specEventCommand: specEventCommand(sledsArgv())
|
|
5415
|
+
specEventCommand: specEventCommand(sledsArgv()),
|
|
5416
|
+
promptEventCommand: promptEventCommand(sledsArgv())
|
|
5009
5417
|
});
|
|
5010
5418
|
case "seat-event":
|
|
5011
|
-
return seatEvent({ home
|
|
5419
|
+
return seatEvent({ home });
|
|
5012
5420
|
case "spec-event":
|
|
5013
5421
|
return specEvent({ home });
|
|
5422
|
+
case "prompt-event":
|
|
5423
|
+
return promptEvent({ home });
|
|
5014
5424
|
case "on":
|
|
5015
5425
|
return on({
|
|
5016
5426
|
home,
|
|
@@ -5052,7 +5462,7 @@ var main = async (argv) => {
|
|
|
5052
5462
|
case "version":
|
|
5053
5463
|
case "--version":
|
|
5054
5464
|
case "-v":
|
|
5055
|
-
console.log("sleds 0.1.
|
|
5465
|
+
console.log("sleds 0.1.19");
|
|
5056
5466
|
return 0;
|
|
5057
5467
|
default:
|
|
5058
5468
|
console.error(`unknown command: ${cmd}
|
|
@@ -5081,6 +5491,7 @@ if (isMain()) {
|
|
|
5081
5491
|
}
|
|
5082
5492
|
export {
|
|
5083
5493
|
parseArgs,
|
|
5494
|
+
promptEventCommand,
|
|
5084
5495
|
seatEventCommand,
|
|
5085
5496
|
specEventCommand,
|
|
5086
5497
|
statusLineCommand
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleds/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
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",
|