@sleds/cli 0.1.19 → 0.1.20
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 +402 -7
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -737,6 +737,43 @@ var SEAT_POST_MIN_DELTA_PCT2 = 1, SEAT_POST_MIN_INTERVAL_MS2, SEAT_POST_TIMEOUT_
|
|
|
737
737
|
const session = parseSeatSession2(raw.session);
|
|
738
738
|
if (session)
|
|
739
739
|
out.session = session;
|
|
740
|
+
if (isObject11(raw.cockpit) && raw.cockpit.kind === "cmux") {
|
|
741
|
+
const c = { kind: "cmux" };
|
|
742
|
+
if (isObject11(raw.cockpit.lines)) {
|
|
743
|
+
const lines = {};
|
|
744
|
+
for (const [k, v] of Object.entries(raw.cockpit.lines))
|
|
745
|
+
if (typeof v === "string")
|
|
746
|
+
lines[k] = v;
|
|
747
|
+
c.lines = lines;
|
|
748
|
+
}
|
|
749
|
+
if (raw.cockpit.progress === true)
|
|
750
|
+
c.progress = true;
|
|
751
|
+
const f = raw.cockpit.feed;
|
|
752
|
+
if (isObject11(f) && num6(f.at) !== undefined) {
|
|
753
|
+
const strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
754
|
+
const j = isObject11(f.jobs) ? f.jobs : {};
|
|
755
|
+
c.feed = {
|
|
756
|
+
at: num6(f.at),
|
|
757
|
+
handoffs: strs(f.handoffs),
|
|
758
|
+
jobs: {
|
|
759
|
+
scheduled: num6(j.scheduled) ?? 0,
|
|
760
|
+
running: num6(j.running) ?? 0,
|
|
761
|
+
delivered: num6(j.delivered) ?? 0,
|
|
762
|
+
failed: num6(j.failed) ?? 0
|
|
763
|
+
},
|
|
764
|
+
delivered: strs(f.delivered),
|
|
765
|
+
failed: strs(f.failed)
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
out.cockpit = c;
|
|
769
|
+
}
|
|
770
|
+
if (isObject11(raw.estimate)) {
|
|
771
|
+
const e = raw.estimate;
|
|
772
|
+
const project = typeof e.project === "string" ? e.project : undefined;
|
|
773
|
+
const [lo, hi, lm, hm, at] = [e.lowUsd, e.highUsd, e.lowMin, e.highMin, e.at].map(num6);
|
|
774
|
+
if (project && lo !== undefined && hi !== undefined && lm !== undefined && hm !== undefined && at !== undefined)
|
|
775
|
+
out.estimate = { project, lowUsd: lo, highUsd: hi, lowMin: lm, highMin: hm, at };
|
|
776
|
+
}
|
|
740
777
|
return out;
|
|
741
778
|
}, parseSeatSession2 = (v) => {
|
|
742
779
|
if (!isObject11(v) || typeof v.id !== "string" || !v.id)
|
|
@@ -1500,6 +1537,43 @@ var parseSeatState = (raw) => {
|
|
|
1500
1537
|
const session = parseSeatSession(raw.session);
|
|
1501
1538
|
if (session)
|
|
1502
1539
|
out.session = session;
|
|
1540
|
+
if (isObject5(raw.cockpit) && raw.cockpit.kind === "cmux") {
|
|
1541
|
+
const c = { kind: "cmux" };
|
|
1542
|
+
if (isObject5(raw.cockpit.lines)) {
|
|
1543
|
+
const lines = {};
|
|
1544
|
+
for (const [k, v] of Object.entries(raw.cockpit.lines))
|
|
1545
|
+
if (typeof v === "string")
|
|
1546
|
+
lines[k] = v;
|
|
1547
|
+
c.lines = lines;
|
|
1548
|
+
}
|
|
1549
|
+
if (raw.cockpit.progress === true)
|
|
1550
|
+
c.progress = true;
|
|
1551
|
+
const f = raw.cockpit.feed;
|
|
1552
|
+
if (isObject5(f) && num3(f.at) !== undefined) {
|
|
1553
|
+
const strs = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
1554
|
+
const j = isObject5(f.jobs) ? f.jobs : {};
|
|
1555
|
+
c.feed = {
|
|
1556
|
+
at: num3(f.at),
|
|
1557
|
+
handoffs: strs(f.handoffs),
|
|
1558
|
+
jobs: {
|
|
1559
|
+
scheduled: num3(j.scheduled) ?? 0,
|
|
1560
|
+
running: num3(j.running) ?? 0,
|
|
1561
|
+
delivered: num3(j.delivered) ?? 0,
|
|
1562
|
+
failed: num3(j.failed) ?? 0
|
|
1563
|
+
},
|
|
1564
|
+
delivered: strs(f.delivered),
|
|
1565
|
+
failed: strs(f.failed)
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
out.cockpit = c;
|
|
1569
|
+
}
|
|
1570
|
+
if (isObject5(raw.estimate)) {
|
|
1571
|
+
const e = raw.estimate;
|
|
1572
|
+
const project = typeof e.project === "string" ? e.project : undefined;
|
|
1573
|
+
const [lo, hi, lm, hm, at] = [e.lowUsd, e.highUsd, e.lowMin, e.highMin, e.at].map(num3);
|
|
1574
|
+
if (project && lo !== undefined && hi !== undefined && lm !== undefined && hm !== undefined && at !== undefined)
|
|
1575
|
+
out.estimate = { project, lowUsd: lo, highUsd: hi, lowMin: lm, highMin: hm, at };
|
|
1576
|
+
}
|
|
1503
1577
|
return out;
|
|
1504
1578
|
};
|
|
1505
1579
|
var parseSeatSession = (v) => {
|
|
@@ -1877,11 +1951,281 @@ var defer = async (o) => {
|
|
|
1877
1951
|
};
|
|
1878
1952
|
|
|
1879
1953
|
// src/commands/doctor.ts
|
|
1880
|
-
init_config();
|
|
1881
|
-
init_meter();
|
|
1882
1954
|
import { execFileSync } from "node:child_process";
|
|
1883
1955
|
import { existsSync as existsSync8, lstatSync, readlinkSync } from "node:fs";
|
|
1884
1956
|
import { resolve as resolve3 } from "node:path";
|
|
1957
|
+
|
|
1958
|
+
// src/channels/cockpit.ts
|
|
1959
|
+
var detectCockpit = (env) => {
|
|
1960
|
+
if (env.CMUX_SOCKET_PATH || env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID) {
|
|
1961
|
+
const c = { kind: "cmux" };
|
|
1962
|
+
if (env.CMUX_WORKSPACE_ID)
|
|
1963
|
+
c.workspaceId = env.CMUX_WORKSPACE_ID;
|
|
1964
|
+
if (env.CMUX_SURFACE_ID)
|
|
1965
|
+
c.surfaceId = env.CMUX_SURFACE_ID;
|
|
1966
|
+
if (env.CMUX_SOCKET_PATH)
|
|
1967
|
+
c.socket = env.CMUX_SOCKET_PATH;
|
|
1968
|
+
return c;
|
|
1969
|
+
}
|
|
1970
|
+
return null;
|
|
1971
|
+
};
|
|
1972
|
+
var SEP2 = " · ";
|
|
1973
|
+
var COLOR = {
|
|
1974
|
+
ok: "#6b7280",
|
|
1975
|
+
approaching: "#d97706",
|
|
1976
|
+
wall: "#dc2626"
|
|
1977
|
+
};
|
|
1978
|
+
var PRIORITY = { ok: 20, approaching: 90, wall: 100 };
|
|
1979
|
+
var BAR_LABEL = {
|
|
1980
|
+
sevenDay: "wk",
|
|
1981
|
+
fiveHour: "5h",
|
|
1982
|
+
spendLimit: "spend",
|
|
1983
|
+
credits: "credits"
|
|
1984
|
+
};
|
|
1985
|
+
var EXITS = "/sled handoff · sleds defer · or continue on the workspace key";
|
|
1986
|
+
var resetIn = (resetsAt, now) => {
|
|
1987
|
+
const mins = Math.max(0, Math.round((resetsAt - now) / 60000));
|
|
1988
|
+
return mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? `${mins % 60}m` : ""}` : `${mins}m`;
|
|
1989
|
+
};
|
|
1990
|
+
var cockpitSeatLine = (w, now = Date.now()) => {
|
|
1991
|
+
if (!w)
|
|
1992
|
+
return null;
|
|
1993
|
+
const bars = [
|
|
1994
|
+
["wk", w.sevenDay],
|
|
1995
|
+
["5h", w.fiveHour],
|
|
1996
|
+
["spend", w.spendLimit]
|
|
1997
|
+
].filter((x) => x[1] !== undefined);
|
|
1998
|
+
if (bars.length === 0)
|
|
1999
|
+
return null;
|
|
2000
|
+
const hot = bars.filter(([, b]) => b.usedPct >= APPROACHING_PCT).sort((a, b) => b[1].usedPct - a[1].usedPct)[0];
|
|
2001
|
+
if (!hot) {
|
|
2002
|
+
return { text: bars.map(([n, b]) => `${n} ${Math.round(b.usedPct)}%`).join(SEP2), stage: "ok" };
|
|
2003
|
+
}
|
|
2004
|
+
const [name, bar] = hot;
|
|
2005
|
+
const atWall = bar.usedPct >= 100;
|
|
2006
|
+
return {
|
|
2007
|
+
text: `${atWall ? "" : "⚠ "}${name} ${atWall ? "at the wall" : `${Math.round(bar.usedPct)}%`}${SEP2}resets ${resetIn(bar.resetsAt, now)}`,
|
|
2008
|
+
stage: atWall ? "wall" : "approaching",
|
|
2009
|
+
progress: { value: Math.min(1, bar.usedPct / 100), label: `seat ${name}` }
|
|
2010
|
+
};
|
|
2011
|
+
};
|
|
2012
|
+
var ws = (c) => c.workspaceId ? ["--workspace", c.workspaceId] : [];
|
|
2013
|
+
var keyed = (argv, c, key, text, prior, style, next) => {
|
|
2014
|
+
if (text) {
|
|
2015
|
+
next[key] = text;
|
|
2016
|
+
if (text !== prior)
|
|
2017
|
+
argv.push([
|
|
2018
|
+
"set-status",
|
|
2019
|
+
`sleds.${key}`,
|
|
2020
|
+
text,
|
|
2021
|
+
"--icon",
|
|
2022
|
+
style.icon,
|
|
2023
|
+
...style.color ? ["--color", style.color] : [],
|
|
2024
|
+
"--priority",
|
|
2025
|
+
String(style.priority),
|
|
2026
|
+
...ws(c)
|
|
2027
|
+
]);
|
|
2028
|
+
} else if (prior) {
|
|
2029
|
+
argv.push(["clear-status", `sleds.${key}`, ...ws(c)]);
|
|
2030
|
+
}
|
|
2031
|
+
};
|
|
2032
|
+
var ESTIMATE_TTL_MS = 2 * 60 * 60 * 1000;
|
|
2033
|
+
var usd2 = (n) => n < 10 ? `$${n.toFixed(2)}` : `$${Math.round(n)}`;
|
|
2034
|
+
var hrs = (min) => min < 60 ? `${Math.round(min)}m` : `${(min / 60).toFixed(min < 600 ? 1 : 0)}h`;
|
|
2035
|
+
var cockpitEstimateLine = (e, now) => {
|
|
2036
|
+
if (!e || now - e.at > ESTIMATE_TTL_MS)
|
|
2037
|
+
return null;
|
|
2038
|
+
const proj = e.project.length > 28 ? `…${e.project.slice(-27)}` : e.project;
|
|
2039
|
+
return `est ${usd2(e.lowUsd)}–${usd2(e.highUsd)}${SEP2}${hrs(e.lowMin)}–${hrs(e.highMin)}${SEP2}${proj}`;
|
|
2040
|
+
};
|
|
2041
|
+
var cockpitFeedLines = (f) => {
|
|
2042
|
+
if (!f)
|
|
2043
|
+
return { handoff: null, jobs: null };
|
|
2044
|
+
const n = f.handoffs.length;
|
|
2045
|
+
const handoff = n ? `${n} hand-off${n === 1 ? "" : "s"} waiting${SEP2}/sled pickup` : null;
|
|
2046
|
+
const parts = [];
|
|
2047
|
+
if (f.jobs.running)
|
|
2048
|
+
parts.push(`${f.jobs.running} running`);
|
|
2049
|
+
if (f.jobs.scheduled)
|
|
2050
|
+
parts.push(`${f.jobs.scheduled} scheduled`);
|
|
2051
|
+
if (f.jobs.delivered)
|
|
2052
|
+
parts.push(`${f.jobs.delivered} done`);
|
|
2053
|
+
if (f.jobs.failed)
|
|
2054
|
+
parts.push(`${f.jobs.failed} failed`);
|
|
2055
|
+
return { handoff, jobs: parts.length ? `overnight: ${parts.join(SEP2)}` : null };
|
|
2056
|
+
};
|
|
2057
|
+
var renderCockpit = (cockpit, window, events, prior, now = Date.now(), extra = {}) => {
|
|
2058
|
+
const argv = [];
|
|
2059
|
+
const priorLines = prior?.lines ?? {};
|
|
2060
|
+
const lines = {};
|
|
2061
|
+
const state = { kind: cockpit.kind };
|
|
2062
|
+
const seat = cockpitSeatLine(window, now);
|
|
2063
|
+
keyed(argv, cockpit, "seat", seat?.text ?? null, priorLines.seat, {
|
|
2064
|
+
icon: "⛷",
|
|
2065
|
+
color: COLOR[seat?.stage ?? "ok"],
|
|
2066
|
+
priority: PRIORITY[seat?.stage ?? "ok"]
|
|
2067
|
+
}, lines);
|
|
2068
|
+
if (seat?.progress) {
|
|
2069
|
+
state.progress = true;
|
|
2070
|
+
argv.push([
|
|
2071
|
+
"set-progress",
|
|
2072
|
+
seat.progress.value.toFixed(2),
|
|
2073
|
+
"--label",
|
|
2074
|
+
seat.progress.label,
|
|
2075
|
+
...ws(cockpit)
|
|
2076
|
+
]);
|
|
2077
|
+
} else if (prior?.progress) {
|
|
2078
|
+
argv.push(["clear-progress", ...ws(cockpit)]);
|
|
2079
|
+
}
|
|
2080
|
+
keyed(argv, cockpit, "estimate", cockpitEstimateLine(extra.estimate, now), priorLines.estimate, { icon: "≈", priority: 30 }, lines);
|
|
2081
|
+
const feed = extra.feed ?? prior?.feed;
|
|
2082
|
+
const fl = cockpitFeedLines(feed);
|
|
2083
|
+
keyed(argv, cockpit, "handoff", fl.handoff, priorLines.handoff, { icon: "⇄", color: "#2563eb", priority: 60 }, lines);
|
|
2084
|
+
keyed(argv, cockpit, "jobs", fl.jobs, priorLines.jobs, { icon: "☾", priority: 25 }, lines);
|
|
2085
|
+
if (Object.keys(lines).length)
|
|
2086
|
+
state.lines = lines;
|
|
2087
|
+
if (feed)
|
|
2088
|
+
state.feed = feed;
|
|
2089
|
+
for (const e of events) {
|
|
2090
|
+
const bar = BAR_LABEL[e.bar];
|
|
2091
|
+
if (e.stage === "hit") {
|
|
2092
|
+
const sub = e.bar === "credits" ? "usage credits exhausted" : `resets ${resetIn(e.resetsAt, now)}`;
|
|
2093
|
+
argv.push([
|
|
2094
|
+
"notify",
|
|
2095
|
+
"--title",
|
|
2096
|
+
`SLEDS · seat ${bar} at the wall`,
|
|
2097
|
+
"--subtitle",
|
|
2098
|
+
sub,
|
|
2099
|
+
"--body",
|
|
2100
|
+
EXITS,
|
|
2101
|
+
...ws(cockpit)
|
|
2102
|
+
]);
|
|
2103
|
+
} else {
|
|
2104
|
+
argv.push([
|
|
2105
|
+
"log",
|
|
2106
|
+
"--level",
|
|
2107
|
+
"warning",
|
|
2108
|
+
"--source",
|
|
2109
|
+
"sleds",
|
|
2110
|
+
`${bar} bar ${Math.round(e.usedPct)}% — resets ${resetIn(e.resetsAt, now)}`,
|
|
2111
|
+
...ws(cockpit)
|
|
2112
|
+
]);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
if (extra.feed) {
|
|
2116
|
+
const before = prior?.feed;
|
|
2117
|
+
const newHandoffs = extra.feed.handoffs.filter((id) => !(before?.handoffs ?? []).includes(id));
|
|
2118
|
+
if (newHandoffs.length)
|
|
2119
|
+
argv.push([
|
|
2120
|
+
"notify",
|
|
2121
|
+
"--title",
|
|
2122
|
+
`SLEDS · ${newHandoffs.length === 1 ? "a hand-off is" : `${newHandoffs.length} hand-offs are`} waiting for you`,
|
|
2123
|
+
"--body",
|
|
2124
|
+
`/sled pickup ${newHandoffs[0]}`,
|
|
2125
|
+
...ws(cockpit)
|
|
2126
|
+
]);
|
|
2127
|
+
for (const id of extra.feed.delivered.filter((id2) => !(before?.delivered ?? []).includes(id2)))
|
|
2128
|
+
argv.push([
|
|
2129
|
+
"log",
|
|
2130
|
+
"--level",
|
|
2131
|
+
"success",
|
|
2132
|
+
"--source",
|
|
2133
|
+
"sleds",
|
|
2134
|
+
`overnight job done · /sled resume ${id}`,
|
|
2135
|
+
...ws(cockpit)
|
|
2136
|
+
]);
|
|
2137
|
+
for (const id of extra.feed.failed.filter((id2) => !(before?.failed ?? []).includes(id2)))
|
|
2138
|
+
argv.push([
|
|
2139
|
+
"notify",
|
|
2140
|
+
"--title",
|
|
2141
|
+
"SLEDS · overnight job failed",
|
|
2142
|
+
"--body",
|
|
2143
|
+
`sleds jobs · ${id}`,
|
|
2144
|
+
...ws(cockpit)
|
|
2145
|
+
]);
|
|
2146
|
+
}
|
|
2147
|
+
return { argv, state };
|
|
2148
|
+
};
|
|
2149
|
+
var FEED_MIN_INTERVAL_MS = 60000;
|
|
2150
|
+
var isObj = (v) => typeof v === "object" && v !== null;
|
|
2151
|
+
var feedFromJobs = (body, me, now) => {
|
|
2152
|
+
const f = {
|
|
2153
|
+
at: now,
|
|
2154
|
+
handoffs: [],
|
|
2155
|
+
jobs: { scheduled: 0, running: 0, delivered: 0, failed: 0 },
|
|
2156
|
+
delivered: [],
|
|
2157
|
+
failed: []
|
|
2158
|
+
};
|
|
2159
|
+
if (!isObj(body))
|
|
2160
|
+
return f;
|
|
2161
|
+
for (const h of Array.isArray(body.handoffs) ? body.handoffs.filter(isObj) : []) {
|
|
2162
|
+
const mine = me !== undefined && typeof h.from === "string" && h.from.toLowerCase() === me.toLowerCase();
|
|
2163
|
+
if (h.forMe === true && !mine && typeof h.handoffId === "string")
|
|
2164
|
+
f.handoffs.push(h.handoffId);
|
|
2165
|
+
}
|
|
2166
|
+
for (const j of Array.isArray(body.jobs) ? body.jobs.filter(isObj) : []) {
|
|
2167
|
+
const id = typeof j.jobId === "string" ? j.jobId : undefined;
|
|
2168
|
+
switch (j.status) {
|
|
2169
|
+
case "scheduled":
|
|
2170
|
+
case "queued":
|
|
2171
|
+
case "awaiting_approval":
|
|
2172
|
+
f.jobs.scheduled++;
|
|
2173
|
+
break;
|
|
2174
|
+
case "running":
|
|
2175
|
+
case "estimating":
|
|
2176
|
+
f.jobs.running++;
|
|
2177
|
+
break;
|
|
2178
|
+
case "delivered":
|
|
2179
|
+
f.jobs.delivered++;
|
|
2180
|
+
if (id)
|
|
2181
|
+
f.delivered.push(id);
|
|
2182
|
+
break;
|
|
2183
|
+
case "failed":
|
|
2184
|
+
f.jobs.failed++;
|
|
2185
|
+
if (id)
|
|
2186
|
+
f.failed.push(id);
|
|
2187
|
+
break;
|
|
2188
|
+
default:
|
|
2189
|
+
break;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
return f;
|
|
2193
|
+
};
|
|
2194
|
+
var fetchFeed = async (o, now) => {
|
|
2195
|
+
try {
|
|
2196
|
+
const res = await (o.fetchFn ?? fetch)(`${o.proxyUrl}/proxy/jobs`, {
|
|
2197
|
+
headers: { authorization: `Bearer ${o.token}` },
|
|
2198
|
+
signal: AbortSignal.timeout(o.timeoutMs ?? 1500)
|
|
2199
|
+
});
|
|
2200
|
+
if (!res.ok)
|
|
2201
|
+
return null;
|
|
2202
|
+
return feedFromJobs(await res.json(), o.email, now);
|
|
2203
|
+
} catch {
|
|
2204
|
+
return null;
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
var runCockpit = async (cockpit, argv, opts = {}) => {
|
|
2208
|
+
const spawn = opts.spawn ?? Bun.spawn;
|
|
2209
|
+
const timeoutMs = opts.timeoutMs ?? 300;
|
|
2210
|
+
const bin = cockpit.kind === "cmux" ? "cmux" : cockpit.kind;
|
|
2211
|
+
await Promise.allSettled(argv.map(async (a) => {
|
|
2212
|
+
const proc = spawn([bin, ...a], { stdout: "ignore", stderr: "ignore", stdin: "ignore" });
|
|
2213
|
+
const timer = setTimeout(() => {
|
|
2214
|
+
try {
|
|
2215
|
+
proc.kill();
|
|
2216
|
+
} catch {}
|
|
2217
|
+
}, timeoutMs);
|
|
2218
|
+
try {
|
|
2219
|
+
await proc.exited;
|
|
2220
|
+
} finally {
|
|
2221
|
+
clearTimeout(timer);
|
|
2222
|
+
}
|
|
2223
|
+
}));
|
|
2224
|
+
};
|
|
2225
|
+
|
|
2226
|
+
// src/commands/doctor.ts
|
|
2227
|
+
init_config();
|
|
2228
|
+
init_meter();
|
|
1885
2229
|
init_settings();
|
|
1886
2230
|
|
|
1887
2231
|
// src/commands/musher.ts
|
|
@@ -2065,6 +2409,12 @@ async function runDoctor(o) {
|
|
|
2065
2409
|
fix: skill ? undefined : "sleds init (re-run; keeps your token)"
|
|
2066
2410
|
});
|
|
2067
2411
|
}
|
|
2412
|
+
const cockpit = detectCockpit(env);
|
|
2413
|
+
checks.push({
|
|
2414
|
+
level: "ok",
|
|
2415
|
+
name: "cockpit",
|
|
2416
|
+
detail: cockpit ? `${cockpit.kind}${cockpit.workspaceId ? ` (workspace ${cockpit.workspaceId})` : ""} — seat line in the sidebar, ring at the wall` : "none detected (run inside cmux to mirror the seat into its sidebar)"
|
|
2417
|
+
});
|
|
2068
2418
|
const nodeVersion = o.nodeVersion ?? process.version;
|
|
2069
2419
|
checks.push({
|
|
2070
2420
|
level: nodeOk(nodeVersion) ? "ok" : "warn",
|
|
@@ -4680,6 +5030,21 @@ var specEvent = async (o) => {
|
|
|
4680
5030
|
if (b && typeof b.lowUsd === "number" && typeof b.highUsd === "number") {
|
|
4681
5031
|
const min = (n) => typeof n === "number" ? Math.round(n) : "?";
|
|
4682
5032
|
log(`sleds: ${project} sized at $${b.lowUsd.toFixed(2)}–${b.highUsd.toFixed(2)} · ${min(b.lowMinutes)}–${min(b.highMinutes)} min`);
|
|
5033
|
+
if (typeof b.lowMinutes === "number" && typeof b.highMinutes === "number") {
|
|
5034
|
+
try {
|
|
5035
|
+
writeSeatState(o.home, {
|
|
5036
|
+
...readSeatState(o.home),
|
|
5037
|
+
estimate: {
|
|
5038
|
+
project,
|
|
5039
|
+
lowUsd: b.lowUsd,
|
|
5040
|
+
highUsd: b.highUsd,
|
|
5041
|
+
lowMin: b.lowMinutes,
|
|
5042
|
+
highMin: b.highMinutes,
|
|
5043
|
+
at: (o.now ?? Date.now)()
|
|
5044
|
+
}
|
|
5045
|
+
});
|
|
5046
|
+
} catch {}
|
|
5047
|
+
}
|
|
4683
5048
|
}
|
|
4684
5049
|
} catch {}
|
|
4685
5050
|
return 0;
|
|
@@ -4818,6 +5183,7 @@ var status = async (o) => {
|
|
|
4818
5183
|
};
|
|
4819
5184
|
|
|
4820
5185
|
// src/commands/statusline.ts
|
|
5186
|
+
init_config();
|
|
4821
5187
|
init_meter();
|
|
4822
5188
|
var statusline = async (o) => {
|
|
4823
5189
|
const log = o.log ?? console.log;
|
|
@@ -4826,9 +5192,10 @@ var statusline = async (o) => {
|
|
|
4826
5192
|
const window = seatWindowFromStdin(input, now);
|
|
4827
5193
|
const session = seatSessionFromStdin(input, now);
|
|
4828
5194
|
let posts = [];
|
|
5195
|
+
let events = [];
|
|
4829
5196
|
try {
|
|
4830
5197
|
if (window)
|
|
4831
|
-
posts = rememberSeat(o, window, session, now, input);
|
|
5198
|
+
({ posts, events } = rememberSeat(o, window, session, now, input));
|
|
4832
5199
|
else {
|
|
4833
5200
|
const st = readSeatState(o.home);
|
|
4834
5201
|
const credits = creditsEvent(o, st, input, now);
|
|
@@ -4840,8 +5207,10 @@ var statusline = async (o) => {
|
|
|
4840
5207
|
};
|
|
4841
5208
|
writeSeatState(o.home, credits.event ? markWarned(next, [credits.event]) : next);
|
|
4842
5209
|
}
|
|
4843
|
-
if (credits.event)
|
|
4844
|
-
|
|
5210
|
+
if (credits.event) {
|
|
5211
|
+
events = [credits.event];
|
|
5212
|
+
posts = announce(o, events);
|
|
5213
|
+
}
|
|
4845
5214
|
}
|
|
4846
5215
|
} catch {}
|
|
4847
5216
|
const seatFragment = formatSeatFragment(window, now);
|
|
@@ -4858,9 +5227,35 @@ var statusline = async (o) => {
|
|
|
4858
5227
|
line = formatMeterLine(null, now, seatFragment);
|
|
4859
5228
|
}
|
|
4860
5229
|
log(line);
|
|
5230
|
+
posts.push(speakToCockpit(o, window, events, now));
|
|
4861
5231
|
await Promise.allSettled(posts);
|
|
4862
5232
|
return 0;
|
|
4863
5233
|
};
|
|
5234
|
+
var speakToCockpit = async (o, window, events, now) => {
|
|
5235
|
+
const cockpit = detectCockpit(o.env ?? process.env);
|
|
5236
|
+
if (!cockpit)
|
|
5237
|
+
return;
|
|
5238
|
+
try {
|
|
5239
|
+
const st = readSeatState(o.home);
|
|
5240
|
+
let feed;
|
|
5241
|
+
const src = meterSource(o.home);
|
|
5242
|
+
if (src && now - (st.cockpit?.feed?.at ?? 0) >= FEED_MIN_INTERVAL_MS) {
|
|
5243
|
+
const got = await fetchFeed({ ...src, email: readConfig(o.home).email, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs }, now);
|
|
5244
|
+
if (got)
|
|
5245
|
+
feed = got;
|
|
5246
|
+
}
|
|
5247
|
+
const r = renderCockpit(cockpit, window, events, st.cockpit, now, {
|
|
5248
|
+
estimate: st.estimate,
|
|
5249
|
+
...feed ? { feed } : {}
|
|
5250
|
+
});
|
|
5251
|
+
const changed = r.argv.length > 0 || feed !== undefined;
|
|
5252
|
+
if (!changed)
|
|
5253
|
+
return;
|
|
5254
|
+
writeSeatState(o.home, { ...readSeatState(o.home), cockpit: r.state });
|
|
5255
|
+
if (r.argv.length)
|
|
5256
|
+
await runCockpit(cockpit, r.argv, { spawn: o.spawn });
|
|
5257
|
+
} catch {}
|
|
5258
|
+
};
|
|
4864
5259
|
var rememberSeat = (o, window, session, now, input) => {
|
|
4865
5260
|
const prior = readSeatState(o.home);
|
|
4866
5261
|
let state = { ...prior, window, ...session ? { session } : {} };
|
|
@@ -4879,7 +5274,7 @@ var rememberSeat = (o, window, session, now, input) => {
|
|
|
4879
5274
|
state = markWarned(state, events);
|
|
4880
5275
|
posts.push(...announce(o, events));
|
|
4881
5276
|
writeSeatState(o.home, state);
|
|
4882
|
-
return posts;
|
|
5277
|
+
return { posts, events };
|
|
4883
5278
|
};
|
|
4884
5279
|
var creditsEvent = (o, prior, input, now) => {
|
|
4885
5280
|
const path = transcriptPathFromStdin(input);
|
|
@@ -5462,7 +5857,7 @@ var main = async (argv) => {
|
|
|
5462
5857
|
case "version":
|
|
5463
5858
|
case "--version":
|
|
5464
5859
|
case "-v":
|
|
5465
|
-
console.log("sleds 0.1.
|
|
5860
|
+
console.log("sleds 0.1.20");
|
|
5466
5861
|
return 0;
|
|
5467
5862
|
default:
|
|
5468
5863
|
console.error(`unknown command: ${cmd}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleds/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "SLEDS CLI \u2014 the cost and limits of AI agent work, decided at the wall. Point Claude Code at a SLEDS proxy; estimate before you run; hold, switch model, or run overnight when you hit a limit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|