@sleds/cli 0.1.15 → 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 +297 -227
- package/package.json +17 -6
package/dist/sleds.mjs
CHANGED
|
@@ -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
|
|
@@ -532,8 +539,8 @@ __export(exports_seat, {
|
|
|
532
539
|
});
|
|
533
540
|
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync10 } from "node:fs";
|
|
534
541
|
import { join as join11 } from "node:path";
|
|
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 =
|
|
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;
|
|
@@ -605,27 +612,27 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
605
612
|
return null;
|
|
606
613
|
return `seat ${parts.join(SEP)}`;
|
|
607
614
|
}, seatStatePath2 = (home) => join11(configDir(home), "seat.json"), parseWindowStored2 = (v) => {
|
|
608
|
-
if (!
|
|
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;
|
|
@@ -1044,11 +1051,11 @@ var SEAT_POST_MIN_DELTA_PCT = 1;
|
|
|
1044
1051
|
var SEAT_POST_MIN_INTERVAL_MS = 5 * 60000;
|
|
1045
1052
|
var SEAT_POST_TIMEOUT_MS = 1500;
|
|
1046
1053
|
var STDIN_TIMEOUT_MS = 300;
|
|
1047
|
-
var
|
|
1048
|
-
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;
|
|
1049
1056
|
var EPOCH_MS_THRESHOLD = 100000000000;
|
|
1050
1057
|
var parseResetsAt = (v) => {
|
|
1051
|
-
const n =
|
|
1058
|
+
const n = num3(v);
|
|
1052
1059
|
if (n !== undefined) {
|
|
1053
1060
|
if (n <= 0)
|
|
1054
1061
|
return;
|
|
@@ -1061,29 +1068,29 @@ var parseResetsAt = (v) => {
|
|
|
1061
1068
|
return;
|
|
1062
1069
|
};
|
|
1063
1070
|
var parseBar = (v) => {
|
|
1064
|
-
if (!
|
|
1071
|
+
if (!isObject5(v))
|
|
1065
1072
|
return;
|
|
1066
|
-
const usedPct =
|
|
1073
|
+
const usedPct = num3(v.used_percentage) ?? num3(v.usedPct);
|
|
1067
1074
|
const resetsAt = parseResetsAt(v.resets_at ?? v.resetsAt);
|
|
1068
1075
|
if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
|
|
1069
1076
|
return;
|
|
1070
1077
|
return { usedPct, resetsAt };
|
|
1071
1078
|
};
|
|
1072
1079
|
var parseSeatWindow = (raw, capturedAt) => {
|
|
1073
|
-
if (!
|
|
1080
|
+
if (!isObject5(raw))
|
|
1074
1081
|
return null;
|
|
1075
|
-
const limits =
|
|
1082
|
+
const limits = isObject5(raw.rate_limits) ? raw.rate_limits : raw;
|
|
1076
1083
|
const fiveHour = parseBar(limits.five_hour ?? limits.fiveHour);
|
|
1077
1084
|
const sevenDay = parseBar(limits.seven_day ?? limits.sevenDay);
|
|
1078
1085
|
const spendLimit = parseBar(limits.spend_limit ?? limits.spendLimit);
|
|
1079
1086
|
const models = {};
|
|
1080
|
-
if (
|
|
1087
|
+
if (isObject5(limits.models))
|
|
1081
1088
|
for (const [k, v] of Object.entries(limits.models)) {
|
|
1082
1089
|
const bar = parseBar(v);
|
|
1083
1090
|
if (bar)
|
|
1084
1091
|
models[k] = bar;
|
|
1085
1092
|
}
|
|
1086
|
-
const usedCreditsUsd =
|
|
1093
|
+
const usedCreditsUsd = num3(limits.usedCreditsUsd);
|
|
1087
1094
|
const hasModels = Object.keys(models).length > 0;
|
|
1088
1095
|
if (!fiveHour && !sevenDay && !spendLimit && !hasModels && usedCreditsUsd === undefined)
|
|
1089
1096
|
return null;
|
|
@@ -1126,28 +1133,28 @@ var formatSeatFragment = (w) => {
|
|
|
1126
1133
|
};
|
|
1127
1134
|
var seatStatePath = (home) => join5(configDir(home), "seat.json");
|
|
1128
1135
|
var parseWindowStored = (v) => {
|
|
1129
|
-
if (!
|
|
1136
|
+
if (!isObject5(v))
|
|
1130
1137
|
return;
|
|
1131
|
-
const capturedAt =
|
|
1138
|
+
const capturedAt = num3(v.capturedAt);
|
|
1132
1139
|
if (capturedAt === undefined)
|
|
1133
1140
|
return;
|
|
1134
1141
|
return parseSeatWindow(v, capturedAt) ?? undefined;
|
|
1135
1142
|
};
|
|
1136
1143
|
var parseSeatState = (raw) => {
|
|
1137
|
-
if (!
|
|
1144
|
+
if (!isObject5(raw))
|
|
1138
1145
|
return {};
|
|
1139
1146
|
const out = {};
|
|
1140
1147
|
const window = parseWindowStored(raw.window);
|
|
1141
1148
|
if (window)
|
|
1142
1149
|
out.window = window;
|
|
1143
|
-
const lastPostedAt =
|
|
1150
|
+
const lastPostedAt = num3(raw.lastPostedAt);
|
|
1144
1151
|
if (lastPostedAt !== undefined)
|
|
1145
1152
|
out.lastPostedAt = lastPostedAt;
|
|
1146
|
-
if (
|
|
1153
|
+
if (isObject5(raw.lastPosted)) {
|
|
1147
1154
|
const lp = {};
|
|
1148
|
-
const f =
|
|
1149
|
-
const s =
|
|
1150
|
-
const sp =
|
|
1155
|
+
const f = num3(raw.lastPosted.fiveHour);
|
|
1156
|
+
const s = num3(raw.lastPosted.sevenDay);
|
|
1157
|
+
const sp = num3(raw.lastPosted.spendLimit);
|
|
1151
1158
|
if (f !== undefined)
|
|
1152
1159
|
lp.fiveHour = f;
|
|
1153
1160
|
if (s !== undefined)
|
|
@@ -1156,7 +1163,7 @@ var parseSeatState = (raw) => {
|
|
|
1156
1163
|
lp.spendLimit = sp;
|
|
1157
1164
|
out.lastPosted = lp;
|
|
1158
1165
|
}
|
|
1159
|
-
const lastWallResetsAt =
|
|
1166
|
+
const lastWallResetsAt = num3(raw.lastWallResetsAt);
|
|
1160
1167
|
if (lastWallResetsAt !== undefined)
|
|
1161
1168
|
out.lastWallResetsAt = lastWallResetsAt;
|
|
1162
1169
|
const session = parseSeatSession(raw.session);
|
|
@@ -1165,9 +1172,9 @@ var parseSeatState = (raw) => {
|
|
|
1165
1172
|
return out;
|
|
1166
1173
|
};
|
|
1167
1174
|
var parseSeatSession = (v) => {
|
|
1168
|
-
if (!
|
|
1175
|
+
if (!isObject5(v) || typeof v.id !== "string" || !v.id)
|
|
1169
1176
|
return;
|
|
1170
|
-
const at =
|
|
1177
|
+
const at = num3(v.at);
|
|
1171
1178
|
if (at === undefined)
|
|
1172
1179
|
return;
|
|
1173
1180
|
const out = { id: v.id, at };
|
|
@@ -1183,12 +1190,12 @@ var seatSessionFromStdin = (text, now) => {
|
|
|
1183
1190
|
return null;
|
|
1184
1191
|
try {
|
|
1185
1192
|
const raw = JSON.parse(trimmed);
|
|
1186
|
-
if (!
|
|
1193
|
+
if (!isObject5(raw) || typeof raw.session_id !== "string" || !raw.session_id)
|
|
1187
1194
|
return null;
|
|
1188
1195
|
const out = { id: raw.session_id, at: now };
|
|
1189
1196
|
if (typeof raw.cwd === "string" && raw.cwd)
|
|
1190
1197
|
out.cwd = raw.cwd;
|
|
1191
|
-
const model =
|
|
1198
|
+
const model = isObject5(raw.model) ? raw.model.id : raw.model;
|
|
1192
1199
|
if (typeof model === "string" && model)
|
|
1193
1200
|
out.model = model;
|
|
1194
1201
|
return out;
|
|
@@ -1315,11 +1322,11 @@ var readStdin = (timeoutMs, stdin = process.stdin) => new Promise((resolve3) =>
|
|
|
1315
1322
|
|
|
1316
1323
|
// src/switch.ts
|
|
1317
1324
|
init_settings();
|
|
1318
|
-
var
|
|
1319
|
-
var envOf2 = (settings) =>
|
|
1325
|
+
var isObject6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1326
|
+
var envOf2 = (settings) => isObject6(settings.env) ? settings.env : {};
|
|
1320
1327
|
var capturePriorEnv = (settings) => {
|
|
1321
1328
|
const env = envOf2(settings);
|
|
1322
|
-
const prior = { hadEnv:
|
|
1329
|
+
const prior = { hadEnv: isObject6(settings.env) };
|
|
1323
1330
|
const url = env[BASE_URL_KEY];
|
|
1324
1331
|
const token = env[AUTH_TOKEN_KEY];
|
|
1325
1332
|
if (typeof url === "string")
|
|
@@ -2186,8 +2193,62 @@ var init = async (o) => {
|
|
|
2186
2193
|
return 0;
|
|
2187
2194
|
};
|
|
2188
2195
|
|
|
2189
|
-
// src/mcp.ts
|
|
2190
|
-
|
|
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
|
+
}
|
|
2191
2252
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
2192
2253
|
var SERVER_INFO = { name: "sleds", version: "0.0.1" };
|
|
2193
2254
|
var MCP_TIMEOUT_MS = 5000;
|
|
@@ -2260,6 +2321,24 @@ var TOOLS = [
|
|
|
2260
2321
|
required: ["task"]
|
|
2261
2322
|
}
|
|
2262
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
|
+
},
|
|
2263
2342
|
{
|
|
2264
2343
|
name: "pickup",
|
|
2265
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.",
|
|
@@ -2271,109 +2350,37 @@ var TOOLS = [
|
|
|
2271
2350
|
}
|
|
2272
2351
|
];
|
|
2273
2352
|
var NOT_SET_UP = {
|
|
2274
|
-
text: "SLEDS is not
|
|
2353
|
+
text: "SLEDS is not connected to a seat here. Run `sleds init` (CLI) or connect SLEDS as a connector.",
|
|
2275
2354
|
isError: true
|
|
2276
2355
|
};
|
|
2277
2356
|
var UNREACHABLE = {
|
|
2278
2357
|
text: "Control plane unreachable. Nothing was recorded → try again in a moment.",
|
|
2279
2358
|
isError: true
|
|
2280
2359
|
};
|
|
2281
|
-
var
|
|
2282
|
-
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;
|
|
2283
2362
|
var str2 = (v) => typeof v === "string" ? v : undefined;
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
if (
|
|
2287
|
-
return
|
|
2288
|
-
|
|
2289
|
-
try {
|
|
2290
|
-
const res = await fetchFn(`${src.proxyUrl}${path}`, {
|
|
2291
|
-
method: "POST",
|
|
2292
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${src.token}` },
|
|
2293
|
-
body: JSON.stringify(body),
|
|
2294
|
-
signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_TIMEOUT_MS)
|
|
2295
|
-
});
|
|
2296
|
-
if (!res.ok)
|
|
2297
|
-
return null;
|
|
2298
|
-
const parsed = await res.json();
|
|
2299
|
-
return isObject6(parsed) ? parsed : null;
|
|
2300
|
-
} catch {
|
|
2301
|
-
return null;
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
async function proxyPostFull(deps, path, body) {
|
|
2305
|
-
const src = meterSource(deps.home);
|
|
2306
|
-
if (!src)
|
|
2307
|
-
return null;
|
|
2308
|
-
const fetchFn = deps.fetchFn ?? fetch;
|
|
2309
|
-
try {
|
|
2310
|
-
const res = await fetchFn(`${src.proxyUrl}${path}`, {
|
|
2311
|
-
method: "POST",
|
|
2312
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${src.token}` },
|
|
2313
|
-
body: JSON.stringify(body),
|
|
2314
|
-
signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_TIMEOUT_MS)
|
|
2315
|
-
});
|
|
2316
|
-
let json = null;
|
|
2317
|
-
try {
|
|
2318
|
-
json = await res.json();
|
|
2319
|
-
} catch {
|
|
2320
|
-
json = null;
|
|
2321
|
-
}
|
|
2322
|
-
return { status: res.status, json: isObject6(json) ? json : null };
|
|
2323
|
-
} catch {
|
|
2324
|
-
return null;
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
var errorMessage = (j) => isObject6(j?.error) ? str2(j.error.message) : undefined;
|
|
2328
|
-
async function proxyGet(deps, path) {
|
|
2329
|
-
const src = meterSource(deps.home);
|
|
2330
|
-
if (!src)
|
|
2331
|
-
return null;
|
|
2332
|
-
const fetchFn = deps.fetchFn ?? fetch;
|
|
2333
|
-
try {
|
|
2334
|
-
const res = await fetchFn(`${src.proxyUrl}${path}`, {
|
|
2335
|
-
headers: { authorization: `Bearer ${src.token}` },
|
|
2336
|
-
signal: AbortSignal.timeout(deps.timeoutMs ?? MCP_TIMEOUT_MS)
|
|
2337
|
-
});
|
|
2338
|
-
if (!res.ok)
|
|
2339
|
-
return null;
|
|
2340
|
-
const parsed = await res.json();
|
|
2341
|
-
return isObject6(parsed) ? parsed : null;
|
|
2342
|
-
} catch {
|
|
2343
|
-
return null;
|
|
2344
|
-
}
|
|
2345
|
-
}
|
|
2346
|
-
async function usageTool(deps) {
|
|
2347
|
-
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)
|
|
2348
2368
|
return NOT_SET_UP;
|
|
2349
|
-
const
|
|
2350
|
-
const m =
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
timeoutMs: deps.timeoutMs
|
|
2355
|
-
});
|
|
2356
|
-
const frag = formatSeatFragment(readSeatState(deps.home).window ?? null);
|
|
2357
|
-
if (!m) {
|
|
2358
|
-
return {
|
|
2359
|
-
text: frag ? `sleds offline (proxy did not answer) · ${frag}` : "sleds offline (proxy did not answer).",
|
|
2360
|
-
isError: true
|
|
2361
|
-
};
|
|
2362
|
-
}
|
|
2363
|
-
const lines = formatMeter(m, now);
|
|
2364
|
-
if (frag)
|
|
2365
|
-
lines.push(`${"Limits:".padEnd(12)}${frag.replace(/^seat /, "")}`);
|
|
2366
|
-
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(`
|
|
2367
2374
|
`) };
|
|
2368
2375
|
}
|
|
2369
2376
|
function bandLine(band) {
|
|
2370
|
-
if (!
|
|
2377
|
+
if (!isObject7(band))
|
|
2371
2378
|
return null;
|
|
2372
|
-
const low =
|
|
2373
|
-
const high =
|
|
2374
|
-
const lowM =
|
|
2375
|
-
const highM =
|
|
2376
|
-
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);
|
|
2377
2384
|
if (low === undefined || high === undefined)
|
|
2378
2385
|
return null;
|
|
2379
2386
|
const d = high >= 10 ? 0 : 2;
|
|
@@ -2381,26 +2388,26 @@ function bandLine(band) {
|
|
|
2381
2388
|
const pct = conf !== undefined ? ` · ${Math.round(conf * 100)}%` : "";
|
|
2382
2389
|
return `$${low.toFixed(d)}–${high.toFixed(d)}${mins}${pct}`;
|
|
2383
2390
|
}
|
|
2384
|
-
async function estimateTool(
|
|
2385
|
-
if (!
|
|
2391
|
+
async function estimateTool(ctx, args) {
|
|
2392
|
+
if (!ctx.client)
|
|
2386
2393
|
return NOT_SET_UP;
|
|
2387
2394
|
const spec = str2(args.spec)?.trim();
|
|
2388
2395
|
if (!spec)
|
|
2389
2396
|
return { text: "Nothing to estimate. Pass spec: what the job should do.", isError: true };
|
|
2390
|
-
const source =
|
|
2391
|
-
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", {
|
|
2392
2399
|
spec,
|
|
2393
2400
|
model: str2(args.model),
|
|
2394
2401
|
...source ? { source } : {}
|
|
2395
2402
|
});
|
|
2396
2403
|
if (!r)
|
|
2397
2404
|
return UNREACHABLE;
|
|
2398
|
-
const band =
|
|
2405
|
+
const band = isObject7(r.band) ? r.band : {};
|
|
2399
2406
|
const line = bandLine(band) ?? "band unreadable";
|
|
2400
2407
|
const method = str2(band.method) ?? "plan";
|
|
2401
|
-
const cap =
|
|
2402
|
-
const threshold =
|
|
2403
|
-
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"}.` : "";
|
|
2404
2411
|
const assumptions = Array.isArray(band.assumptions) ? band.assumptions.filter((a) => typeof a === "string") : [];
|
|
2405
2412
|
const tail = assumptions.length > 0 ? `
|
|
2406
2413
|
Assumes: ${assumptions.join("; ")}.` : "";
|
|
@@ -2418,11 +2425,11 @@ function byModelText(raw) {
|
|
|
2418
2425
|
return "";
|
|
2419
2426
|
const parts = [];
|
|
2420
2427
|
for (const row2 of raw) {
|
|
2421
|
-
if (!
|
|
2428
|
+
if (!isObject7(row2) || !isObject7(row2.standard))
|
|
2422
2429
|
continue;
|
|
2423
2430
|
const model = str2(row2.model);
|
|
2424
|
-
const low =
|
|
2425
|
-
const high =
|
|
2431
|
+
const low = num4(row2.standard.lowUsd);
|
|
2432
|
+
const high = num4(row2.standard.highUsd);
|
|
2426
2433
|
if (!model || low === undefined || high === undefined)
|
|
2427
2434
|
continue;
|
|
2428
2435
|
const m = /^claude-([a-z]+)-(\d+)(?:-(\d+))?/.exec(model);
|
|
@@ -2433,7 +2440,7 @@ function byModelText(raw) {
|
|
|
2433
2440
|
By model: ${parts.join(" · ")} (overnight batch ½).` : "";
|
|
2434
2441
|
}
|
|
2435
2442
|
function routingText(raw) {
|
|
2436
|
-
if (!
|
|
2443
|
+
if (!isObject7(raw) || !Array.isArray(raw.segments) || !isObject7(raw.cost))
|
|
2437
2444
|
return "";
|
|
2438
2445
|
const short = (model) => {
|
|
2439
2446
|
const m = /^claude-([a-z]+)-(\d+)(?:-(\d+))?/.exec(model);
|
|
@@ -2441,10 +2448,10 @@ function routingText(raw) {
|
|
|
2441
2448
|
};
|
|
2442
2449
|
const parts = [];
|
|
2443
2450
|
for (const s of raw.segments) {
|
|
2444
|
-
if (!
|
|
2451
|
+
if (!isObject7(s))
|
|
2445
2452
|
continue;
|
|
2446
|
-
const from =
|
|
2447
|
-
const to =
|
|
2453
|
+
const from = num4(s.from);
|
|
2454
|
+
const to = num4(s.to);
|
|
2448
2455
|
const model = str2(s.model);
|
|
2449
2456
|
if (from === undefined || to === undefined || !model)
|
|
2450
2457
|
continue;
|
|
@@ -2452,55 +2459,55 @@ function routingText(raw) {
|
|
|
2452
2459
|
parts.push(`${from === to ? from : `${from}–${to}`} ${short(model)}${reasons.length ? ` (${reasons.slice(0, 3).join(", ")})` : ""}`);
|
|
2453
2460
|
}
|
|
2454
2461
|
const c = raw.cost;
|
|
2455
|
-
const lo =
|
|
2456
|
-
const hi =
|
|
2457
|
-
const slo =
|
|
2458
|
-
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);
|
|
2459
2466
|
const single = str2(c.singleModel);
|
|
2460
2467
|
if (parts.length === 0 || lo === undefined || hi === undefined || slo === undefined || shi === undefined || !single)
|
|
2461
2468
|
return "";
|
|
2462
2469
|
return `
|
|
2463
2470
|
Routing: ${parts.join(" · ")} ≈ ${usdRange(lo, hi)} vs ${usdRange(slo, shi)} all ${short(single)}.`;
|
|
2464
2471
|
}
|
|
2465
|
-
async function overnightTool(
|
|
2466
|
-
if (!
|
|
2472
|
+
async function overnightTool(ctx, args) {
|
|
2473
|
+
if (!ctx.client)
|
|
2467
2474
|
return NOT_SET_UP;
|
|
2468
2475
|
const spec = str2(args.spec)?.trim();
|
|
2469
2476
|
if (!spec)
|
|
2470
2477
|
return { text: "Nothing to queue. Pass spec: what the job should do.", isError: true };
|
|
2471
|
-
const r = await
|
|
2478
|
+
const r = await ctx.client.post("/proxy/overnight", { spec, model: str2(args.model) });
|
|
2472
2479
|
if (!r)
|
|
2473
2480
|
return { text: `Could not queue the job. ${UNREACHABLE.text}`, isError: true };
|
|
2474
2481
|
const jobId = str2(r.jobId) ?? "unknown";
|
|
2475
|
-
const now = (
|
|
2476
|
-
const at =
|
|
2482
|
+
const now = (ctx.now ?? Date.now)();
|
|
2483
|
+
const at = num4(r.scheduledAt);
|
|
2477
2484
|
const when = at !== undefined && at > now ? `Runs in ${shortDuration(at - now)}` : "Runs at the next tick";
|
|
2478
2485
|
const band = bandLine(r.band);
|
|
2479
|
-
const cap =
|
|
2486
|
+
const cap = isObject7(r.band) ? num4(r.band.capUsd) : undefined;
|
|
2480
2487
|
const sized = band ? ` ${band}${cap !== undefined ? ` · cap ${usd(cap)}` : ""}.` : "";
|
|
2481
2488
|
return {
|
|
2482
2489
|
text: `Queued overnight. Batch tier · ½ price.${sized} ${when}. Check with the jobs tool; pull the result into a session with /sled resume ${jobId}.`
|
|
2483
2490
|
};
|
|
2484
2491
|
}
|
|
2485
|
-
async function jobsTool(
|
|
2486
|
-
if (!
|
|
2492
|
+
async function jobsTool(ctx) {
|
|
2493
|
+
if (!ctx.client)
|
|
2487
2494
|
return NOT_SET_UP;
|
|
2488
|
-
const r = await
|
|
2495
|
+
const r = await ctx.client.get("/proxy/jobs");
|
|
2489
2496
|
if (!r)
|
|
2490
2497
|
return UNREACHABLE;
|
|
2491
|
-
const rows = Array.isArray(r.jobs) ? r.jobs.filter(
|
|
2492
|
-
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(
|
|
2493
|
-
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)();
|
|
2494
2501
|
if (rows.length === 0 && handoffs.length === 0)
|
|
2495
2502
|
return { text: "No jobs for this seat. Queue one with the overnight tool." };
|
|
2496
2503
|
if (rows.length === 0)
|
|
2497
2504
|
return { text: ["No jobs for this seat.", ...handoffLines(handoffs, now)].join(`
|
|
2498
2505
|
`) };
|
|
2499
2506
|
const lines = rows.map((j) => {
|
|
2500
|
-
const when = (ts) =>
|
|
2507
|
+
const when = (ts) => num4(ts) !== undefined ? shortDuration(Math.abs(now - num4(ts))) : "";
|
|
2501
2508
|
const status = str2(j.status) ?? "unknown";
|
|
2502
|
-
const cost =
|
|
2503
|
-
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) ?? "";
|
|
2504
2511
|
return [
|
|
2505
2512
|
str2(j.jobId) ?? "?",
|
|
2506
2513
|
status.padEnd(12),
|
|
@@ -2522,7 +2529,7 @@ function handoffLines(handoffs, now) {
|
|
|
2522
2529
|
const forMe = h.forMe === true;
|
|
2523
2530
|
const to = str2(h.to);
|
|
2524
2531
|
const who = forMe ? to ? "for you" : "parked for anyone" : `for ${to ?? "anyone"}`;
|
|
2525
|
-
const created =
|
|
2532
|
+
const created = num4(h.createdAt);
|
|
2526
2533
|
const age = created !== undefined ? `${shortDuration(Math.max(0, now - created))} ago` : "";
|
|
2527
2534
|
const note = str2(h.note);
|
|
2528
2535
|
const parts = [`from ${str2(h.from) ?? "?"}`, who, str2(h.wallKind) ?? "", age].filter(Boolean);
|
|
@@ -2533,16 +2540,16 @@ function handoffLines(handoffs, now) {
|
|
|
2533
2540
|
out.push("Take one in Claude Code with /sled pickup <id>.");
|
|
2534
2541
|
return out;
|
|
2535
2542
|
}
|
|
2536
|
-
async function handoffsTool(
|
|
2537
|
-
if (!
|
|
2543
|
+
async function handoffsTool(ctx) {
|
|
2544
|
+
if (!ctx.client)
|
|
2538
2545
|
return NOT_SET_UP;
|
|
2539
|
-
const r = await
|
|
2546
|
+
const r = await ctx.client.get("/proxy/jobs");
|
|
2540
2547
|
if (!r)
|
|
2541
2548
|
return UNREACHABLE;
|
|
2542
|
-
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(
|
|
2549
|
+
const handoffs = Array.isArray(r.handoffs) ? r.handoffs.filter(isObject7) : [];
|
|
2543
2550
|
if (handoffs.length === 0)
|
|
2544
2551
|
return { text: "No open hand-offs on your team. Park one with the handoff tool." };
|
|
2545
|
-
const now = (
|
|
2552
|
+
const now = (ctx.now ?? Date.now)();
|
|
2546
2553
|
const packets = handoffs.map((h) => str2(h.packet)).filter((p) => Boolean(p)).join(`
|
|
2547
2554
|
|
|
2548
2555
|
---
|
|
@@ -2555,14 +2562,14 @@ ${packets}` : ""].join(`
|
|
|
2555
2562
|
`)
|
|
2556
2563
|
};
|
|
2557
2564
|
}
|
|
2558
|
-
async function handoffTool(
|
|
2559
|
-
if (!
|
|
2565
|
+
async function handoffTool(ctx, args) {
|
|
2566
|
+
if (!ctx.client)
|
|
2560
2567
|
return NOT_SET_UP;
|
|
2561
2568
|
const task = str2(args.task)?.trim();
|
|
2562
2569
|
if (!task)
|
|
2563
2570
|
return { text: "Nothing to hand off. Pass task: what the picker should do.", isError: true };
|
|
2564
2571
|
const to = str2(args.to)?.trim();
|
|
2565
|
-
const r = await
|
|
2572
|
+
const r = await ctx.client.postFull("/proxy/handoff/park", {
|
|
2566
2573
|
task,
|
|
2567
2574
|
to: to || undefined,
|
|
2568
2575
|
note: str2(args.note)?.trim() || undefined
|
|
@@ -2581,13 +2588,13 @@ async function handoffTool(deps, args) {
|
|
|
2581
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.`
|
|
2582
2589
|
};
|
|
2583
2590
|
}
|
|
2584
|
-
async function pickupTool(
|
|
2585
|
-
if (!
|
|
2591
|
+
async function pickupTool(ctx, args) {
|
|
2592
|
+
if (!ctx.client)
|
|
2586
2593
|
return NOT_SET_UP;
|
|
2587
2594
|
const handoffId = str2(args.handoffId)?.trim();
|
|
2588
2595
|
if (!handoffId)
|
|
2589
2596
|
return { text: "Pass handoffId, from the handoffs tool.", isError: true };
|
|
2590
|
-
const r = await
|
|
2597
|
+
const r = await ctx.client.postFull("/proxy/handoff/pickup", { handoffId });
|
|
2591
2598
|
if (!r)
|
|
2592
2599
|
return { text: `Nothing was picked up. ${UNREACHABLE.text}`, isError: true };
|
|
2593
2600
|
if (r.status !== 200)
|
|
@@ -2608,7 +2615,7 @@ ${str2(j.task) ?? ""}
|
|
|
2608
2615
|
Carry it through from here.` };
|
|
2609
2616
|
const first = str2(j.firstUserText) ?? "";
|
|
2610
2617
|
const last = str2(j.lastUserText) ?? "";
|
|
2611
|
-
const n =
|
|
2618
|
+
const n = num4(j.messageCount);
|
|
2612
2619
|
return {
|
|
2613
2620
|
text: [
|
|
2614
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.`,
|
|
@@ -2623,29 +2630,60 @@ ${last}`] : [],
|
|
|
2623
2630
|
`)
|
|
2624
2631
|
};
|
|
2625
2632
|
}
|
|
2626
|
-
|
|
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) {
|
|
2627
2663
|
switch (name) {
|
|
2628
2664
|
case "usage":
|
|
2629
|
-
return usageTool(
|
|
2665
|
+
return usageTool(ctx);
|
|
2630
2666
|
case "estimate":
|
|
2631
|
-
return estimateTool(
|
|
2667
|
+
return estimateTool(ctx, args);
|
|
2632
2668
|
case "overnight":
|
|
2633
|
-
return overnightTool(
|
|
2669
|
+
return overnightTool(ctx, args);
|
|
2634
2670
|
case "jobs":
|
|
2635
|
-
return jobsTool(
|
|
2671
|
+
return jobsTool(ctx);
|
|
2636
2672
|
case "handoffs":
|
|
2637
|
-
return handoffsTool(
|
|
2673
|
+
return handoffsTool(ctx);
|
|
2638
2674
|
case "handoff":
|
|
2639
|
-
return handoffTool(
|
|
2675
|
+
return handoffTool(ctx, args);
|
|
2640
2676
|
case "pickup":
|
|
2641
|
-
return pickupTool(
|
|
2677
|
+
return pickupTool(ctx, args);
|
|
2678
|
+
case "defer":
|
|
2679
|
+
return deferTool(ctx, args);
|
|
2642
2680
|
default:
|
|
2643
2681
|
return { text: `Unknown tool: ${name}`, isError: true };
|
|
2644
2682
|
}
|
|
2645
2683
|
}
|
|
2646
2684
|
var response = (id, result) => JSON.stringify({ jsonrpc: "2.0", id, result });
|
|
2647
2685
|
var errorResponse = (id, code, message) => JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
|
|
2648
|
-
async function handleLine(line,
|
|
2686
|
+
async function handleLine(line, ctx) {
|
|
2649
2687
|
const trimmed = line.trim();
|
|
2650
2688
|
if (trimmed.length === 0)
|
|
2651
2689
|
return null;
|
|
@@ -2655,7 +2693,7 @@ async function handleLine(line, deps) {
|
|
|
2655
2693
|
} catch {
|
|
2656
2694
|
return errorResponse(null, -32700, "parse error");
|
|
2657
2695
|
}
|
|
2658
|
-
if (!
|
|
2696
|
+
if (!isObject7(msg))
|
|
2659
2697
|
return errorResponse(null, -32600, "invalid request");
|
|
2660
2698
|
const id = typeof msg.id === "string" || typeof msg.id === "number" ? msg.id : null;
|
|
2661
2699
|
const method = str2(msg.method);
|
|
@@ -2664,7 +2702,7 @@ async function handleLine(line, deps) {
|
|
|
2664
2702
|
return isNotification ? null : errorResponse(id, -32600, "invalid request");
|
|
2665
2703
|
if (method.startsWith("notifications/"))
|
|
2666
2704
|
return null;
|
|
2667
|
-
const params =
|
|
2705
|
+
const params = isObject7(msg.params) ? msg.params : {};
|
|
2668
2706
|
switch (method) {
|
|
2669
2707
|
case "initialize": {
|
|
2670
2708
|
const requested = str2(params.protocolVersion);
|
|
@@ -2680,8 +2718,8 @@ async function handleLine(line, deps) {
|
|
|
2680
2718
|
return response(id, { tools: TOOLS });
|
|
2681
2719
|
case "tools/call": {
|
|
2682
2720
|
const name = str2(params.name) ?? "";
|
|
2683
|
-
const args =
|
|
2684
|
-
const r = await callTool(name, args,
|
|
2721
|
+
const args = isObject7(params.arguments) ? params.arguments : {};
|
|
2722
|
+
const r = await callTool(name, args, ctx);
|
|
2685
2723
|
return response(id, {
|
|
2686
2724
|
content: [{ type: "text", text: r.text }],
|
|
2687
2725
|
...r.isError ? { isError: true } : {}
|
|
@@ -2692,6 +2730,38 @@ async function handleLine(line, deps) {
|
|
|
2692
2730
|
}
|
|
2693
2731
|
}
|
|
2694
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
|
+
|
|
2695
2765
|
// src/commands/mcp.ts
|
|
2696
2766
|
var mcp = async (o) => {
|
|
2697
2767
|
const stdin = o.stdin ?? process.stdin;
|
|
@@ -2705,7 +2775,7 @@ var mcp = async (o) => {
|
|
|
2705
2775
|
while (nl !== -1) {
|
|
2706
2776
|
const line = buffer.slice(0, nl);
|
|
2707
2777
|
buffer = buffer.slice(nl + 1);
|
|
2708
|
-
const out = await
|
|
2778
|
+
const out = await handleLine2(line, { home: o.home });
|
|
2709
2779
|
if (out !== null)
|
|
2710
2780
|
write(out);
|
|
2711
2781
|
nl = buffer.indexOf(`
|
|
@@ -2713,7 +2783,7 @@ var mcp = async (o) => {
|
|
|
2713
2783
|
}
|
|
2714
2784
|
}
|
|
2715
2785
|
if (buffer.trim().length > 0) {
|
|
2716
|
-
const out = await
|
|
2786
|
+
const out = await handleLine2(buffer, { home: o.home });
|
|
2717
2787
|
if (out !== null)
|
|
2718
2788
|
write(out);
|
|
2719
2789
|
}
|
|
@@ -3225,11 +3295,11 @@ init_config();
|
|
|
3225
3295
|
init_meter();
|
|
3226
3296
|
var HOOK_STDIN_TIMEOUT_MS = 1000;
|
|
3227
3297
|
var NOTIFICATION_TITLE = "sleds";
|
|
3228
|
-
var
|
|
3298
|
+
var isObject8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3229
3299
|
var notificationType = (text) => {
|
|
3230
3300
|
try {
|
|
3231
3301
|
const raw = JSON.parse(text);
|
|
3232
|
-
if (!
|
|
3302
|
+
if (!isObject8(raw))
|
|
3233
3303
|
return;
|
|
3234
3304
|
return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
|
|
3235
3305
|
} catch {
|
|
@@ -3404,8 +3474,8 @@ function resolveProfileDir(localStateJson, profile) {
|
|
|
3404
3474
|
if (localStateJson) {
|
|
3405
3475
|
try {
|
|
3406
3476
|
const raw = JSON.parse(localStateJson);
|
|
3407
|
-
const p =
|
|
3408
|
-
if (p &&
|
|
3477
|
+
const p = isObject9(raw) && isObject9(raw.profile) ? raw.profile : null;
|
|
3478
|
+
if (p && isObject9(p.info_cache))
|
|
3409
3479
|
cache = p.info_cache;
|
|
3410
3480
|
} catch {}
|
|
3411
3481
|
}
|
|
@@ -3414,7 +3484,7 @@ function resolveProfileDir(localStateJson, profile) {
|
|
|
3414
3484
|
const want = profile.toLowerCase();
|
|
3415
3485
|
const hits = [];
|
|
3416
3486
|
for (const [dir, info] of Object.entries(cache)) {
|
|
3417
|
-
if (!
|
|
3487
|
+
if (!isObject9(info))
|
|
3418
3488
|
continue;
|
|
3419
3489
|
const match = ["name", "user_name", "gaia_name"].some((k) => {
|
|
3420
3490
|
const v = info[k];
|
|
@@ -3452,23 +3522,23 @@ function sourceArgs(source) {
|
|
|
3452
3522
|
out.push("--token", source.token);
|
|
3453
3523
|
return out;
|
|
3454
3524
|
}
|
|
3455
|
-
var
|
|
3456
|
-
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;
|
|
3457
3527
|
var round2 = (n) => Math.round(n * 100) / 100;
|
|
3458
3528
|
function parseUsageResponse(raw, now = Date.now()) {
|
|
3459
|
-
if (!
|
|
3529
|
+
if (!isObject9(raw))
|
|
3460
3530
|
return null;
|
|
3461
3531
|
const out = { capturedAt: now, models: {} };
|
|
3462
3532
|
for (const [k, v] of Object.entries(raw)) {
|
|
3463
|
-
if (!
|
|
3533
|
+
if (!isObject9(v))
|
|
3464
3534
|
continue;
|
|
3465
3535
|
if (k === "extra_usage") {
|
|
3466
|
-
const credits =
|
|
3536
|
+
const credits = num5(v.used_credits);
|
|
3467
3537
|
if (credits !== undefined)
|
|
3468
3538
|
out.usedCreditsUsd = round2(credits / 100);
|
|
3469
3539
|
continue;
|
|
3470
3540
|
}
|
|
3471
|
-
const usedPct =
|
|
3541
|
+
const usedPct = num5(v.utilization);
|
|
3472
3542
|
const resetsAt = parseResetsAt(v.resets_at);
|
|
3473
3543
|
if (usedPct === undefined || usedPct < 0 || resetsAt === undefined)
|
|
3474
3544
|
continue;
|
|
@@ -4433,7 +4503,7 @@ var main = async (argv) => {
|
|
|
4433
4503
|
case "version":
|
|
4434
4504
|
case "--version":
|
|
4435
4505
|
case "-v":
|
|
4436
|
-
console.log("sleds 0.1.
|
|
4506
|
+
console.log("sleds 0.1.16");
|
|
4437
4507
|
return 0;
|
|
4438
4508
|
default:
|
|
4439
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
|
}
|