@sleds/cli 0.1.14 → 0.1.16
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 +354 -254
- package/package.json +17 -6
package/dist/sleds.mjs
CHANGED
|
@@ -24,7 +24,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
24
24
|
var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN", claudeDir = (home, env = process.env) => {
|
|
25
25
|
const o = env.CLAUDE_CONFIG_DIR?.trim();
|
|
26
26
|
return o ? resolve(home, o) : join(home, ".claude");
|
|
27
|
-
}, hasHomeOverride = (env = process.env) => Boolean(env.SLEDS_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim()), settingsPath = (home) => join(claudeDir(home), "settings.json"), sledsCommand = (sub, home, env = process.env) => {
|
|
27
|
+
}, hasHomeOverride = (env = process.env) => Boolean(env.SLEDS_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim()), settingsPath = (home) => join(claudeDir(home), "settings.json"), sledsCommand = (sub, home, env = process.env) => withSeatEnv(`sleds ${sub}`, home, env), withSeatEnv = (command, home, env = process.env) => {
|
|
28
28
|
const parts = [];
|
|
29
29
|
const sh = env.SLEDS_HOME?.trim();
|
|
30
30
|
const cc = env.CLAUDE_CONFIG_DIR?.trim();
|
|
@@ -32,7 +32,7 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
|
32
32
|
parts.push(`SLEDS_HOME=${shellWord(resolve(home, sh))}`);
|
|
33
33
|
if (cc)
|
|
34
34
|
parts.push(`CLAUDE_CONFIG_DIR=${shellWord(resolve(home, cc))}`);
|
|
35
|
-
parts.push(
|
|
35
|
+
parts.push(command);
|
|
36
36
|
return parts.join(" ");
|
|
37
37
|
}, sledsCommandsIn = (settings) => {
|
|
38
38
|
const out = [];
|
|
@@ -303,10 +303,8 @@ var init_config = __esm(() => {
|
|
|
303
303
|
init_settings();
|
|
304
304
|
});
|
|
305
305
|
|
|
306
|
-
// src/
|
|
307
|
-
|
|
308
|
-
import { join as join3 } from "node:path";
|
|
309
|
-
var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, str = (v) => typeof v === "string" ? v : undefined, parseMeter = (raw) => {
|
|
306
|
+
// ../../packages/shared/src/mcp/format.ts
|
|
307
|
+
var isObject3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, str = (v) => typeof v === "string" ? v : undefined, parseMeter = (raw) => {
|
|
310
308
|
if (!isObject3(raw))
|
|
311
309
|
return null;
|
|
312
310
|
const seat = isObject3(raw.seat) ? str(raw.seat.email) : undefined;
|
|
@@ -430,15 +428,29 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject3 = (v) => typeof
|
|
|
430
428
|
if (m.estimateAccuracyPct !== undefined)
|
|
431
429
|
lines.push(row("Estimates:", `${Math.round(m.estimateAccuracyPct)}% landed in band`));
|
|
432
430
|
return lines;
|
|
433
|
-
}
|
|
431
|
+
};
|
|
432
|
+
var init_format = __esm(() => {
|
|
433
|
+
KIND_WORDS = {
|
|
434
|
+
rate_limit: "rate limit hit",
|
|
435
|
+
weekly_cap: "usage limit reached",
|
|
436
|
+
budget: "budget exhausted",
|
|
437
|
+
provider_down: "provider unavailable"
|
|
438
|
+
};
|
|
439
|
+
OFFLINE_LINE = `${PREFIX}offline`;
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// src/meter.ts
|
|
443
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
444
|
+
import { join as join3 } from "node:path";
|
|
445
|
+
var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject4 = (v) => typeof v === "object" && v !== null && !Array.isArray(v), num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined, meterCachePath = (home) => join3(configDir(home), "meter.json"), readMeterCache = (home, now) => {
|
|
434
446
|
const p = meterCachePath(home);
|
|
435
447
|
if (!existsSync3(p))
|
|
436
448
|
return null;
|
|
437
449
|
try {
|
|
438
450
|
const raw = JSON.parse(readFileSync3(p, "utf8"));
|
|
439
|
-
if (!
|
|
451
|
+
if (!isObject4(raw))
|
|
440
452
|
return null;
|
|
441
|
-
const fetchedAt =
|
|
453
|
+
const fetchedAt = num2(raw.fetchedAt);
|
|
442
454
|
if (fetchedAt === undefined || now - fetchedAt >= METER_CACHE_TTL_MS || fetchedAt > now)
|
|
443
455
|
return null;
|
|
444
456
|
return parseMeter(raw.meter);
|
|
@@ -491,15 +503,10 @@ var METER_CACHE_TTL_MS = 1e4, METER_TIMEOUT_MS = 1500, isObject3 = (v) => typeof
|
|
|
491
503
|
return fresh;
|
|
492
504
|
};
|
|
493
505
|
var init_meter = __esm(() => {
|
|
506
|
+
init_format();
|
|
494
507
|
init_config();
|
|
495
508
|
init_settings();
|
|
496
|
-
|
|
497
|
-
rate_limit: "rate limit hit",
|
|
498
|
-
weekly_cap: "usage limit reached",
|
|
499
|
-
budget: "budget exhausted",
|
|
500
|
-
provider_down: "provider unavailable"
|
|
501
|
-
};
|
|
502
|
-
OFFLINE_LINE = `${PREFIX}offline`;
|
|
509
|
+
init_format();
|
|
503
510
|
});
|
|
504
511
|
|
|
505
512
|
// src/seat.ts
|
|
@@ -531,9 +538,9 @@ __export(exports_seat, {
|
|
|
531
538
|
writeSeatState: () => writeSeatState2
|
|
532
539
|
});
|
|
533
540
|
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync10 } from "node:fs";
|
|
534
|
-
import { join as
|
|
535
|
-
var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_MS2 = 1500, STDIN_TIMEOUT_MS2 = 300,
|
|
536
|
-
const n =
|
|
541
|
+
import { join as join11 } from "node:path";
|
|
542
|
+
var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_MS2 = 1500, STDIN_TIMEOUT_MS2 = 300, isObject10 = (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) => {
|
|
543
|
+
const n = num6(v);
|
|
537
544
|
if (n !== undefined) {
|
|
538
545
|
if (n <= 0)
|
|
539
546
|
return;
|
|
@@ -545,28 +552,28 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
545
552
|
}
|
|
546
553
|
return;
|
|
547
554
|
}, parseBar2 = (v) => {
|
|
548
|
-
if (!
|
|
555
|
+
if (!isObject10(v))
|
|
549
556
|
return;
|
|
550
|
-
const usedPct =
|
|
557
|
+
const usedPct = num6(v.used_percentage) ?? num6(v.usedPct);
|
|
551
558
|
const resetsAt = parseResetsAt2(v.resets_at ?? v.resetsAt);
|
|
552
559
|
if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
|
|
553
560
|
return;
|
|
554
561
|
return { usedPct, resetsAt };
|
|
555
562
|
}, parseSeatWindow2 = (raw, capturedAt) => {
|
|
556
|
-
if (!
|
|
563
|
+
if (!isObject10(raw))
|
|
557
564
|
return null;
|
|
558
|
-
const limits =
|
|
565
|
+
const limits = isObject10(raw.rate_limits) ? raw.rate_limits : raw;
|
|
559
566
|
const fiveHour = parseBar2(limits.five_hour ?? limits.fiveHour);
|
|
560
567
|
const sevenDay = parseBar2(limits.seven_day ?? limits.sevenDay);
|
|
561
568
|
const spendLimit = parseBar2(limits.spend_limit ?? limits.spendLimit);
|
|
562
569
|
const models = {};
|
|
563
|
-
if (
|
|
570
|
+
if (isObject10(limits.models))
|
|
564
571
|
for (const [k, v] of Object.entries(limits.models)) {
|
|
565
572
|
const bar = parseBar2(v);
|
|
566
573
|
if (bar)
|
|
567
574
|
models[k] = bar;
|
|
568
575
|
}
|
|
569
|
-
const usedCreditsUsd =
|
|
576
|
+
const usedCreditsUsd = num6(limits.usedCreditsUsd);
|
|
570
577
|
const hasModels = Object.keys(models).length > 0;
|
|
571
578
|
if (!fiveHour && !sevenDay && !spendLimit && !hasModels && usedCreditsUsd === undefined)
|
|
572
579
|
return null;
|
|
@@ -604,28 +611,28 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
604
611
|
if (parts.length === 0)
|
|
605
612
|
return null;
|
|
606
613
|
return `seat ${parts.join(SEP)}`;
|
|
607
|
-
}, seatStatePath2 = (home) =>
|
|
608
|
-
if (!
|
|
614
|
+
}, seatStatePath2 = (home) => join11(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
|
|
615
|
+
if (!isObject10(v))
|
|
609
616
|
return;
|
|
610
|
-
const capturedAt =
|
|
617
|
+
const capturedAt = num6(v.capturedAt);
|
|
611
618
|
if (capturedAt === undefined)
|
|
612
619
|
return;
|
|
613
620
|
return parseSeatWindow2(v, capturedAt) ?? undefined;
|
|
614
621
|
}, parseSeatState2 = (raw) => {
|
|
615
|
-
if (!
|
|
622
|
+
if (!isObject10(raw))
|
|
616
623
|
return {};
|
|
617
624
|
const out = {};
|
|
618
625
|
const window = parseWindowStored2(raw.window);
|
|
619
626
|
if (window)
|
|
620
627
|
out.window = window;
|
|
621
|
-
const lastPostedAt =
|
|
628
|
+
const lastPostedAt = num6(raw.lastPostedAt);
|
|
622
629
|
if (lastPostedAt !== undefined)
|
|
623
630
|
out.lastPostedAt = lastPostedAt;
|
|
624
|
-
if (
|
|
631
|
+
if (isObject10(raw.lastPosted)) {
|
|
625
632
|
const lp = {};
|
|
626
|
-
const f =
|
|
627
|
-
const s =
|
|
628
|
-
const sp =
|
|
633
|
+
const f = num6(raw.lastPosted.fiveHour);
|
|
634
|
+
const s = num6(raw.lastPosted.sevenDay);
|
|
635
|
+
const sp = num6(raw.lastPosted.spendLimit);
|
|
629
636
|
if (f !== undefined)
|
|
630
637
|
lp.fiveHour = f;
|
|
631
638
|
if (s !== undefined)
|
|
@@ -634,7 +641,7 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
634
641
|
lp.spendLimit = sp;
|
|
635
642
|
out.lastPosted = lp;
|
|
636
643
|
}
|
|
637
|
-
const lastWallResetsAt =
|
|
644
|
+
const lastWallResetsAt = num6(raw.lastWallResetsAt);
|
|
638
645
|
if (lastWallResetsAt !== undefined)
|
|
639
646
|
out.lastWallResetsAt = lastWallResetsAt;
|
|
640
647
|
const session = parseSeatSession2(raw.session);
|
|
@@ -642,9 +649,9 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
642
649
|
out.session = session;
|
|
643
650
|
return out;
|
|
644
651
|
}, parseSeatSession2 = (v) => {
|
|
645
|
-
if (!
|
|
652
|
+
if (!isObject10(v) || typeof v.id !== "string" || !v.id)
|
|
646
653
|
return;
|
|
647
|
-
const at =
|
|
654
|
+
const at = num6(v.at);
|
|
648
655
|
if (at === undefined)
|
|
649
656
|
return;
|
|
650
657
|
const out = { id: v.id, at };
|
|
@@ -659,12 +666,12 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
659
666
|
return null;
|
|
660
667
|
try {
|
|
661
668
|
const raw = JSON.parse(trimmed);
|
|
662
|
-
if (!
|
|
669
|
+
if (!isObject10(raw) || typeof raw.session_id !== "string" || !raw.session_id)
|
|
663
670
|
return null;
|
|
664
671
|
const out = { id: raw.session_id, at: now };
|
|
665
672
|
if (typeof raw.cwd === "string" && raw.cwd)
|
|
666
673
|
out.cwd = raw.cwd;
|
|
667
|
-
const model =
|
|
674
|
+
const model = isObject10(raw.model) ? raw.model.id : raw.model;
|
|
668
675
|
if (typeof model === "string" && model)
|
|
669
676
|
out.model = model;
|
|
670
677
|
return out;
|
|
@@ -750,9 +757,9 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
750
757
|
} catch {
|
|
751
758
|
return false;
|
|
752
759
|
}
|
|
753
|
-
}, postSeatWindow2 = (o, w) => postJson2(o, "/proxy/seat", seatWindowBody2(w)), postSeatWall2 = (o, input) => postJson2(o, "/proxy/seat-wall", input), SEAT_WALL_LINE2 = "Seat limit hit. sleds on --until-reset to continue on the org key, or sleds defer to resume here at the reset.", SEAT_WALL_LINE_NO_KEY2 = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.", seatWallLine2 = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE2 : SEAT_WALL_LINE_NO_KEY2, readStdin2 = (timeoutMs, stdin = process.stdin) => new Promise((
|
|
760
|
+
}, postSeatWindow2 = (o, w) => postJson2(o, "/proxy/seat", seatWindowBody2(w)), postSeatWall2 = (o, input) => postJson2(o, "/proxy/seat-wall", input), SEAT_WALL_LINE2 = "Seat limit hit. sleds on --until-reset to continue on the org key, or sleds defer to resume here at the reset.", SEAT_WALL_LINE_NO_KEY2 = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.", seatWallLine2 = (hasUpstreamKey) => hasUpstreamKey ? SEAT_WALL_LINE2 : SEAT_WALL_LINE_NO_KEY2, readStdin2 = (timeoutMs, stdin = process.stdin) => new Promise((resolve5) => {
|
|
754
761
|
if (stdin.isTTY) {
|
|
755
|
-
|
|
762
|
+
resolve5("");
|
|
756
763
|
return;
|
|
757
764
|
}
|
|
758
765
|
let data = "";
|
|
@@ -762,7 +769,7 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
762
769
|
return;
|
|
763
770
|
settled = true;
|
|
764
771
|
clearTimeout(timer);
|
|
765
|
-
|
|
772
|
+
resolve5(data);
|
|
766
773
|
};
|
|
767
774
|
const timer = setTimeout(finish, timeoutMs);
|
|
768
775
|
stdin.setEncoding("utf8");
|
|
@@ -793,13 +800,20 @@ import { hostname } from "node:os";
|
|
|
793
800
|
// src/musher.ts
|
|
794
801
|
import { spawnSync } from "node:child_process";
|
|
795
802
|
import { createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
796
|
-
import { dirname as dirname2, join as join4 } from "node:path";
|
|
803
|
+
import { basename, dirname as dirname2, join as join4 } from "node:path";
|
|
797
804
|
var MUSHER_LABEL = "ai.sleds.musher";
|
|
798
805
|
var MUSHER_INTERVAL_S = 300;
|
|
799
806
|
var DEFAULT_PERMISSION_MODE = "acceptEdits";
|
|
800
807
|
var DEFAULT_TIMEOUT_MINUTES = 90;
|
|
801
808
|
var SUMMARY_MAX = 2000;
|
|
802
|
-
var
|
|
809
|
+
var musherLabel = (env = process.env) => {
|
|
810
|
+
const sh = env.SLEDS_HOME?.trim();
|
|
811
|
+
if (!sh)
|
|
812
|
+
return MUSHER_LABEL;
|
|
813
|
+
const tag = basename(sh).replace(/^\.+/, "").replace(/[^A-Za-z0-9_-]+/g, "-") || "seat";
|
|
814
|
+
return `${MUSHER_LABEL}.${tag}`;
|
|
815
|
+
};
|
|
816
|
+
var musherPlistPath = (home, env = process.env) => join4(home, "Library", "LaunchAgents", `${musherLabel(env)}.plist`);
|
|
803
817
|
var musherLogPath = (configDir2) => join4(configDir2, "musher.log");
|
|
804
818
|
var xmlEscape = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
805
819
|
var musherPath = (home, execPath, currentPath = "") => {
|
|
@@ -817,6 +831,7 @@ var musherPath = (home, execPath, currentPath = "") => {
|
|
|
817
831
|
return [...new Set(parts)].join(":");
|
|
818
832
|
};
|
|
819
833
|
var buildMusherPlist = (o) => {
|
|
834
|
+
const label = o.label ?? MUSHER_LABEL;
|
|
820
835
|
const args = [...o.sledsArgv, "musher", "tick"].map((a) => ` <string>${xmlEscape(a)}</string>`).join(`
|
|
821
836
|
`);
|
|
822
837
|
const env = Object.entries(o.env).filter(([, v]) => v !== "").map(([k, v]) => ` <key>${xmlEscape(k)}</key>
|
|
@@ -827,7 +842,7 @@ var buildMusherPlist = (o) => {
|
|
|
827
842
|
<plist version="1.0">
|
|
828
843
|
<dict>
|
|
829
844
|
<key>Label</key>
|
|
830
|
-
<string>${
|
|
845
|
+
<string>${label}</string>
|
|
831
846
|
<key>ProgramArguments</key>
|
|
832
847
|
<array>
|
|
833
848
|
${args}
|
|
@@ -1036,11 +1051,11 @@ var SEAT_POST_MIN_DELTA_PCT = 1;
|
|
|
1036
1051
|
var SEAT_POST_MIN_INTERVAL_MS = 5 * 60000;
|
|
1037
1052
|
var SEAT_POST_TIMEOUT_MS = 1500;
|
|
1038
1053
|
var STDIN_TIMEOUT_MS = 300;
|
|
1039
|
-
var
|
|
1040
|
-
var
|
|
1054
|
+
var isObject5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1055
|
+
var num3 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
1041
1056
|
var EPOCH_MS_THRESHOLD = 100000000000;
|
|
1042
1057
|
var parseResetsAt = (v) => {
|
|
1043
|
-
const n =
|
|
1058
|
+
const n = num3(v);
|
|
1044
1059
|
if (n !== undefined) {
|
|
1045
1060
|
if (n <= 0)
|
|
1046
1061
|
return;
|
|
@@ -1053,29 +1068,29 @@ var parseResetsAt = (v) => {
|
|
|
1053
1068
|
return;
|
|
1054
1069
|
};
|
|
1055
1070
|
var parseBar = (v) => {
|
|
1056
|
-
if (!
|
|
1071
|
+
if (!isObject5(v))
|
|
1057
1072
|
return;
|
|
1058
|
-
const usedPct =
|
|
1073
|
+
const usedPct = num3(v.used_percentage) ?? num3(v.usedPct);
|
|
1059
1074
|
const resetsAt = parseResetsAt(v.resets_at ?? v.resetsAt);
|
|
1060
1075
|
if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
|
|
1061
1076
|
return;
|
|
1062
1077
|
return { usedPct, resetsAt };
|
|
1063
1078
|
};
|
|
1064
1079
|
var parseSeatWindow = (raw, capturedAt) => {
|
|
1065
|
-
if (!
|
|
1080
|
+
if (!isObject5(raw))
|
|
1066
1081
|
return null;
|
|
1067
|
-
const limits =
|
|
1082
|
+
const limits = isObject5(raw.rate_limits) ? raw.rate_limits : raw;
|
|
1068
1083
|
const fiveHour = parseBar(limits.five_hour ?? limits.fiveHour);
|
|
1069
1084
|
const sevenDay = parseBar(limits.seven_day ?? limits.sevenDay);
|
|
1070
1085
|
const spendLimit = parseBar(limits.spend_limit ?? limits.spendLimit);
|
|
1071
1086
|
const models = {};
|
|
1072
|
-
if (
|
|
1087
|
+
if (isObject5(limits.models))
|
|
1073
1088
|
for (const [k, v] of Object.entries(limits.models)) {
|
|
1074
1089
|
const bar = parseBar(v);
|
|
1075
1090
|
if (bar)
|
|
1076
1091
|
models[k] = bar;
|
|
1077
1092
|
}
|
|
1078
|
-
const usedCreditsUsd =
|
|
1093
|
+
const usedCreditsUsd = num3(limits.usedCreditsUsd);
|
|
1079
1094
|
const hasModels = Object.keys(models).length > 0;
|
|
1080
1095
|
if (!fiveHour && !sevenDay && !spendLimit && !hasModels && usedCreditsUsd === undefined)
|
|
1081
1096
|
return null;
|
|
@@ -1118,28 +1133,28 @@ var formatSeatFragment = (w) => {
|
|
|
1118
1133
|
};
|
|
1119
1134
|
var seatStatePath = (home) => join5(configDir(home), "seat.json");
|
|
1120
1135
|
var parseWindowStored = (v) => {
|
|
1121
|
-
if (!
|
|
1136
|
+
if (!isObject5(v))
|
|
1122
1137
|
return;
|
|
1123
|
-
const capturedAt =
|
|
1138
|
+
const capturedAt = num3(v.capturedAt);
|
|
1124
1139
|
if (capturedAt === undefined)
|
|
1125
1140
|
return;
|
|
1126
1141
|
return parseSeatWindow(v, capturedAt) ?? undefined;
|
|
1127
1142
|
};
|
|
1128
1143
|
var parseSeatState = (raw) => {
|
|
1129
|
-
if (!
|
|
1144
|
+
if (!isObject5(raw))
|
|
1130
1145
|
return {};
|
|
1131
1146
|
const out = {};
|
|
1132
1147
|
const window = parseWindowStored(raw.window);
|
|
1133
1148
|
if (window)
|
|
1134
1149
|
out.window = window;
|
|
1135
|
-
const lastPostedAt =
|
|
1150
|
+
const lastPostedAt = num3(raw.lastPostedAt);
|
|
1136
1151
|
if (lastPostedAt !== undefined)
|
|
1137
1152
|
out.lastPostedAt = lastPostedAt;
|
|
1138
|
-
if (
|
|
1153
|
+
if (isObject5(raw.lastPosted)) {
|
|
1139
1154
|
const lp = {};
|
|
1140
|
-
const f =
|
|
1141
|
-
const s =
|
|
1142
|
-
const sp =
|
|
1155
|
+
const f = num3(raw.lastPosted.fiveHour);
|
|
1156
|
+
const s = num3(raw.lastPosted.sevenDay);
|
|
1157
|
+
const sp = num3(raw.lastPosted.spendLimit);
|
|
1143
1158
|
if (f !== undefined)
|
|
1144
1159
|
lp.fiveHour = f;
|
|
1145
1160
|
if (s !== undefined)
|
|
@@ -1148,7 +1163,7 @@ var parseSeatState = (raw) => {
|
|
|
1148
1163
|
lp.spendLimit = sp;
|
|
1149
1164
|
out.lastPosted = lp;
|
|
1150
1165
|
}
|
|
1151
|
-
const lastWallResetsAt =
|
|
1166
|
+
const lastWallResetsAt = num3(raw.lastWallResetsAt);
|
|
1152
1167
|
if (lastWallResetsAt !== undefined)
|
|
1153
1168
|
out.lastWallResetsAt = lastWallResetsAt;
|
|
1154
1169
|
const session = parseSeatSession(raw.session);
|
|
@@ -1157,9 +1172,9 @@ var parseSeatState = (raw) => {
|
|
|
1157
1172
|
return out;
|
|
1158
1173
|
};
|
|
1159
1174
|
var parseSeatSession = (v) => {
|
|
1160
|
-
if (!
|
|
1175
|
+
if (!isObject5(v) || typeof v.id !== "string" || !v.id)
|
|
1161
1176
|
return;
|
|
1162
|
-
const at =
|
|
1177
|
+
const at = num3(v.at);
|
|
1163
1178
|
if (at === undefined)
|
|
1164
1179
|
return;
|
|
1165
1180
|
const out = { id: v.id, at };
|
|
@@ -1175,12 +1190,12 @@ var seatSessionFromStdin = (text, now) => {
|
|
|
1175
1190
|
return null;
|
|
1176
1191
|
try {
|
|
1177
1192
|
const raw = JSON.parse(trimmed);
|
|
1178
|
-
if (!
|
|
1193
|
+
if (!isObject5(raw) || typeof raw.session_id !== "string" || !raw.session_id)
|
|
1179
1194
|
return null;
|
|
1180
1195
|
const out = { id: raw.session_id, at: now };
|
|
1181
1196
|
if (typeof raw.cwd === "string" && raw.cwd)
|
|
1182
1197
|
out.cwd = raw.cwd;
|
|
1183
|
-
const model =
|
|
1198
|
+
const model = isObject5(raw.model) ? raw.model.id : raw.model;
|
|
1184
1199
|
if (typeof model === "string" && model)
|
|
1185
1200
|
out.model = model;
|
|
1186
1201
|
return out;
|
|
@@ -1307,11 +1322,11 @@ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) =>
|
|
|
1307
1322
|
|
|
1308
1323
|
// src/switch.ts
|
|
1309
1324
|
init_settings();
|
|
1310
|
-
var
|
|
1311
|
-
var envOf2 = (settings) =>
|
|
1325
|
+
var isObject6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1326
|
+
var envOf2 = (settings) => isObject6(settings.env) ? settings.env : {};
|
|
1312
1327
|
var capturePriorEnv = (settings) => {
|
|
1313
1328
|
const env = envOf2(settings);
|
|
1314
|
-
const prior = { hadEnv:
|
|
1329
|
+
const prior = { hadEnv: isObject6(settings.env) };
|
|
1315
1330
|
const url = env[BASE_URL_KEY];
|
|
1316
1331
|
const token = env[AUTH_TOKEN_KEY];
|
|
1317
1332
|
if (typeof url === "string")
|
|
@@ -1500,7 +1515,7 @@ var findClaude = (home, configured, which) => {
|
|
|
1500
1515
|
return null;
|
|
1501
1516
|
};
|
|
1502
1517
|
var musherAgentState = (home, env = process.env) => {
|
|
1503
|
-
const plist = musherPlistPath(home);
|
|
1518
|
+
const plist = musherPlistPath(home, env);
|
|
1504
1519
|
if (!existsSync6(plist))
|
|
1505
1520
|
return { installed: false, thisSeat: false };
|
|
1506
1521
|
try {
|
|
@@ -1696,7 +1711,11 @@ async function runDoctor(o) {
|
|
|
1696
1711
|
name: "musher",
|
|
1697
1712
|
detail: "agent installed for a different seat (SLEDS_HOME differs)",
|
|
1698
1713
|
fix: "sleds musher --install"
|
|
1699
|
-
} : {
|
|
1714
|
+
} : {
|
|
1715
|
+
level: "ok",
|
|
1716
|
+
name: "musher",
|
|
1717
|
+
detail: `launchd agent ${musherLabel(env)}, every 5 min`
|
|
1718
|
+
});
|
|
1700
1719
|
const claude = findClaude(home, readConfig(home).musher?.claudePath, (c) => (o.which ?? defaultWhich2)(c)[0] ?? null);
|
|
1701
1720
|
checks.push(claude ? { level: "ok", name: "claude cli", detail: claude } : {
|
|
1702
1721
|
level: "warn",
|
|
@@ -2174,8 +2193,62 @@ var init = async (o) => {
|
|
|
2174
2193
|
return 0;
|
|
2175
2194
|
};
|
|
2176
2195
|
|
|
2177
|
-
// src/mcp.ts
|
|
2178
|
-
|
|
2196
|
+
// ../../packages/shared/src/mcp/server.ts
|
|
2197
|
+
init_format();
|
|
2198
|
+
function fetchClient(o) {
|
|
2199
|
+
const fetchFn = o.fetchFn ?? fetch;
|
|
2200
|
+
const base = o.proxyUrl.replace(/\/+$/, "");
|
|
2201
|
+
const headers = { "content-type": "application/json", authorization: `Bearer ${o.token}` };
|
|
2202
|
+
const timeout = () => AbortSignal.timeout(o.timeoutMs ?? MCP_TIMEOUT_MS);
|
|
2203
|
+
return {
|
|
2204
|
+
async post(path, body) {
|
|
2205
|
+
try {
|
|
2206
|
+
const res = await fetchFn(`${base}${path}`, {
|
|
2207
|
+
method: "POST",
|
|
2208
|
+
headers,
|
|
2209
|
+
body: JSON.stringify(body),
|
|
2210
|
+
signal: timeout()
|
|
2211
|
+
});
|
|
2212
|
+
if (!res.ok)
|
|
2213
|
+
return null;
|
|
2214
|
+
const parsed = await res.json();
|
|
2215
|
+
return isObject7(parsed) ? parsed : null;
|
|
2216
|
+
} catch {
|
|
2217
|
+
return null;
|
|
2218
|
+
}
|
|
2219
|
+
},
|
|
2220
|
+
async postFull(path, body) {
|
|
2221
|
+
try {
|
|
2222
|
+
const res = await fetchFn(`${base}${path}`, {
|
|
2223
|
+
method: "POST",
|
|
2224
|
+
headers,
|
|
2225
|
+
body: JSON.stringify(body),
|
|
2226
|
+
signal: timeout()
|
|
2227
|
+
});
|
|
2228
|
+
let json = null;
|
|
2229
|
+
try {
|
|
2230
|
+
json = await res.json();
|
|
2231
|
+
} catch {
|
|
2232
|
+
json = null;
|
|
2233
|
+
}
|
|
2234
|
+
return { status: res.status, json: isObject7(json) ? json : null };
|
|
2235
|
+
} catch {
|
|
2236
|
+
return null;
|
|
2237
|
+
}
|
|
2238
|
+
},
|
|
2239
|
+
async get(path) {
|
|
2240
|
+
try {
|
|
2241
|
+
const res = await fetchFn(`${base}${path}`, { headers, signal: timeout() });
|
|
2242
|
+
if (!res.ok)
|
|
2243
|
+
return null;
|
|
2244
|
+
const parsed = await res.json();
|
|
2245
|
+
return isObject7(parsed) ? parsed : null;
|
|
2246
|
+
} catch {
|
|
2247
|
+
return null;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2179
2252
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
2180
2253
|
var SERVER_INFO = { name: "sleds", version: "0.0.1" };
|
|
2181
2254
|
var MCP_TIMEOUT_MS = 5000;
|
|
@@ -2248,6 +2321,24 @@ var TOOLS = [
|
|
|
2248
2321
|
required: ["task"]
|
|
2249
2322
|
}
|
|
2250
2323
|
},
|
|
2324
|
+
{
|
|
2325
|
+
name: "defer",
|
|
2326
|
+
description: "Hit a limit? Defer the work to the reset — the musher runs it then. With an org key it runs in the cloud on the pinned model; without one, the seat's own Mac resumes the last Claude Code session at the reset (sleds musher). Give `at` as +2h, +90m or an ISO time to override the binding reset.",
|
|
2327
|
+
inputSchema: {
|
|
2328
|
+
type: "object",
|
|
2329
|
+
properties: {
|
|
2330
|
+
at: {
|
|
2331
|
+
type: "string",
|
|
2332
|
+
description: "When to start: +2h, +90m, or an ISO timestamp. Omit for the binding reset."
|
|
2333
|
+
},
|
|
2334
|
+
model: { type: "string", description: "Pin the model the deferred run must use." },
|
|
2335
|
+
task: {
|
|
2336
|
+
type: "string",
|
|
2337
|
+
description: "Optional note on what the deferred run should finish (kept with the job)."
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
},
|
|
2251
2342
|
{
|
|
2252
2343
|
name: "pickup",
|
|
2253
2344
|
description: "Take an open hand-off and get its context to continue here: a parked task comes back verbatim; a walled Claude Code session comes back as its first and last user messages. Marks it picked for the team.",
|
|
@@ -2259,109 +2350,37 @@ var TOOLS = [
|
|
|
2259
2350
|
}
|
|
2260
2351
|
];
|
|
2261
2352
|
var NOT_SET_UP = {
|
|
2262
|
-
text: "SLEDS is not
|
|
2353
|
+
text: "SLEDS is not connected to a seat here. Run `sleds init` (CLI) or connect SLEDS as a connector.",
|
|
2263
2354
|
isError: true
|
|
2264
2355
|
};
|
|
2265
2356
|
var UNREACHABLE = {
|
|
2266
2357
|
text: "Control plane unreachable. Nothing was recorded → try again in a moment.",
|
|
2267
2358
|
isError: true
|
|
2268
2359
|
};
|
|
2269
|
-
var
|
|
2270
|
-
var
|
|
2360
|
+
var isObject7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2361
|
+
var num4 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
2271
2362
|
var str2 = (v) => typeof v === "string" ? v : undefined;
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
if (
|
|
2275
|
-
return
|
|
2276
|
-
|
|
2277
|
-
try {
|
|
2278
|
-
const res = await fetchFn(`${src.proxyUrl}${path}`, {
|
|
2279
|
-
method: "POST",
|
|
2280
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${src.token}` },
|
|
2281
|
-
body: JSON.stringify(body),
|
|
2282
|
-
signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_TIMEOUT_MS)
|
|
2283
|
-
});
|
|
2284
|
-
if (!res.ok)
|
|
2285
|
-
return null;
|
|
2286
|
-
const parsed = await res.json();
|
|
2287
|
-
return isObject6(parsed) ? parsed : null;
|
|
2288
|
-
} catch {
|
|
2289
|
-
return null;
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
async function proxyPostFull(deps, path, body) {
|
|
2293
|
-
const src = meterSource(deps.home);
|
|
2294
|
-
if (!src)
|
|
2295
|
-
return null;
|
|
2296
|
-
const fetchFn = deps.fetchFn ?? fetch;
|
|
2297
|
-
try {
|
|
2298
|
-
const res = await fetchFn(`${src.proxyUrl}${path}`, {
|
|
2299
|
-
method: "POST",
|
|
2300
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${src.token}` },
|
|
2301
|
-
body: JSON.stringify(body),
|
|
2302
|
-
signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_TIMEOUT_MS)
|
|
2303
|
-
});
|
|
2304
|
-
let json = null;
|
|
2305
|
-
try {
|
|
2306
|
-
json = await res.json();
|
|
2307
|
-
} catch {
|
|
2308
|
-
json = null;
|
|
2309
|
-
}
|
|
2310
|
-
return { status: res.status, json: isObject6(json) ? json : null };
|
|
2311
|
-
} catch {
|
|
2312
|
-
return null;
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
var errorMessage = (j) => isObject6(j?.error) ? str2(j.error.message) : undefined;
|
|
2316
|
-
async function proxyGet(deps, path) {
|
|
2317
|
-
const src = meterSource(deps.home);
|
|
2318
|
-
if (!src)
|
|
2319
|
-
return null;
|
|
2320
|
-
const fetchFn = deps.fetchFn ?? fetch;
|
|
2321
|
-
try {
|
|
2322
|
-
const res = await fetchFn(`${src.proxyUrl}${path}`, {
|
|
2323
|
-
headers: { authorization: `Bearer ${src.token}` },
|
|
2324
|
-
signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_TIMEOUT_MS)
|
|
2325
|
-
});
|
|
2326
|
-
if (!res.ok)
|
|
2327
|
-
return null;
|
|
2328
|
-
const parsed = await res.json();
|
|
2329
|
-
return isObject6(parsed) ? parsed : null;
|
|
2330
|
-
} catch {
|
|
2331
|
-
return null;
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
async function usageTool(deps) {
|
|
2335
|
-
if (!meterSource(deps.home))
|
|
2363
|
+
var errorMessage = (j) => isObject7(j?.error) ? str2(j.error.message) : undefined;
|
|
2364
|
+
async function usageTool(ctx) {
|
|
2365
|
+
if (ctx.usage)
|
|
2366
|
+
return ctx.usage();
|
|
2367
|
+
if (!ctx.client)
|
|
2336
2368
|
return NOT_SET_UP;
|
|
2337
|
-
const
|
|
2338
|
-
const m =
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
timeoutMs: deps.timeoutMs
|
|
2343
|
-
});
|
|
2344
|
-
const frag = formatSeatFragment(readSeatState(deps.home).window ?? null);
|
|
2345
|
-
if (!m) {
|
|
2346
|
-
return {
|
|
2347
|
-
text: frag ? `sleds offline (proxy did not answer) · ${frag}` : "sleds offline (proxy did not answer).",
|
|
2348
|
-
isError: true
|
|
2349
|
-
};
|
|
2350
|
-
}
|
|
2351
|
-
const lines = formatMeter(m, now);
|
|
2352
|
-
if (frag)
|
|
2353
|
-
lines.push(`${"Limits:".padEnd(12)}${frag.replace(/^seat /, "")}`);
|
|
2354
|
-
return { text: lines.join(`
|
|
2369
|
+
const raw = await ctx.client.get("/proxy/meter");
|
|
2370
|
+
const m = raw ? parseMeter(raw) : null;
|
|
2371
|
+
if (!m)
|
|
2372
|
+
return { text: "sleds offline (proxy did not answer).", isError: true };
|
|
2373
|
+
return { text: formatMeter(m, (ctx.now ?? Date.now)()).join(`
|
|
2355
2374
|
`) };
|
|
2356
2375
|
}
|
|
2357
2376
|
function bandLine(band) {
|
|
2358
|
-
if (!
|
|
2377
|
+
if (!isObject7(band))
|
|
2359
2378
|
return null;
|
|
2360
|
-
const low =
|
|
2361
|
-
const high =
|
|
2362
|
-
const lowM =
|
|
2363
|
-
const highM =
|
|
2364
|
-
const conf =
|
|
2379
|
+
const low = num4(band.lowUsd);
|
|
2380
|
+
const high = num4(band.highUsd);
|
|
2381
|
+
const lowM = num4(band.lowMinutes);
|
|
2382
|
+
const highM = num4(band.highMinutes);
|
|
2383
|
+
const conf = num4(band.confidence);
|
|
2365
2384
|
if (low === undefined || high === undefined)
|
|
2366
2385
|
return null;
|
|
2367
2386
|
const d = high >= 10 ? 0 : 2;
|
|
@@ -2369,26 +2388,26 @@ function bandLine(band) {
|
|
|
2369
2388
|
const pct = conf !== undefined ? ` · ${Math.round(conf * 100)}%` : "";
|
|
2370
2389
|
return `$${low.toFixed(d)}–${high.toFixed(d)}${mins}${pct}`;
|
|
2371
2390
|
}
|
|
2372
|
-
async function estimateTool(
|
|
2373
|
-
if (!
|
|
2391
|
+
async function estimateTool(ctx, args) {
|
|
2392
|
+
if (!ctx.client)
|
|
2374
2393
|
return NOT_SET_UP;
|
|
2375
2394
|
const spec = str2(args.spec)?.trim();
|
|
2376
2395
|
if (!spec)
|
|
2377
2396
|
return { text: "Nothing to estimate. Pass spec: what the job should do.", isError: true };
|
|
2378
|
-
const source =
|
|
2379
|
-
const r = await
|
|
2397
|
+
const source = isObject7(args.source) && str2(args.source.kind) && str2(args.source.project) ? args.source : undefined;
|
|
2398
|
+
const r = await ctx.client.post("/proxy/estimate", {
|
|
2380
2399
|
spec,
|
|
2381
2400
|
model: str2(args.model),
|
|
2382
2401
|
...source ? { source } : {}
|
|
2383
2402
|
});
|
|
2384
2403
|
if (!r)
|
|
2385
2404
|
return UNREACHABLE;
|
|
2386
|
-
const band =
|
|
2405
|
+
const band = isObject7(r.band) ? r.band : {};
|
|
2387
2406
|
const line = bandLine(band) ?? "band unreadable";
|
|
2388
2407
|
const method = str2(band.method) ?? "plan";
|
|
2389
|
-
const cap =
|
|
2390
|
-
const threshold =
|
|
2391
|
-
const over = threshold !== undefined && threshold > 0 &&
|
|
2408
|
+
const cap = num4(band.capUsd);
|
|
2409
|
+
const threshold = num4(r.approvalThresholdUsd);
|
|
2410
|
+
const over = threshold !== undefined && threshold > 0 && num4(band.highUsd) !== undefined ? ` Over the ${usd(threshold)} threshold? ${num4(band.highUsd) > threshold ? "yes" : "no"}.` : "";
|
|
2392
2411
|
const assumptions = Array.isArray(band.assumptions) ? band.assumptions.filter((a) => typeof a === "string") : [];
|
|
2393
2412
|
const tail = assumptions.length > 0 ? `
|
|
2394
2413
|
Assumes: ${assumptions.join("; ")}.` : "";
|
|
@@ -2406,11 +2425,11 @@ function byModelText(raw) {
|
|
|
2406
2425
|
return "";
|
|
2407
2426
|
const parts = [];
|
|
2408
2427
|
for (const row2 of raw) {
|
|
2409
|
-
if (!
|
|
2428
|
+
if (!isObject7(row2) || !isObject7(row2.standard))
|
|
2410
2429
|
continue;
|
|
2411
2430
|
const model = str2(row2.model);
|
|
2412
|
-
const low =
|
|
2413
|
-
const high =
|
|
2431
|
+
const low = num4(row2.standard.lowUsd);
|
|
2432
|
+
const high = num4(row2.standard.highUsd);
|
|
2414
2433
|
if (!model || low === undefined || high === undefined)
|
|
2415
2434
|
continue;
|
|
2416
2435
|
const m = /^claude-([a-z]+)-(\d+)(?:-(\d+))?/.exec(model);
|
|
@@ -2421,7 +2440,7 @@ function byModelText(raw) {
|
|
|
2421
2440
|
By model: ${parts.join(" · ")} (overnight batch ½).` : "";
|
|
2422
2441
|
}
|
|
2423
2442
|
function routingText(raw) {
|
|
2424
|
-
if (!
|
|
2443
|
+
if (!isObject7(raw) || !Array.isArray(raw.segments) || !isObject7(raw.cost))
|
|
2425
2444
|
return "";
|
|
2426
2445
|
const short = (model) => {
|
|
2427
2446
|
const m = /^claude-([a-z]+)-(\d+)(?:-(\d+))?/.exec(model);
|
|
@@ -2429,10 +2448,10 @@ function routingText(raw) {
|
|
|
2429
2448
|
};
|
|
2430
2449
|
const parts = [];
|
|
2431
2450
|
for (const s of raw.segments) {
|
|
2432
|
-
if (!
|
|
2451
|
+
if (!isObject7(s))
|
|
2433
2452
|
continue;
|
|
2434
|
-
const from =
|
|
2435
|
-
const to =
|
|
2453
|
+
const from = num4(s.from);
|
|
2454
|
+
const to = num4(s.to);
|
|
2436
2455
|
const model = str2(s.model);
|
|
2437
2456
|
if (from === undefined || to === undefined || !model)
|
|
2438
2457
|
continue;
|
|
@@ -2440,55 +2459,55 @@ function routingText(raw) {
|
|
|
2440
2459
|
parts.push(`${from === to ? from : `${from}–${to}`} ${short(model)}${reasons.length ? ` (${reasons.slice(0, 3).join(", ")})` : ""}`);
|
|
2441
2460
|
}
|
|
2442
2461
|
const c = raw.cost;
|
|
2443
|
-
const lo =
|
|
2444
|
-
const hi =
|
|
2445
|
-
const slo =
|
|
2446
|
-
const shi =
|
|
2462
|
+
const lo = num4(c.lowUsd);
|
|
2463
|
+
const hi = num4(c.highUsd);
|
|
2464
|
+
const slo = num4(c.singleLowUsd);
|
|
2465
|
+
const shi = num4(c.singleHighUsd);
|
|
2447
2466
|
const single = str2(c.singleModel);
|
|
2448
2467
|
if (parts.length === 0 || lo === undefined || hi === undefined || slo === undefined || shi === undefined || !single)
|
|
2449
2468
|
return "";
|
|
2450
2469
|
return `
|
|
2451
2470
|
Routing: ${parts.join(" · ")} ≈ ${usdRange(lo, hi)} vs ${usdRange(slo, shi)} all ${short(single)}.`;
|
|
2452
2471
|
}
|
|
2453
|
-
async function overnightTool(
|
|
2454
|
-
if (!
|
|
2472
|
+
async function overnightTool(ctx, args) {
|
|
2473
|
+
if (!ctx.client)
|
|
2455
2474
|
return NOT_SET_UP;
|
|
2456
2475
|
const spec = str2(args.spec)?.trim();
|
|
2457
2476
|
if (!spec)
|
|
2458
2477
|
return { text: "Nothing to queue. Pass spec: what the job should do.", isError: true };
|
|
2459
|
-
const r = await
|
|
2478
|
+
const r = await ctx.client.post("/proxy/overnight", { spec, model: str2(args.model) });
|
|
2460
2479
|
if (!r)
|
|
2461
2480
|
return { text: `Could not queue the job. ${UNREACHABLE.text}`, isError: true };
|
|
2462
2481
|
const jobId = str2(r.jobId) ?? "unknown";
|
|
2463
|
-
const now = (
|
|
2464
|
-
const at =
|
|
2482
|
+
const now = (ctx.now ?? Date.now)();
|
|
2483
|
+
const at = num4(r.scheduledAt);
|
|
2465
2484
|
const when = at !== undefined && at > now ? `Runs in ${shortDuration(at - now)}` : "Runs at the next tick";
|
|
2466
2485
|
const band = bandLine(r.band);
|
|
2467
|
-
const cap =
|
|
2486
|
+
const cap = isObject7(r.band) ? num4(r.band.capUsd) : undefined;
|
|
2468
2487
|
const sized = band ? ` ${band}${cap !== undefined ? ` · cap ${usd(cap)}` : ""}.` : "";
|
|
2469
2488
|
return {
|
|
2470
2489
|
text: `Queued overnight. Batch tier · ½ price.${sized} ${when}. Check with the jobs tool; pull the result into a session with /sled resume ${jobId}.`
|
|
2471
2490
|
};
|
|
2472
2491
|
}
|
|
2473
|
-
async function jobsTool(
|
|
2474
|
-
if (!
|
|
2492
|
+
async function jobsTool(ctx) {
|
|
2493
|
+
if (!ctx.client)
|
|
2475
2494
|
return NOT_SET_UP;
|
|
2476
|
-
const r = await
|
|
2495
|
+
const r = await ctx.client.get("/proxy/jobs");
|
|
2477
2496
|
if (!r)
|
|
2478
2497
|
return UNREACHABLE;
|
|
2479
|
-
const rows = Array.isArray(r.jobs) ? r.jobs.filter(
|
|
2480
|
-
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(
|
|
2481
|
-
const now = (
|
|
2498
|
+
const rows = Array.isArray(r.jobs) ? r.jobs.filter(isObject7) : [];
|
|
2499
|
+
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(isObject7) : [];
|
|
2500
|
+
const now = (ctx.now ?? Date.now)();
|
|
2482
2501
|
if (rows.length === 0 && handoffs.length === 0)
|
|
2483
2502
|
return { text: "No jobs for this seat. Queue one with the overnight tool." };
|
|
2484
2503
|
if (rows.length === 0)
|
|
2485
2504
|
return { text: ["No jobs for this seat.", ...handoffLines(handoffs, now)].join(`
|
|
2486
2505
|
`) };
|
|
2487
2506
|
const lines = rows.map((j) => {
|
|
2488
|
-
const when = (ts) =>
|
|
2507
|
+
const when = (ts) => num4(ts) !== undefined ? shortDuration(Math.abs(now - num4(ts))) : "";
|
|
2489
2508
|
const status = str2(j.status) ?? "unknown";
|
|
2490
|
-
const cost =
|
|
2491
|
-
const tail = status === "delivered" ? `delivered ${when(j.deliveredAt)} ago` : status === "scheduled" ?
|
|
2509
|
+
const cost = num4(j.costUsd);
|
|
2510
|
+
const tail = status === "delivered" ? `delivered ${when(j.deliveredAt)} ago` : status === "scheduled" ? num4(j.scheduledAt) !== undefined && num4(j.scheduledAt) > now ? `runs in ${when(j.scheduledAt)}` : "runs at the next tick" : str2(j.reason) ?? "";
|
|
2492
2511
|
return [
|
|
2493
2512
|
str2(j.jobId) ?? "?",
|
|
2494
2513
|
status.padEnd(12),
|
|
@@ -2510,7 +2529,7 @@ function handoffLines(handoffs, now) {
|
|
|
2510
2529
|
const forMe = h.forMe === true;
|
|
2511
2530
|
const to = str2(h.to);
|
|
2512
2531
|
const who = forMe ? to ? "for you" : "parked for anyone" : `for ${to ?? "anyone"}`;
|
|
2513
|
-
const created =
|
|
2532
|
+
const created = num4(h.createdAt);
|
|
2514
2533
|
const age = created !== undefined ? `${shortDuration(Math.max(0, now - created))} ago` : "";
|
|
2515
2534
|
const note = str2(h.note);
|
|
2516
2535
|
const parts = [`from ${str2(h.from) ?? "?"}`, who, str2(h.wallKind) ?? "", age].filter(Boolean);
|
|
@@ -2521,16 +2540,16 @@ function handoffLines(handoffs, now) {
|
|
|
2521
2540
|
out.push("Take one in Claude Code with /sled pickup <id>.");
|
|
2522
2541
|
return out;
|
|
2523
2542
|
}
|
|
2524
|
-
async function handoffsTool(
|
|
2525
|
-
if (!
|
|
2543
|
+
async function handoffsTool(ctx) {
|
|
2544
|
+
if (!ctx.client)
|
|
2526
2545
|
return NOT_SET_UP;
|
|
2527
|
-
const r = await
|
|
2546
|
+
const r = await ctx.client.get("/proxy/jobs");
|
|
2528
2547
|
if (!r)
|
|
2529
2548
|
return UNREACHABLE;
|
|
2530
|
-
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(
|
|
2549
|
+
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(isObject7) : [];
|
|
2531
2550
|
if (handoffs.length === 0)
|
|
2532
2551
|
return { text: "No open hand-offs on your team. Park one with the handoff tool." };
|
|
2533
|
-
const now = (
|
|
2552
|
+
const now = (ctx.now ?? Date.now)();
|
|
2534
2553
|
const packets = handoffs.map((h) => str2(h.packet)).filter((p) => Boolean(p)).join(`
|
|
2535
2554
|
|
|
2536
2555
|
---
|
|
@@ -2543,14 +2562,14 @@ ${packets}` : ""].join(`
|
|
|
2543
2562
|
`)
|
|
2544
2563
|
};
|
|
2545
2564
|
}
|
|
2546
|
-
async function handoffTool(
|
|
2547
|
-
if (!
|
|
2565
|
+
async function handoffTool(ctx, args) {
|
|
2566
|
+
if (!ctx.client)
|
|
2548
2567
|
return NOT_SET_UP;
|
|
2549
2568
|
const task = str2(args.task)?.trim();
|
|
2550
2569
|
if (!task)
|
|
2551
2570
|
return { text: "Nothing to hand off. Pass task: what the picker should do.", isError: true };
|
|
2552
2571
|
const to = str2(args.to)?.trim();
|
|
2553
|
-
const r = await
|
|
2572
|
+
const r = await ctx.client.postFull("/proxy/handoff/park", {
|
|
2554
2573
|
task,
|
|
2555
2574
|
to: to || undefined,
|
|
2556
2575
|
note: str2(args.note)?.trim() || undefined
|
|
@@ -2569,13 +2588,13 @@ async function handoffTool(deps, args) {
|
|
|
2569
2588
|
text: `${to ? `Handed off to ${to}` : "Parked for the team"}. They take it in Claude Code with /sled pickup ${id}, or with the pickup tool here. The team was told on Slack.`
|
|
2570
2589
|
};
|
|
2571
2590
|
}
|
|
2572
|
-
async function pickupTool(
|
|
2573
|
-
if (!
|
|
2591
|
+
async function pickupTool(ctx, args) {
|
|
2592
|
+
if (!ctx.client)
|
|
2574
2593
|
return NOT_SET_UP;
|
|
2575
2594
|
const handoffId = str2(args.handoffId)?.trim();
|
|
2576
2595
|
if (!handoffId)
|
|
2577
2596
|
return { text: "Pass handoffId, from the handoffs tool.", isError: true };
|
|
2578
|
-
const r = await
|
|
2597
|
+
const r = await ctx.client.postFull("/proxy/handoff/pickup", { handoffId });
|
|
2579
2598
|
if (!r)
|
|
2580
2599
|
return { text: `Nothing was picked up. ${UNREACHABLE.text}`, isError: true };
|
|
2581
2600
|
if (r.status !== 200)
|
|
@@ -2596,7 +2615,7 @@ ${str2(j.task) ?? ""}
|
|
|
2596
2615
|
Carry it through from here.` };
|
|
2597
2616
|
const first = str2(j.firstUserText) ?? "";
|
|
2598
2617
|
const last = str2(j.lastUserText) ?? "";
|
|
2599
|
-
const n =
|
|
2618
|
+
const n = num4(j.messageCount);
|
|
2600
2619
|
return {
|
|
2601
2620
|
text: [
|
|
2602
2621
|
`${head} This was a Claude Code session${n !== undefined ? ` (${n} messages)` : ""} that hit a wall; its snapshot can also be replayed in Claude Code with /sled pickup ${handoffId} — but it is now marked picked by you.`,
|
|
@@ -2611,29 +2630,60 @@ ${last}`] : [],
|
|
|
2611
2630
|
`)
|
|
2612
2631
|
};
|
|
2613
2632
|
}
|
|
2614
|
-
|
|
2633
|
+
function parseAt2(v, now) {
|
|
2634
|
+
const rel = /^\+(\d+)([hm])$/.exec(v.trim());
|
|
2635
|
+
if (rel)
|
|
2636
|
+
return now + Number.parseInt(rel[1] ?? "0", 10) * (rel[2] === "h" ? 3600000 : 60000);
|
|
2637
|
+
const t = Date.parse(v);
|
|
2638
|
+
return Number.isFinite(t) ? t : undefined;
|
|
2639
|
+
}
|
|
2640
|
+
async function deferTool(ctx, args) {
|
|
2641
|
+
if (!ctx.client)
|
|
2642
|
+
return NOT_SET_UP;
|
|
2643
|
+
const now = (ctx.now ?? Date.now)();
|
|
2644
|
+
const atRaw = str2(args.at)?.trim();
|
|
2645
|
+
const at = atRaw ? parseAt2(atRaw, now) : undefined;
|
|
2646
|
+
if (atRaw && at === undefined)
|
|
2647
|
+
return { text: "`at` takes +2h, +90m or an ISO time.", isError: true };
|
|
2648
|
+
const r = await ctx.client.post("/proxy/defer", {
|
|
2649
|
+
...at !== undefined ? { resetAt: at } : {},
|
|
2650
|
+
...str2(args.model) ? { model: str2(args.model) } : {},
|
|
2651
|
+
host: "mcp"
|
|
2652
|
+
});
|
|
2653
|
+
if (!r)
|
|
2654
|
+
return { text: `Nothing was deferred. ${UNREACHABLE.text}`, isError: true };
|
|
2655
|
+
const jobId = str2(r.jobId) ?? "unknown";
|
|
2656
|
+
const when = num4(r.scheduledAt);
|
|
2657
|
+
const startsIn = when !== undefined && when > now + 60000 ? `starts in ${shortDuration(when - now)}` : "starts at the next musher tick (within 5 min)";
|
|
2658
|
+
return {
|
|
2659
|
+
text: `Deferred. Job ${jobId} ${startsIn}. With no org key it runs on the seat's own Mac (sleds musher must be installed there — sleds doctor checks); with a key, in the cloud. Watch it with the jobs tool or /sled jobs.${str2(args.task) ? ` Note kept: ${str2(args.task)}` : ""}`
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
async function callTool(name, args, ctx) {
|
|
2615
2663
|
switch (name) {
|
|
2616
2664
|
case "usage":
|
|
2617
|
-
return usageTool(
|
|
2665
|
+
return usageTool(ctx);
|
|
2618
2666
|
case "estimate":
|
|
2619
|
-
return estimateTool(
|
|
2667
|
+
return estimateTool(ctx, args);
|
|
2620
2668
|
case "overnight":
|
|
2621
|
-
return overnightTool(
|
|
2669
|
+
return overnightTool(ctx, args);
|
|
2622
2670
|
case "jobs":
|
|
2623
|
-
return jobsTool(
|
|
2671
|
+
return jobsTool(ctx);
|
|
2624
2672
|
case "handoffs":
|
|
2625
|
-
return handoffsTool(
|
|
2673
|
+
return handoffsTool(ctx);
|
|
2626
2674
|
case "handoff":
|
|
2627
|
-
return handoffTool(
|
|
2675
|
+
return handoffTool(ctx, args);
|
|
2628
2676
|
case "pickup":
|
|
2629
|
-
return pickupTool(
|
|
2677
|
+
return pickupTool(ctx, args);
|
|
2678
|
+
case "defer":
|
|
2679
|
+
return deferTool(ctx, args);
|
|
2630
2680
|
default:
|
|
2631
2681
|
return { text: `Unknown tool: ${name}`, isError: true };
|
|
2632
2682
|
}
|
|
2633
2683
|
}
|
|
2634
2684
|
var response = (id, result) => JSON.stringify({ jsonrpc: "2.0", id, result });
|
|
2635
2685
|
var errorResponse = (id, code, message) => JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
|
|
2636
|
-
async function handleLine(line,
|
|
2686
|
+
async function handleLine(line, ctx) {
|
|
2637
2687
|
const trimmed = line.trim();
|
|
2638
2688
|
if (trimmed.length === 0)
|
|
2639
2689
|
return null;
|
|
@@ -2643,7 +2693,7 @@ async function handleLine(line, deps) {
|
|
|
2643
2693
|
} catch {
|
|
2644
2694
|
return errorResponse(null, -32700, "parse error");
|
|
2645
2695
|
}
|
|
2646
|
-
if (!
|
|
2696
|
+
if (!isObject7(msg))
|
|
2647
2697
|
return errorResponse(null, -32600, "invalid request");
|
|
2648
2698
|
const id = typeof msg.id === "string" || typeof msg.id === "number" ? msg.id : null;
|
|
2649
2699
|
const method = str2(msg.method);
|
|
@@ -2652,7 +2702,7 @@ async function handleLine(line, deps) {
|
|
|
2652
2702
|
return isNotification ? null : errorResponse(id, -32600, "invalid request");
|
|
2653
2703
|
if (method.startsWith("notifications/"))
|
|
2654
2704
|
return null;
|
|
2655
|
-
const params =
|
|
2705
|
+
const params = isObject7(msg.params) ? msg.params : {};
|
|
2656
2706
|
switch (method) {
|
|
2657
2707
|
case "initialize": {
|
|
2658
2708
|
const requested = str2(params.protocolVersion);
|
|
@@ -2668,8 +2718,8 @@ async function handleLine(line, deps) {
|
|
|
2668
2718
|
return response(id, { tools: TOOLS });
|
|
2669
2719
|
case "tools/call": {
|
|
2670
2720
|
const name = str2(params.name) ?? "";
|
|
2671
|
-
const args =
|
|
2672
|
-
const r = await callTool(name, args,
|
|
2721
|
+
const args = isObject7(params.arguments) ? params.arguments : {};
|
|
2722
|
+
const r = await callTool(name, args, ctx);
|
|
2673
2723
|
return response(id, {
|
|
2674
2724
|
content: [{ type: "text", text: r.text }],
|
|
2675
2725
|
...r.isError ? { isError: true } : {}
|
|
@@ -2680,6 +2730,38 @@ async function handleLine(line, deps) {
|
|
|
2680
2730
|
}
|
|
2681
2731
|
}
|
|
2682
2732
|
|
|
2733
|
+
// src/mcp.ts
|
|
2734
|
+
init_meter();
|
|
2735
|
+
async function usageTool2(deps) {
|
|
2736
|
+
if (!meterSource(deps.home))
|
|
2737
|
+
return { text: "SLEDS is not set up on this machine. Run `sleds init` first.", isError: true };
|
|
2738
|
+
const now = (deps.now ?? Date.now)();
|
|
2739
|
+
const m = await loadMeter({
|
|
2740
|
+
home: deps.home,
|
|
2741
|
+
now,
|
|
2742
|
+
fetchFn: deps.fetchFn,
|
|
2743
|
+
timeoutMs: deps.timeoutMs
|
|
2744
|
+
});
|
|
2745
|
+
const frag = formatSeatFragment(readSeatState(deps.home).window ?? null);
|
|
2746
|
+
if (!m) {
|
|
2747
|
+
return {
|
|
2748
|
+
text: frag ? `sleds offline (proxy did not answer) · ${frag}` : "sleds offline (proxy did not answer).",
|
|
2749
|
+
isError: true
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
const lines = formatMeter(m, now);
|
|
2753
|
+
if (frag)
|
|
2754
|
+
lines.push(`${"Limits:".padEnd(12)}${frag.replace(/^seat /, "")}`);
|
|
2755
|
+
return { text: lines.join(`
|
|
2756
|
+
`) };
|
|
2757
|
+
}
|
|
2758
|
+
function contextFor(deps) {
|
|
2759
|
+
const src = meterSource(deps.home);
|
|
2760
|
+
const client = src ? fetchClient({ ...src, fetchFn: deps.fetchFn, timeoutMs: deps.timeoutMs }) : null;
|
|
2761
|
+
return { client, now: deps.now, usage: () => usageTool2(deps) };
|
|
2762
|
+
}
|
|
2763
|
+
var handleLine2 = (line, deps) => handleLine(line, contextFor(deps));
|
|
2764
|
+
|
|
2683
2765
|
// src/commands/mcp.ts
|
|
2684
2766
|
var mcp = async (o) => {
|
|
2685
2767
|
const stdin = o.stdin ?? process.stdin;
|
|
@@ -2693,7 +2775,7 @@ var mcp = async (o) => {
|
|
|
2693
2775
|
while (nl !== -1) {
|
|
2694
2776
|
const line = buffer.slice(0, nl);
|
|
2695
2777
|
buffer = buffer.slice(nl + 1);
|
|
2696
|
-
const out = await
|
|
2778
|
+
const out = await handleLine2(line, { home: o.home });
|
|
2697
2779
|
if (out !== null)
|
|
2698
2780
|
write(out);
|
|
2699
2781
|
nl = buffer.indexOf(`
|
|
@@ -2701,7 +2783,7 @@ var mcp = async (o) => {
|
|
|
2701
2783
|
}
|
|
2702
2784
|
}
|
|
2703
2785
|
if (buffer.trim().length > 0) {
|
|
2704
|
-
const out = await
|
|
2786
|
+
const out = await handleLine2(buffer, { home: o.home });
|
|
2705
2787
|
if (out !== null)
|
|
2706
2788
|
write(out);
|
|
2707
2789
|
}
|
|
@@ -2770,7 +2852,8 @@ var musherInstall = (o) => {
|
|
|
2770
2852
|
return 1;
|
|
2771
2853
|
}
|
|
2772
2854
|
const env = o.env ?? process.env;
|
|
2773
|
-
const plist = musherPlistPath(o.home);
|
|
2855
|
+
const plist = musherPlistPath(o.home, env);
|
|
2856
|
+
const label = musherLabel(env);
|
|
2774
2857
|
const dir = configDir(o.home, env);
|
|
2775
2858
|
mkdirSync7(dirname3(plist), { recursive: true });
|
|
2776
2859
|
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
@@ -2784,7 +2867,7 @@ var musherInstall = (o) => {
|
|
|
2784
2867
|
const exec = o.exec ?? defaultExecSync;
|
|
2785
2868
|
if (existsSync8(plist))
|
|
2786
2869
|
exec("launchctl", ["unload", plist]);
|
|
2787
|
-
writeFileSync7(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir) }));
|
|
2870
|
+
writeFileSync7(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
|
|
2788
2871
|
const r = exec("launchctl", ["load", plist]);
|
|
2789
2872
|
if (!r.ok) {
|
|
2790
2873
|
error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
|
|
@@ -2793,19 +2876,20 @@ var musherInstall = (o) => {
|
|
|
2793
2876
|
const cfg = readConfig(o.home);
|
|
2794
2877
|
writeConfig(o.home, { ...cfg, musher: { ...cfg.musher ?? {}, agent: "launchd" } });
|
|
2795
2878
|
const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
|
|
2796
|
-
log(`musher installed: launchd agent ${
|
|
2879
|
+
log(`musher installed: launchd agent ${label}, every 5 min · log ${musherLogPath(dir)}`);
|
|
2797
2880
|
if (!claude)
|
|
2798
2881
|
log("warn: `claude` not found on PATH — set it with `sleds musher --claude /path/to/claude`");
|
|
2799
2882
|
return 0;
|
|
2800
2883
|
};
|
|
2801
2884
|
var musherUninstall = (o) => {
|
|
2802
2885
|
const log = o.log ?? console.log;
|
|
2803
|
-
const
|
|
2886
|
+
const env = o.env ?? process.env;
|
|
2887
|
+
const plist = musherPlistPath(o.home, env);
|
|
2804
2888
|
const exec = o.exec ?? defaultExecSync;
|
|
2805
2889
|
if (existsSync8(plist)) {
|
|
2806
2890
|
exec("launchctl", ["unload", plist]);
|
|
2807
2891
|
rmSync2(plist, { force: true });
|
|
2808
|
-
log(`musher removed: ${
|
|
2892
|
+
log(`musher removed: ${musherLabel(env)}`);
|
|
2809
2893
|
} else
|
|
2810
2894
|
log("musher was not installed.");
|
|
2811
2895
|
const cfg = readConfig(o.home);
|
|
@@ -2834,9 +2918,10 @@ var clientFor = (o) => {
|
|
|
2834
2918
|
var musherStatus = async (o) => {
|
|
2835
2919
|
const log = o.log ?? console.log;
|
|
2836
2920
|
const now = (o.now ?? Date.now)();
|
|
2837
|
-
const
|
|
2921
|
+
const env = o.env ?? process.env;
|
|
2922
|
+
const plist = musherPlistPath(o.home, env);
|
|
2838
2923
|
const cfg = readConfig(o.home);
|
|
2839
|
-
log(existsSync8(plist) ? `agent ${
|
|
2924
|
+
log(existsSync8(plist) ? `agent ${musherLabel(env)} installed (${plist})` : "agent not installed — sleds musher --install");
|
|
2840
2925
|
const claude = findClaude2(o.home, cfg.musher?.claudePath, o.which ?? defaultWhich3);
|
|
2841
2926
|
log(`claude ${claude ?? "not found — sleds musher --claude /path/to/claude"}`);
|
|
2842
2927
|
log(`mode ${cfg.musher?.permissionMode ?? DEFAULT_PERMISSION_MODE} · timeout ${cfg.musher?.timeoutMinutes ?? DEFAULT_TIMEOUT_MINUTES} min`);
|
|
@@ -3210,11 +3295,11 @@ init_config();
|
|
|
3210
3295
|
init_meter();
|
|
3211
3296
|
var HOOK_STDIN_TIMEOUT_MS = 1000;
|
|
3212
3297
|
var NOTIFICATION_TITLE = "sleds";
|
|
3213
|
-
var
|
|
3298
|
+
var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3214
3299
|
var notificationType = (text) => {
|
|
3215
3300
|
try {
|
|
3216
3301
|
const raw = JSON.parse(text);
|
|
3217
|
-
if (!
|
|
3302
|
+
if (!isObject8(raw))
|
|
3218
3303
|
return;
|
|
3219
3304
|
return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
|
|
3220
3305
|
} catch {
|
|
@@ -3389,8 +3474,8 @@ function resolveProfileDir(localStateJson, profile) {
|
|
|
3389
3474
|
if (localStateJson) {
|
|
3390
3475
|
try {
|
|
3391
3476
|
const raw = JSON.parse(localStateJson);
|
|
3392
|
-
const p =
|
|
3393
|
-
if (p &&
|
|
3477
|
+
const p = isObject9(raw) && isObject9(raw.profile) ? raw.profile : null;
|
|
3478
|
+
if (p && isObject9(p.info_cache))
|
|
3394
3479
|
cache = p.info_cache;
|
|
3395
3480
|
} catch {}
|
|
3396
3481
|
}
|
|
@@ -3399,7 +3484,7 @@ function resolveProfileDir(localStateJson, profile) {
|
|
|
3399
3484
|
const want = profile.toLowerCase();
|
|
3400
3485
|
const hits = [];
|
|
3401
3486
|
for (const [dir, info] of Object.entries(cache)) {
|
|
3402
|
-
if (!
|
|
3487
|
+
if (!isObject9(info))
|
|
3403
3488
|
continue;
|
|
3404
3489
|
const match = ["name", "user_name", "gaia_name"].some((k) => {
|
|
3405
3490
|
const v = info[k];
|
|
@@ -3437,23 +3522,23 @@ function sourceArgs(source) {
|
|
|
3437
3522
|
out.push("--token", source.token);
|
|
3438
3523
|
return out;
|
|
3439
3524
|
}
|
|
3440
|
-
var
|
|
3441
|
-
var
|
|
3525
|
+
var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3526
|
+
var num5 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
3442
3527
|
var round2 = (n) => Math.round(n * 100) / 100;
|
|
3443
3528
|
function parseUsageResponse(raw, now = Date.now()) {
|
|
3444
|
-
if (!
|
|
3529
|
+
if (!isObject9(raw))
|
|
3445
3530
|
return null;
|
|
3446
3531
|
const out = { capturedAt: now, models: {} };
|
|
3447
3532
|
for (const [k, v] of Object.entries(raw)) {
|
|
3448
|
-
if (!
|
|
3533
|
+
if (!isObject9(v))
|
|
3449
3534
|
continue;
|
|
3450
3535
|
if (k === "extra_usage") {
|
|
3451
|
-
const credits =
|
|
3536
|
+
const credits = num5(v.used_credits);
|
|
3452
3537
|
if (credits !== undefined)
|
|
3453
3538
|
out.usedCreditsUsd = round2(credits / 100);
|
|
3454
3539
|
continue;
|
|
3455
3540
|
}
|
|
3456
|
-
const usedPct =
|
|
3541
|
+
const usedPct = num5(v.utilization);
|
|
3457
3542
|
const resetsAt = parseResetsAt(v.resets_at);
|
|
3458
3543
|
if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
|
|
3459
3544
|
continue;
|
|
@@ -3797,7 +3882,7 @@ async function postSeat(proxyUrl, token, body, fetchFn) {
|
|
|
3797
3882
|
// src/commands/specEvent.ts
|
|
3798
3883
|
init_meter();
|
|
3799
3884
|
import { readFileSync as readFileSync9, statSync } from "node:fs";
|
|
3800
|
-
import { basename, isAbsolute, relative } from "node:path";
|
|
3885
|
+
import { basename as basename2, isAbsolute, relative } from "node:path";
|
|
3801
3886
|
var HOOK_STDIN_TIMEOUT_MS2 = 1000;
|
|
3802
3887
|
var ESTIMATE_TIMEOUT_MS = 4000;
|
|
3803
3888
|
var MIN_SPEC_CHARS = 200;
|
|
@@ -3810,12 +3895,12 @@ function isSpecPath(path) {
|
|
|
3810
3895
|
return false;
|
|
3811
3896
|
if (NOT_SPEC.test(path))
|
|
3812
3897
|
return false;
|
|
3813
|
-
return SPEC_DIRS.test(path) || SPEC_NAMES.test(
|
|
3898
|
+
return SPEC_DIRS.test(path) || SPEC_NAMES.test(basename2(path));
|
|
3814
3899
|
}
|
|
3815
3900
|
function specTitle(body, path) {
|
|
3816
3901
|
const m = /^#\s+(.+?)\s*$/m.exec(body);
|
|
3817
3902
|
const t = m?.[1]?.trim();
|
|
3818
|
-
return (t && t.length > 0 ? t :
|
|
3903
|
+
return (t && t.length > 0 ? t : basename2(path).replace(/\.(md|mdx)$/i, "")).slice(0, 120);
|
|
3819
3904
|
}
|
|
3820
3905
|
function looksLikeSpec(body) {
|
|
3821
3906
|
if (body.trim().length < MIN_SPEC_CHARS)
|
|
@@ -3873,7 +3958,7 @@ var specEvent = async (o) => {
|
|
|
3873
3958
|
const spec = {
|
|
3874
3959
|
title: specTitle(body, input.filePath),
|
|
3875
3960
|
body: body.slice(0, MAX_SPEC_CHARS),
|
|
3876
|
-
repoHint: input.cwd ?
|
|
3961
|
+
repoHint: input.cwd ? basename2(input.cwd) : undefined
|
|
3877
3962
|
};
|
|
3878
3963
|
try {
|
|
3879
3964
|
const res = await (o.fetchFn ?? fetch)(`${src.proxyUrl}/proxy/estimate`, {
|
|
@@ -4136,7 +4221,7 @@ var uninstall2 = (o) => {
|
|
|
4136
4221
|
if (existsSync12(plist)) {
|
|
4137
4222
|
(o.unloadAgent ?? defaultUnload)(plist);
|
|
4138
4223
|
rmSync5(plist, { force: true });
|
|
4139
|
-
removed.push(`launchd agent ${
|
|
4224
|
+
removed.push(`launchd agent ${musherLabel()}`);
|
|
4140
4225
|
}
|
|
4141
4226
|
if (removed.length === 0) {
|
|
4142
4227
|
log("Nothing to remove; SLEDS was not installed.");
|
|
@@ -4212,6 +4297,21 @@ var defaultExec = (cmd, args) => {
|
|
|
4212
4297
|
return { ok: r.status === 0, stderr: r.stderr ?? "" };
|
|
4213
4298
|
};
|
|
4214
4299
|
|
|
4300
|
+
// src/settings.ts
|
|
4301
|
+
import { dirname as dirname4, join as join10, resolve as resolve4 } from "node:path";
|
|
4302
|
+
var withSeatEnv2 = (command, home, env = process.env) => {
|
|
4303
|
+
const parts = [];
|
|
4304
|
+
const sh = env.SLEDS_HOME?.trim();
|
|
4305
|
+
const cc = env.CLAUDE_CONFIG_DIR?.trim();
|
|
4306
|
+
if (sh)
|
|
4307
|
+
parts.push(`SLEDS_HOME=${shellWord2(resolve4(home, sh))}`);
|
|
4308
|
+
if (cc)
|
|
4309
|
+
parts.push(`CLAUDE_CONFIG_DIR=${shellWord2(resolve4(home, cc))}`);
|
|
4310
|
+
parts.push(command);
|
|
4311
|
+
return parts.join(" ");
|
|
4312
|
+
};
|
|
4313
|
+
var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
|
|
4314
|
+
|
|
4215
4315
|
// src/index.ts
|
|
4216
4316
|
var USAGE = `usage: sleds <command> [options]
|
|
4217
4317
|
|
|
@@ -4309,9 +4409,9 @@ var sledsArgv = () => {
|
|
|
4309
4409
|
return script ? [process.execPath, script] : [process.execPath];
|
|
4310
4410
|
};
|
|
4311
4411
|
var shellQuote = (s) => /^[\w./-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
|
|
4312
|
-
var statusLineCommand = (argv) => [...argv, "statusline"].map(shellQuote).join(" ");
|
|
4313
|
-
var seatEventCommand = (argv) => [...argv, "seat-event"].map(shellQuote).join(" ");
|
|
4314
|
-
var specEventCommand = (argv) => [...argv, "spec-event"].map(shellQuote).join(" ");
|
|
4412
|
+
var statusLineCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "statusline"].map(shellQuote).join(" "), home);
|
|
4413
|
+
var seatEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "seat-event"].map(shellQuote).join(" "), home);
|
|
4414
|
+
var specEventCommand = (argv, home = homedir()) => withSeatEnv2([...argv, "spec-event"].map(shellQuote).join(" "), home);
|
|
4315
4415
|
var main = async (argv) => {
|
|
4316
4416
|
const { cmd, flags } = parseArgs(argv);
|
|
4317
4417
|
const home = homedir();
|
|
@@ -4403,7 +4503,7 @@ var main = async (argv) => {
|
|
|
4403
4503
|
case "version":
|
|
4404
4504
|
case "--version":
|
|
4405
4505
|
case "-v":
|
|
4406
|
-
console.log("sleds 0.1.
|
|
4506
|
+
console.log("sleds 0.1.16");
|
|
4407
4507
|
return 0;
|
|
4408
4508
|
default:
|
|
4409
4509
|
console.error(`unknown command: ${cmd}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleds/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "SLEDS CLI
|
|
3
|
+
"version": "0.1.16",
|
|
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",
|
|
7
|
-
"bin": {
|
|
7
|
+
"bin": {
|
|
8
|
+
"sleds": "dist/sleds.mjs"
|
|
9
|
+
},
|
|
8
10
|
"files": ["dist/sleds.mjs", "README.md"],
|
|
9
|
-
"engines": {
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22.5"
|
|
13
|
+
},
|
|
10
14
|
"os": ["darwin", "linux"],
|
|
11
15
|
"repository": {
|
|
12
16
|
"type": "git",
|
|
@@ -24,12 +28,19 @@
|
|
|
24
28
|
"batch",
|
|
25
29
|
"mcp"
|
|
26
30
|
],
|
|
27
|
-
"publishConfig": {
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "restricted"
|
|
33
|
+
},
|
|
28
34
|
"scripts": {
|
|
29
35
|
"build": "bun scripts/build.ts",
|
|
30
36
|
"prepack": "bun scripts/build.ts",
|
|
31
37
|
"typecheck": "tsc --noEmit",
|
|
32
38
|
"test": "vitest run"
|
|
33
39
|
},
|
|
34
|
-
"devDependencies": {
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/bun": "^1.1.13",
|
|
42
|
+
"typescript": "^5.6.3",
|
|
43
|
+
"vitest": "^2.1.4",
|
|
44
|
+
"@sleds/shared": "workspace:*"
|
|
45
|
+
}
|
|
35
46
|
}
|