@sleds/cli 0.1.41 → 0.1.43
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 -107
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -345,11 +345,11 @@ var init_guides = __esm(() => {
|
|
|
345
345
|
{
|
|
346
346
|
do: "Open `Work` and read the tabs",
|
|
347
347
|
where: "Work",
|
|
348
|
-
note: "
|
|
348
|
+
note: "Three tabs, each with its count: Ready · On sleds · Landed this week; what waits for product's yes is on Intake (a line above the tabs says how many). The search at the top narrows the rows as you type; the chips beside the tabs narrow by source, worth, type and `filed by` (you first). Your own work, and what is in front of you, is on Sleds."
|
|
349
349
|
},
|
|
350
350
|
{
|
|
351
351
|
do: "Read a pool item: the band, the worth, the chip, the readiness",
|
|
352
|
-
note: "`suggested` (dashed, with a star) names the sled the rules (and the model) suggest, with why on hover; `waiting for product` needs a yes; the five boxes say what the item has (why · done · worth · repo · route) and `N gaps` names what is missing before anyone could start; an amber note says a related or duplicate item exists. `Take it` puts it on your sled; the ⋯ menu can put it on the suggested sled."
|
|
352
|
+
note: "`suggested` (dashed, with a star) names the sled the rules (and the model) suggest, with why on hover; `waiting for product` needs a yes (on Intake, where a row opens a modal to decide it); the five boxes say what the item has (why · done · worth · repo · route) and `N gaps` names what is missing before anyone could start; an amber note says a related or duplicate item exists. `Take it` puts it on your sled; the ⋯ menu can put it on the suggested sled."
|
|
353
353
|
},
|
|
354
354
|
{
|
|
355
355
|
do: "Read a sled card: the seats, the jobs, the band spent",
|
|
@@ -679,16 +679,16 @@ var init_guides = __esm(() => {
|
|
|
679
679
|
note: "Emails, comma-separated. Blank means any owner or admin of the workspace."
|
|
680
680
|
},
|
|
681
681
|
{
|
|
682
|
-
do: "Approvers decide
|
|
683
|
-
where: "Overview › `Waiting on product
|
|
684
|
-
note: "`Approve` opens the gate; `Defer` keeps it closed and carries the note back to the filer, who answers in the brief and it comes back. In chat the same is `approve {project, decision, note?}`."
|
|
682
|
+
do: "Approvers decide on Intake",
|
|
683
|
+
where: "Intake (the rail), or Overview › `Waiting on product`",
|
|
684
|
+
note: "A row opens the ask in a modal over the ledger: `Approve` opens the gate; `Defer` keeps it closed and carries the note back to the filer, who answers in the brief and it comes back; `Ask the filer` sends the one question. A, D and Q do the same from the keyboard and move to the next ask. In chat the same is `approve {project, decision, note?}`."
|
|
685
685
|
},
|
|
686
686
|
{
|
|
687
687
|
do: "Plan after the yes",
|
|
688
688
|
note: "An approved epic can be planned in chat (`plan`): its milestones are sized, filed under it, and inherit the yes — they do not queue again. The epic's cap is the sum."
|
|
689
689
|
}
|
|
690
690
|
],
|
|
691
|
-
check: "File an ask from chat (`intake`): its item shows waiting for product
|
|
691
|
+
check: "File an ask from chat (`intake`): its item shows waiting for product on Intake, the approvers see it under Waiting on product, and `run` on it is refused until someone approves."
|
|
692
692
|
}
|
|
693
693
|
];
|
|
694
694
|
});
|
|
@@ -1083,6 +1083,83 @@ var init_verbs = __esm(() => {
|
|
|
1083
1083
|
];
|
|
1084
1084
|
});
|
|
1085
1085
|
|
|
1086
|
+
// ../../packages/shared/src/intake/forms.ts
|
|
1087
|
+
function norm(label) {
|
|
1088
|
+
return label.replace(/[*_`~]/g, "").replace(/\(s\)/gi, "s").replace(/[:?]+\s*$/, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1089
|
+
}
|
|
1090
|
+
function readForm(text) {
|
|
1091
|
+
const lines = text.split(/\r?\n/);
|
|
1092
|
+
for (const def of FORMS) {
|
|
1093
|
+
const found = [];
|
|
1094
|
+
let cur = null;
|
|
1095
|
+
let known = 0;
|
|
1096
|
+
for (const raw of lines) {
|
|
1097
|
+
const line = raw.trim();
|
|
1098
|
+
if (!line)
|
|
1099
|
+
continue;
|
|
1100
|
+
const inline = /^\**([^:*]{2,60}?)\**\s*:\s*(.+)$/.exec(line);
|
|
1101
|
+
const whole = norm(line);
|
|
1102
|
+
const slotWhole = def.labels[whole];
|
|
1103
|
+
const slotInline = inline ? def.labels[norm(inline[1] ?? "")] : undefined;
|
|
1104
|
+
if (slotWhole) {
|
|
1105
|
+
cur = { label: line.replace(/[*_]/g, "").replace(/:\s*$/, ""), slot: slotWhole, value: [] };
|
|
1106
|
+
found.push(cur);
|
|
1107
|
+
known++;
|
|
1108
|
+
} else if (slotInline && inline) {
|
|
1109
|
+
cur = { label: (inline[1] ?? "").trim(), slot: slotInline, value: [inline[2] ?? ""] };
|
|
1110
|
+
found.push(cur);
|
|
1111
|
+
known++;
|
|
1112
|
+
} else if (/^\*[^*]{2,60}\*:?$/.test(line) && cur) {
|
|
1113
|
+
cur = { label: line.replace(/[*:]/g, "").trim(), slot: null, value: [] };
|
|
1114
|
+
found.push(cur);
|
|
1115
|
+
} else if (cur)
|
|
1116
|
+
cur.value.push(line);
|
|
1117
|
+
}
|
|
1118
|
+
if (known < def.min)
|
|
1119
|
+
continue;
|
|
1120
|
+
const val = (f) => f.value.join(" ").trim();
|
|
1121
|
+
const of = (slot) => found.filter((f) => f.slot === slot && val(f)).map((f) => ({ label: f.label, v: val(f) }));
|
|
1122
|
+
const ask = of("ask")[0]?.v;
|
|
1123
|
+
const who = of("for").map((x) => x.v);
|
|
1124
|
+
const extra = found.filter((f) => f.slot === null && val(f)).map((f) => `${f.label}: ${val(f)}`);
|
|
1125
|
+
const why = [who.length > 0 ? `For ${who.join(", ")}.` : "", ask ?? "", ...extra].filter(Boolean).join(" ");
|
|
1126
|
+
const touches = of("touches").map((x) => `${x.label} ${x.v}`).join(" · ");
|
|
1127
|
+
const n = of("demand")[0]?.v;
|
|
1128
|
+
const demand = n ? /^\d+$/.test(n) ? `${n} clients affected` : n : undefined;
|
|
1129
|
+
const rev = of("revenue")[0]?.v;
|
|
1130
|
+
return {
|
|
1131
|
+
form: def.name,
|
|
1132
|
+
...ask ? { ask } : {},
|
|
1133
|
+
...why ? { why } : {},
|
|
1134
|
+
...touches ? { touches } : {},
|
|
1135
|
+
...demand ? { demand } : {},
|
|
1136
|
+
...rev && YES.test(rev) ? { worth: "high · impacting revenue" } : {}
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
var FORMS, YES;
|
|
1142
|
+
var init_forms = __esm(() => {
|
|
1143
|
+
FORMS = [
|
|
1144
|
+
{
|
|
1145
|
+
name: "Create Tech Support Ticket",
|
|
1146
|
+
labels: {
|
|
1147
|
+
"users experiencing the issue": "for",
|
|
1148
|
+
"user experiencing the issue": "for",
|
|
1149
|
+
sids: "touches",
|
|
1150
|
+
sid: "touches",
|
|
1151
|
+
"location ids": "touches",
|
|
1152
|
+
"location id": "touches",
|
|
1153
|
+
"issue description": "ask",
|
|
1154
|
+
"number of clients affected": "demand",
|
|
1155
|
+
"is the issue currently impacting revenue": "revenue"
|
|
1156
|
+
},
|
|
1157
|
+
min: 3
|
|
1158
|
+
}
|
|
1159
|
+
];
|
|
1160
|
+
YES = /^(yes|y|yep|true|definitely|it is)\b/i;
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1086
1163
|
// ../../packages/shared/src/intake/brief.ts
|
|
1087
1164
|
function labelOf(line) {
|
|
1088
1165
|
const h = /^#{1,6}\s+(.+?)\s*#*\s*$/.exec(line);
|
|
@@ -1155,13 +1232,16 @@ function worthFromAnswer(answer) {
|
|
|
1155
1232
|
function draftBrief(input) {
|
|
1156
1233
|
const text = (input.text ?? "").trim();
|
|
1157
1234
|
const a = input.answers ?? {};
|
|
1158
|
-
const
|
|
1159
|
-
const
|
|
1235
|
+
const form = readForm(text);
|
|
1236
|
+
const parsed = sections(text);
|
|
1237
|
+
const preamble = parsed.preamble;
|
|
1238
|
+
const byKey = form ? { ...parsed.byKey, why: form.why, touches: form.touches, demand: form.demand } : parsed.byKey;
|
|
1239
|
+
const askText = a.ask?.trim() || (form?.ask ? askFrom(form.ask, form.ask) : askFrom(text, preamble));
|
|
1160
1240
|
if (!askText)
|
|
1161
1241
|
return null;
|
|
1162
1242
|
const ask = a.ask?.trim() ? { text: clip(a.ask, MAX_ASK), from: "filer" } : { text: clip(askText, MAX_ASK), from: "artifact" };
|
|
1163
1243
|
const pick = (k) => field(a[k], "filer") ?? field(byKey[k], "artifact");
|
|
1164
|
-
const worth = worthFromAnswer(a.worth) ?? parseWorth(text) ?? undefined;
|
|
1244
|
+
const worth = worthFromAnswer(a.worth) ?? parseWorth(text) ?? worthFromAnswer(form?.worth) ?? undefined;
|
|
1165
1245
|
const sources = [...new Set((input.links ?? []).map((l) => l.trim()).filter(Boolean))].slice(0, MAX_SOURCES);
|
|
1166
1246
|
return {
|
|
1167
1247
|
ask,
|
|
@@ -1288,6 +1368,7 @@ var MAX_FIELD = 280, MAX_ASK = 120, MAX_SOURCES = 12, QUESTIONS, REQUIRED_TO_FIL
|
|
|
1288
1368
|
}, fromWord = (f) => f.from === "filer" ? "from you" : f.from === "product" ? "from the product map" : "from what you sent";
|
|
1289
1369
|
var init_brief = __esm(() => {
|
|
1290
1370
|
init_worth();
|
|
1371
|
+
init_forms();
|
|
1291
1372
|
QUESTIONS = {
|
|
1292
1373
|
why: "Who is it for, and why now?",
|
|
1293
1374
|
done: "What does done look like, in your words?",
|
|
@@ -1346,6 +1427,9 @@ var init_related = __esm(() => {
|
|
|
1346
1427
|
"via"
|
|
1347
1428
|
]);
|
|
1348
1429
|
});
|
|
1430
|
+
|
|
1431
|
+
// ../../packages/shared/src/intake/rank.ts
|
|
1432
|
+
var init_rank = () => {};
|
|
1349
1433
|
// ../../packages/shared/src/profile/profile.ts
|
|
1350
1434
|
var LIGHT;
|
|
1351
1435
|
var init_profile = __esm(() => {
|
|
@@ -1766,7 +1850,7 @@ function buildRoute(drafts, opts = {}) {
|
|
|
1766
1850
|
problems.push({ level: "error", text: "a route needs at least one node" });
|
|
1767
1851
|
if (drafts.length > MAX_NODES)
|
|
1768
1852
|
problems.push({ level: "error", text: `a route holds at most ${MAX_NODES} nodes` });
|
|
1769
|
-
const given = drafts.map((d) => d.key ?
|
|
1853
|
+
const given = drafts.map((d) => d.key ? norm2(d.key) : "");
|
|
1770
1854
|
const fresh = mintKeys(given.filter((k) => !k).length, given.filter((k) => k));
|
|
1771
1855
|
let f = 0;
|
|
1772
1856
|
const keys = given.map((k) => k || fresh[f++]);
|
|
@@ -1781,7 +1865,7 @@ function buildRoute(drafts, opts = {}) {
|
|
|
1781
1865
|
const title = stripControl(d.title.replace(/\s+/g, " ")).trim().slice(0, 160);
|
|
1782
1866
|
if (!title)
|
|
1783
1867
|
problems.push({ level: "error", node: key, text: `node ${key} has no title` });
|
|
1784
|
-
const blockedBy = [...new Set((d.blockedBy ?? []).map(
|
|
1868
|
+
const blockedBy = [...new Set((d.blockedBy ?? []).map(norm2).filter(Boolean))];
|
|
1785
1869
|
for (const b of blockedBy) {
|
|
1786
1870
|
if (b === key)
|
|
1787
1871
|
problems.push({ level: "error", node: key, text: `node ${key} blocks itself` });
|
|
@@ -1958,7 +2042,7 @@ ${state}`)}, fillcolor="${fill}", color="${border}"${style}];`);
|
|
|
1958
2042
|
`)}
|
|
1959
2043
|
`;
|
|
1960
2044
|
}
|
|
1961
|
-
var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4,
|
|
2045
|
+
var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4, norm2 = (s) => s.trim().toUpperCase(), AREA_ROOTS, NOT_AREAS, sameRepo = (a, b) => a.length === 0 || b.length === 0 || a.some((r) => b.includes(r)), DOT_COLORS, ROUTE_DRAFT_RULE = "Draft the route: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on (by key: A, B, C…). Prefer wide waves over chains deeper than four. Make the tough calls once, so each node's session follows them: an `approach` paragraph for the whole route, and on each node a `plan` — its approach, what it `provides` that later nodes rely on (names, shapes, files), what it `expects` from the nodes it waits on, and the `doneChecks` that say it landed. Show the graph and the plans as text and ask for a yes; file nothing before it. Then `route {q, approach, nodes: [{key, title, touches, repos, blockedBy, plan: {approach, provides, expects, doneChecks}}], confirm: true}`.";
|
|
1962
2046
|
var init_route = __esm(() => {
|
|
1963
2047
|
CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
1964
2048
|
AREA_ROOTS = new Set(["apps", "packages", "services", "libs", "crates", "modules"]);
|
|
@@ -5543,6 +5627,72 @@ var init_fieldMap = __esm(() => {
|
|
|
5543
5627
|
var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
|
|
5544
5628
|
var init_whatsNew = __esm(() => {
|
|
5545
5629
|
WHATS_NEW = [
|
|
5630
|
+
{
|
|
5631
|
+
date: "2026-10-21",
|
|
5632
|
+
title: "The Slack thread is the item's discussion",
|
|
5633
|
+
lede: "An item filed from a Slack thread keeps that thread. What people say there is on the item, what is said on the item goes back there, every move is one line in the thread, and everyone who has spoken hears what happens next.",
|
|
5634
|
+
sections: [
|
|
5635
|
+
{
|
|
5636
|
+
title: "Replies are comments; moves are lines",
|
|
5637
|
+
where: "Slack, after Filed as",
|
|
5638
|
+
body: "After Filed as, a reply in the thread is a comment on the item, as that person, marked from Slack. Approve, defer, take, land and archive each post one line in the thread with a link to the item. The item's Details has Slack, which opens the thread."
|
|
5639
|
+
},
|
|
5640
|
+
{
|
|
5641
|
+
title: "Comments go out",
|
|
5642
|
+
where: "The item › Discussion",
|
|
5643
|
+
body: "A comment written on the item is posted in its thread as the person who wrote it, and to Jira when the comments switch is on. The line under the composer says where it will go. SLEDS's own strips stay on SLEDS."
|
|
5644
|
+
},
|
|
5645
|
+
{
|
|
5646
|
+
title: "Watchers",
|
|
5647
|
+
where: "The item › Discussion, and @SLEDS in the thread",
|
|
5648
|
+
body: "Whoever filed it, holds it, or has spoken on it hears each move — once, never their own. Watch on the Discussion adds you; Unwatch stops it. In a thread, @SLEDS watch QW-123 ties an item to that thread, @SLEDS status answers with the item's status, and @SLEDS ask: … puts a question to the filer."
|
|
5649
|
+
},
|
|
5650
|
+
{
|
|
5651
|
+
title: "Forms are read as fields",
|
|
5652
|
+
where: "Slack, filing from a workflow post",
|
|
5653
|
+
body: "A support ticket form posted by a Slack workflow is read field by field: the issue description is the ask, who is affected and why go to the why, the IDs to what it touches, the count of clients to demand, and a yes on revenue to a high worth. The draft then asks only what the form did not say."
|
|
5654
|
+
},
|
|
5655
|
+
{
|
|
5656
|
+
title: "Which workspace, picked",
|
|
5657
|
+
where: "Slack, filing",
|
|
5658
|
+
body: "A channel tied to a workspace files there. With several workspaces and none tied to the channel, SLEDS asks which, as a numbered pick: reply with the number. The Jira key is never typed — the Filed as reply shows it."
|
|
5659
|
+
}
|
|
5660
|
+
],
|
|
5661
|
+
also: [
|
|
5662
|
+
{
|
|
5663
|
+
label: "Fixed",
|
|
5664
|
+
text: "An ask filed from Slack now creates its Jira ticket in the workspace it was filed on, and the reply names the key or says why none was made."
|
|
5665
|
+
}
|
|
5666
|
+
]
|
|
5667
|
+
},
|
|
5668
|
+
{
|
|
5669
|
+
date: "2026-10-20",
|
|
5670
|
+
title: "Intake: the gate gets its own page",
|
|
5671
|
+
lede: "What waits for product's yes was the first tab on Work. It is a page now, Intake, between Work and Sleds on the rail: the same rows, with the policy an approver decides under on one line, the counts over them, and a modal that reads one ask fast and decides it without leaving the list. Work keeps what is ready and who is on it, and opens on Ready.",
|
|
5672
|
+
sections: [
|
|
5673
|
+
{
|
|
5674
|
+
title: "One ask at a time, over the ledger",
|
|
5675
|
+
where: "Intake › a row",
|
|
5676
|
+
body: "A row on Intake opens a centred modal, not the item's page: what SLEDS says and why, the six steps from filed to landed with this one's place on them, the brief, and — folded, each with its answer on the fold — the ticket, the split and who takes it. Approve, Defer with a note and Ask the filer are at the foot, with Open the full item for anything deeper. ‹ › and the arrow keys walk the ledger in its order; A, D and Q decide and move on; Decide in order starts at the top. Esc goes back to the ledger as it was."
|
|
5677
|
+
},
|
|
5678
|
+
{
|
|
5679
|
+
title: "The policy on the page",
|
|
5680
|
+
where: "Intake, above the rows",
|
|
5681
|
+
body: "Above the rows, one dashed line says whose yes is needed and for which asks, who the approvers are, and what a yes writes to Jira — read from Policy, with Change in Policy beside it. Under it, four counts: open, need a worth, waiting on a filer, and became work this week (which opens the Record)."
|
|
5682
|
+
},
|
|
5683
|
+
{
|
|
5684
|
+
title: "Where things went on Work",
|
|
5685
|
+
where: "Work",
|
|
5686
|
+
body: "Work's tabs are Ready, On sleds and Landed. A line above them says how many wait in Intake and links there; the Board layout says the same in place of its Waiting column. The rail's count under Work is what is ready now; Intake's is what waits. Old links — Approvals, a waiting lane, a search hit on a waiting item — land on Intake."
|
|
5687
|
+
}
|
|
5688
|
+
],
|
|
5689
|
+
also: [
|
|
5690
|
+
{
|
|
5691
|
+
label: "Next",
|
|
5692
|
+
text: "Worth from the modal and from Jira's Business Value, the return of each ask against its time, SLEDS's own suggested verdict, and auto-approve in shadow mode first — spec 41's later slices."
|
|
5693
|
+
}
|
|
5694
|
+
]
|
|
5695
|
+
},
|
|
5546
5696
|
{
|
|
5547
5697
|
date: "2026-10-10",
|
|
5548
5698
|
title: "Docs beside the work, and webhooks for your own tools",
|
|
@@ -6544,7 +6694,7 @@ function nodePrompt(o) {
|
|
|
6544
6694
|
`- Work on branch \`${o.branch}\` from the latest \`origin/${base}\` (create it if this checkout is not on it). One branch, one PR.`,
|
|
6545
6695
|
`- Open the PR against \`${base}\` with \`gh pr create${visual ? " --draft" : ""}\`${o.prTitle ? ` and title it exactly \`${o.prTitle}\` (the repo's CI checks it)` : ""}. Its body carries \`SLEDS-Node: ${claim.node}\` on the line under \`SLEDS-Source:\` when the pack names a source.`,
|
|
6546
6696
|
...claim.pack.includes(PR_HEADER_OPEN) ? [
|
|
6547
|
-
`- The PR body starts with the pack's PR header block (\`${PR_HEADER_OPEN}\` … \`${PR_HEADER_CLOSE}\`, in its part 5), above everything else
|
|
6697
|
+
`- The PR body starts with the pack's PR header block (\`${PR_HEADER_OPEN}\` … \`${PR_HEADER_CLOSE}\`, in its part 5), above everything else. The pack wrote it before your work existed: when you make a mock (a screenshot or an artifact) or write a doc, put its link on that part's line in place of \`None\` — not only lower in the body (SL-1746). When you edit the body later, keep it on top.`
|
|
6548
6698
|
] : [],
|
|
6549
6699
|
"- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
|
|
6550
6700
|
"- Stay inside this node's scope. Work another node needs goes in the PR description, not in this branch.",
|
|
@@ -7522,7 +7672,9 @@ var init_src = __esm(() => {
|
|
|
7522
7672
|
init_guides();
|
|
7523
7673
|
init_verbs();
|
|
7524
7674
|
init_brief();
|
|
7675
|
+
init_forms();
|
|
7525
7676
|
init_related();
|
|
7677
|
+
init_rank();
|
|
7526
7678
|
init_markdown2();
|
|
7527
7679
|
init_plan3();
|
|
7528
7680
|
init_recommend();
|
|
@@ -8221,7 +8373,9 @@ init_cowork();
|
|
|
8221
8373
|
init_guides();
|
|
8222
8374
|
init_verbs();
|
|
8223
8375
|
init_brief();
|
|
8376
|
+
init_forms();
|
|
8224
8377
|
init_related();
|
|
8378
|
+
init_rank();
|
|
8225
8379
|
init_markdown2();
|
|
8226
8380
|
init_plan3();
|
|
8227
8381
|
init_recommend();
|
|
@@ -9199,12 +9353,13 @@ var scanTranscriptForCredits = (path, prior) => {
|
|
|
9199
9353
|
return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
|
|
9200
9354
|
}
|
|
9201
9355
|
};
|
|
9202
|
-
var seatWindowBody = (w) => ({
|
|
9356
|
+
var seatWindowBody = (w, account) => ({
|
|
9203
9357
|
capturedAt: w.capturedAt,
|
|
9204
9358
|
source: "claude",
|
|
9205
9359
|
...w.fiveHour ? { fiveHour: w.fiveHour } : {},
|
|
9206
9360
|
...w.sevenDay ? { sevenDay: w.sevenDay } : {},
|
|
9207
|
-
...w.spendLimit ? { spendLimit: w.spendLimit } : {}
|
|
9361
|
+
...w.spendLimit ? { spendLimit: w.spendLimit } : {},
|
|
9362
|
+
...account ? { account } : {}
|
|
9208
9363
|
});
|
|
9209
9364
|
var postJson = async (o, path, body) => {
|
|
9210
9365
|
const fetchFn = o.fetchFn ?? fetch;
|
|
@@ -9220,7 +9375,7 @@ var postJson = async (o, path, body) => {
|
|
|
9220
9375
|
return false;
|
|
9221
9376
|
}
|
|
9222
9377
|
};
|
|
9223
|
-
var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
|
|
9378
|
+
var postSeatWindow = (o, w, account) => postJson(o, "/proxy/seat", seatWindowBody(w, account));
|
|
9224
9379
|
var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", { source: "claude", ...input });
|
|
9225
9380
|
var SEAT_WALL_LINE = "Seat limit hit. sleds on --until-reset to continue on the platform key, or sleds defer to resume here at the reset.";
|
|
9226
9381
|
var SEAT_WALL_LINE_NO_KEY = "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.";
|
|
@@ -9414,7 +9569,7 @@ import {
|
|
|
9414
9569
|
existsSync as existsSync16,
|
|
9415
9570
|
lstatSync,
|
|
9416
9571
|
mkdtempSync,
|
|
9417
|
-
readFileSync as
|
|
9572
|
+
readFileSync as readFileSync17,
|
|
9418
9573
|
readlinkSync,
|
|
9419
9574
|
rmSync as rmSync8,
|
|
9420
9575
|
writeFileSync as writeFileSync17
|
|
@@ -9817,7 +9972,7 @@ function resolveRepoPath(o) {
|
|
|
9817
9972
|
const want = o.repos.length ? o.repos.join(", ") : "this node";
|
|
9818
9973
|
const dir = names[0] ? ` (the folder that holds ${names[0]})` : "";
|
|
9819
9974
|
return {
|
|
9820
|
-
error: `No repo for ${want}: this engine runs in ${o.cwd}, which is not a git checkout. Set Repos
|
|
9975
|
+
error: `No repo for ${want}: this engine runs in ${o.cwd}, which is not a git checkout. Set Repos folders in Settings › Running work${dir}, or start the engine with --repo.`
|
|
9821
9976
|
};
|
|
9822
9977
|
}
|
|
9823
9978
|
for (const name of names) {
|
|
@@ -10267,28 +10422,17 @@ function cmuxRunner(exec, deps = {}) {
|
|
|
10267
10422
|
}
|
|
10268
10423
|
let id = parseCmuxWorkspace(r.stdout);
|
|
10269
10424
|
const ref = id ? null : parseCmuxRef(r.stdout);
|
|
10270
|
-
let closed = false;
|
|
10271
10425
|
if (ref) {
|
|
10272
|
-
let
|
|
10273
|
-
for (let i = 0;i < CMUX_LIST_TRIES; i++) {
|
|
10426
|
+
for (let i = 0;i < CMUX_LIST_TRIES && !id; i++) {
|
|
10274
10427
|
if (i > 0)
|
|
10275
10428
|
sleep(CMUX_LIST_WAIT_MS);
|
|
10276
10429
|
const l = cmux(["--id-format", "both", "list-workspaces"]);
|
|
10277
|
-
if (l.status
|
|
10278
|
-
|
|
10279
|
-
listed = l.stdout;
|
|
10280
|
-
if (namedLines(l.stdout, o.name).length > 0)
|
|
10281
|
-
break;
|
|
10282
|
-
}
|
|
10283
|
-
if (listed !== null) {
|
|
10284
|
-
id = cmuxIdForRef(listed, ref, o.name);
|
|
10285
|
-
const stray = id ? null : cmuxIdForName(listed, o.name);
|
|
10286
|
-
if (stray)
|
|
10287
|
-
closed = closeWorkspace(stray);
|
|
10430
|
+
if (l.status === 0)
|
|
10431
|
+
id = cmuxIdForRef(l.stdout, ref, o.name);
|
|
10288
10432
|
}
|
|
10289
10433
|
}
|
|
10290
10434
|
if (!id) {
|
|
10291
|
-
throw new Error(ref ? `cmux workspace create ${o.name} gave ${ref} but no workspace id for it; ${
|
|
10435
|
+
throw new Error(ref ? `cmux workspace create ${o.name} gave ${ref} but no workspace id for it; ${UNWATCHED}` : `cmux workspace create ${o.name} returned no workspace (${r.stdout.trim() || "nothing"}); ${UNWATCHED}`);
|
|
10292
10436
|
}
|
|
10293
10437
|
if (group && listed && !joined) {
|
|
10294
10438
|
const c = cmux([
|
|
@@ -10353,7 +10497,7 @@ function cmuxRunner(exec, deps = {}) {
|
|
|
10353
10497
|
throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
|
|
10354
10498
|
},
|
|
10355
10499
|
stop(h) {
|
|
10356
|
-
|
|
10500
|
+
closeWorkspace(h.id);
|
|
10357
10501
|
if (!h.group)
|
|
10358
10502
|
return;
|
|
10359
10503
|
const l = cmux(LIST_GROUPS);
|
|
@@ -10422,7 +10566,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10422
10566
|
name: flags.name,
|
|
10423
10567
|
os: host.os ?? process.platform,
|
|
10424
10568
|
runner: flags.runner,
|
|
10425
|
-
version: host.version ?? "0.1.
|
|
10569
|
+
version: host.version ?? "0.1.43" ?? "dev",
|
|
10426
10570
|
running: live.length,
|
|
10427
10571
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
10428
10572
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -10441,10 +10585,13 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10441
10585
|
}
|
|
10442
10586
|
async function postBeat(client, body) {
|
|
10443
10587
|
try {
|
|
10444
|
-
const r = await client.
|
|
10445
|
-
|
|
10588
|
+
const r = await client.postFull("/proxy/engine/beat", { ...body });
|
|
10589
|
+
if (r?.status === 200 && r.json?.ok === true)
|
|
10590
|
+
return { ok: true };
|
|
10591
|
+
const error = r?.json?.error;
|
|
10592
|
+
return r?.status === 409 && typeof error === "string" ? { ok: false, refused: error } : { ok: false };
|
|
10446
10593
|
} catch {
|
|
10447
|
-
return false;
|
|
10594
|
+
return { ok: false };
|
|
10448
10595
|
}
|
|
10449
10596
|
}
|
|
10450
10597
|
function beater(client, now, everyMs = BEAT_EVERY_MS) {
|
|
@@ -11232,6 +11379,11 @@ var notificationType = (text) => {
|
|
|
11232
11379
|
|
|
11233
11380
|
// src/commands/permissionEvent.ts
|
|
11234
11381
|
var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
11382
|
+
var NOT_A_TOOL = new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
|
|
11383
|
+
var isToolPermission = (message) => {
|
|
11384
|
+
const tool = /permission to use\s+(.+?)\.?\s*$/i.exec(message)?.[1];
|
|
11385
|
+
return !tool || !NOT_A_TOOL.has(tool.replace(/[\s_-]/g, "").toLowerCase());
|
|
11386
|
+
};
|
|
11235
11387
|
var readPermissionMark = (file) => {
|
|
11236
11388
|
try {
|
|
11237
11389
|
const raw = JSON.parse(readFileSync10(file, "utf8"));
|
|
@@ -12114,7 +12266,36 @@ var DIALOG_ASKS = /^Do you want to .+\?$/;
|
|
|
12114
12266
|
var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
|
|
12115
12267
|
var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
|
|
12116
12268
|
var unbox = (line) => line.trimEnd().replace(/^\s*[│┃║]\s?/, "").replace(/\s*[│┃║]$/, "");
|
|
12269
|
+
var PICKER_CHIP = /^[☐☒✔✓□■]\s+\S/;
|
|
12270
|
+
var PICKER_OPTION = /^(?:Type something\.?|Chat about this|Submit answers?)$/i;
|
|
12271
|
+
var PICKER_FOOTER = /\benter to select\b|\bto navigate\b/i;
|
|
12272
|
+
var PICKER_ASKS = /^(?:Would you like to proceed\?|Ready to code\?)$/;
|
|
12273
|
+
var PICKER_REACH = 40;
|
|
12274
|
+
function choicePicker(screen) {
|
|
12275
|
+
const lines = screen.split(`
|
|
12276
|
+
`).map((l) => unbox(l).trim());
|
|
12277
|
+
let c = -1;
|
|
12278
|
+
for (let i = lines.length - 1;i >= 0; i--)
|
|
12279
|
+
if (/^❯\s*\d\.\s/.test(lines[i])) {
|
|
12280
|
+
c = i;
|
|
12281
|
+
break;
|
|
12282
|
+
}
|
|
12283
|
+
if (c < 0)
|
|
12284
|
+
return false;
|
|
12285
|
+
let from = c;
|
|
12286
|
+
while (from > 0 && c - from < PICKER_REACH && !/^⏺/.test(lines[from - 1]))
|
|
12287
|
+
from--;
|
|
12288
|
+
for (let i = from;i < lines.length; i++) {
|
|
12289
|
+
const t = lines[i];
|
|
12290
|
+
const choice = DIALOG_OPTION.exec(t)?.[3]?.trim() ?? "";
|
|
12291
|
+
if (PICKER_CHIP.test(t) || PICKER_ASKS.test(t) || PICKER_OPTION.test(choice) || i > c && PICKER_FOOTER.test(t))
|
|
12292
|
+
return true;
|
|
12293
|
+
}
|
|
12294
|
+
return false;
|
|
12295
|
+
}
|
|
12117
12296
|
function permissionDialog(screen) {
|
|
12297
|
+
if (choicePicker(screen))
|
|
12298
|
+
return null;
|
|
12118
12299
|
const raw = screen.split(`
|
|
12119
12300
|
`);
|
|
12120
12301
|
const lines = raw.map(unbox);
|
|
@@ -13129,7 +13310,11 @@ async function settlePermission(d, runner, run, shown) {
|
|
|
13129
13310
|
run.permission = undefined;
|
|
13130
13311
|
return false;
|
|
13131
13312
|
}
|
|
13132
|
-
|
|
13313
|
+
let mark = markFile ? readPermissionMark(markFile) : null;
|
|
13314
|
+
if (mark && markFile && (!isToolPermission(mark.message) || runner.readsSession && choicePicker(shown))) {
|
|
13315
|
+
clearPermissionMark(markFile);
|
|
13316
|
+
mark = null;
|
|
13317
|
+
}
|
|
13133
13318
|
if (!dialog && !mark)
|
|
13134
13319
|
return false;
|
|
13135
13320
|
if (!dialog && runner.readsSession && !run.seenTicks)
|
|
@@ -15293,7 +15478,7 @@ var statusLine2 = (session, events, now) => {
|
|
|
15293
15478
|
return `managed session: idle — its turn ended${money}`;
|
|
15294
15479
|
}
|
|
15295
15480
|
};
|
|
15296
|
-
var
|
|
15481
|
+
var YES2 = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
|
|
15297
15482
|
function managedRunner(exec, deps = {}) {
|
|
15298
15483
|
const env = deps.env ?? process.env;
|
|
15299
15484
|
const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
|
|
@@ -15418,7 +15603,7 @@ ${renderTranscript(eventMessages(all))}`;
|
|
|
15418
15603
|
const idle = lastIdle(all);
|
|
15419
15604
|
const asks = str9(obj2(idle?.stop_reason)?.type) === "requires_action" ? pendingAsks(all, idle) : [];
|
|
15420
15605
|
if (asks.length) {
|
|
15421
|
-
const allow =
|
|
15606
|
+
const allow = YES2.test(text);
|
|
15422
15607
|
api("POST", `/v1/sessions/${h.id}/events`, {
|
|
15423
15608
|
events: asks.map((e) => ({
|
|
15424
15609
|
type: "user.tool_confirmation",
|
|
@@ -16314,7 +16499,7 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
16314
16499
|
runner: deps.runner.name,
|
|
16315
16500
|
atOnce: flags.atOnce,
|
|
16316
16501
|
interval: flags.interval,
|
|
16317
|
-
cliVersion: "0.1.
|
|
16502
|
+
cliVersion: "0.1.43"
|
|
16318
16503
|
};
|
|
16319
16504
|
const beat = (checkedAt) => {
|
|
16320
16505
|
const m = deps.machine;
|
|
@@ -16625,7 +16810,6 @@ async function enginePauseCmd(o) {
|
|
|
16625
16810
|
|
|
16626
16811
|
// src/commands/engineInstall.ts
|
|
16627
16812
|
init_config();
|
|
16628
|
-
init_state();
|
|
16629
16813
|
import {
|
|
16630
16814
|
existsSync as existsSync14,
|
|
16631
16815
|
mkdirSync as mkdirSync14,
|
|
@@ -16637,6 +16821,7 @@ import {
|
|
|
16637
16821
|
} from "node:fs";
|
|
16638
16822
|
import { hostname as hostname3 } from "node:os";
|
|
16639
16823
|
import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join18 } from "node:path";
|
|
16824
|
+
init_state();
|
|
16640
16825
|
|
|
16641
16826
|
// src/schedule.ts
|
|
16642
16827
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
@@ -17124,6 +17309,42 @@ var musherAgentState = (home, env = process.env) => {
|
|
|
17124
17309
|
}
|
|
17125
17310
|
};
|
|
17126
17311
|
|
|
17312
|
+
// src/claudeAccount.ts
|
|
17313
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
17314
|
+
var PLANS = {
|
|
17315
|
+
claude_max: "Max",
|
|
17316
|
+
claude_pro: "Pro",
|
|
17317
|
+
claude_team: "Team",
|
|
17318
|
+
claude_enterprise: "Enterprise"
|
|
17319
|
+
};
|
|
17320
|
+
var str11 = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : undefined;
|
|
17321
|
+
var parseClaudeAccount = (raw) => {
|
|
17322
|
+
if (typeof raw !== "object" || raw === null)
|
|
17323
|
+
return null;
|
|
17324
|
+
const acct = raw.oauthAccount;
|
|
17325
|
+
if (typeof acct !== "object" || acct === null)
|
|
17326
|
+
return null;
|
|
17327
|
+
const a = acct;
|
|
17328
|
+
const email = str11(a.emailAddress);
|
|
17329
|
+
if (!email)
|
|
17330
|
+
return null;
|
|
17331
|
+
const out = { email };
|
|
17332
|
+
const type = str11(a.organizationType);
|
|
17333
|
+
if (type && PLANS[type])
|
|
17334
|
+
out.plan = PLANS[type];
|
|
17335
|
+
const orgName = str11(a.organizationName);
|
|
17336
|
+
if (orgName)
|
|
17337
|
+
out.orgName = orgName;
|
|
17338
|
+
return out;
|
|
17339
|
+
};
|
|
17340
|
+
var readClaudeAccount = (home, env = process.env) => {
|
|
17341
|
+
try {
|
|
17342
|
+
return parseClaudeAccount(JSON.parse(readFileSync16(claudeUserConfigPath2(home, env), "utf8")));
|
|
17343
|
+
} catch {
|
|
17344
|
+
return null;
|
|
17345
|
+
}
|
|
17346
|
+
};
|
|
17347
|
+
|
|
17127
17348
|
// src/commands/status.ts
|
|
17128
17349
|
init_config();
|
|
17129
17350
|
init_settings();
|
|
@@ -17608,12 +17829,12 @@ function engineChecks(o, env) {
|
|
|
17608
17829
|
level: "warn",
|
|
17609
17830
|
name: "repos dir",
|
|
17610
17831
|
detail: `${dirs.join(" → ")} — ${missing.join(" and ")} ${missing.length === 1 ? "does" : "do"} not exist`,
|
|
17611
|
-
fix: "create the folder or drop it from Repos
|
|
17832
|
+
fix: "create the folder or drop it from Repos folders in Settings › Running work"
|
|
17612
17833
|
} : dirs.length ? { level: "ok", name: "repos dir", detail: dirs.join(" → ") } : repo ? { level: "ok", name: "repos dir", detail: repo } : {
|
|
17613
17834
|
level: "warn",
|
|
17614
17835
|
name: "repos dir",
|
|
17615
|
-
detail: `${e.label} names no --repos-dir — it works only if
|
|
17616
|
-
fix: "set Repos
|
|
17836
|
+
detail: `${e.label} names no --repos-dir — it works only if your engine profile sets Repos folders`,
|
|
17837
|
+
fix: "set Repos folders in Settings › Running work, or sleds engine install --repos-dir ~/code"
|
|
17617
17838
|
});
|
|
17618
17839
|
}
|
|
17619
17840
|
return checks;
|
|
@@ -17623,7 +17844,7 @@ function boxChecks(o, env, which, need) {
|
|
|
17623
17844
|
const file = boxEnvPath(o.home);
|
|
17624
17845
|
let named = false;
|
|
17625
17846
|
try {
|
|
17626
|
-
named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(
|
|
17847
|
+
named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync17(file, "utf8"));
|
|
17627
17848
|
} catch {
|
|
17628
17849
|
named = false;
|
|
17629
17850
|
}
|
|
@@ -17729,7 +17950,7 @@ import {
|
|
|
17729
17950
|
appendFileSync as appendFileSync3,
|
|
17730
17951
|
chmodSync as chmodSync5,
|
|
17731
17952
|
mkdirSync as mkdirSync16,
|
|
17732
|
-
readFileSync as
|
|
17953
|
+
readFileSync as readFileSync18,
|
|
17733
17954
|
existsSync as realExists2,
|
|
17734
17955
|
rmSync as rmSync9,
|
|
17735
17956
|
writeFileSync as writeFileSync18
|
|
@@ -18151,7 +18372,7 @@ function realPidAlive2(pid) {
|
|
|
18151
18372
|
function memberMcpConfigs2(o, cwd) {
|
|
18152
18373
|
const read = o.readJson ?? ((p) => {
|
|
18153
18374
|
try {
|
|
18154
|
-
return JSON.parse(
|
|
18375
|
+
return JSON.parse(readFileSync18(p, "utf8"));
|
|
18155
18376
|
} catch {
|
|
18156
18377
|
return null;
|
|
18157
18378
|
}
|
|
@@ -18376,7 +18597,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
18376
18597
|
runner: deps.runner.name,
|
|
18377
18598
|
atOnce: flags.atOnce,
|
|
18378
18599
|
interval: flags.interval,
|
|
18379
|
-
cliVersion: "0.1.
|
|
18600
|
+
cliVersion: "0.1.43"
|
|
18380
18601
|
};
|
|
18381
18602
|
const beat = (checkedAt) => {
|
|
18382
18603
|
const m = deps.machine;
|
|
@@ -18683,6 +18904,13 @@ var engineCmd = async (o) => {
|
|
|
18683
18904
|
if (client) {
|
|
18684
18905
|
const send = beater(client, o.now ?? Date.now);
|
|
18685
18906
|
const who = { name: flags.name, runner: runner.name };
|
|
18907
|
+
let refused;
|
|
18908
|
+
const sayRefused = (r) => {
|
|
18909
|
+
if (!r || r.ok || !r.refused || r.refused === refused)
|
|
18910
|
+
return;
|
|
18911
|
+
refused = r.refused;
|
|
18912
|
+
error(`${r.refused}. This engine is not shown as up until it beats under a name of its own.`);
|
|
18913
|
+
};
|
|
18686
18914
|
afterTick = () => {
|
|
18687
18915
|
try {
|
|
18688
18916
|
const m = deps.machine;
|
|
@@ -18690,7 +18918,7 @@ var engineCmd = async (o) => {
|
|
|
18690
18918
|
atOnce: deps.atOnce,
|
|
18691
18919
|
...m?.last ? { machine: m.last } : {},
|
|
18692
18920
|
...m?.hold && !m.off ? { hold: m.hold } : {}
|
|
18693
|
-
}));
|
|
18921
|
+
}))?.then(sayRefused, () => {});
|
|
18694
18922
|
} catch {}
|
|
18695
18923
|
};
|
|
18696
18924
|
}
|
|
@@ -18818,11 +19046,10 @@ async function enginePauseCmd2(o) {
|
|
|
18818
19046
|
|
|
18819
19047
|
// src/commands/engineInstall.ts
|
|
18820
19048
|
init_config();
|
|
18821
|
-
init_state();
|
|
18822
19049
|
import {
|
|
18823
19050
|
existsSync as existsSync17,
|
|
18824
19051
|
mkdirSync as mkdirSync17,
|
|
18825
|
-
readFileSync as
|
|
19052
|
+
readFileSync as readFileSync19,
|
|
18826
19053
|
readdirSync as readdirSync4,
|
|
18827
19054
|
realpathSync as realpathSync2,
|
|
18828
19055
|
rmSync as rmSync10,
|
|
@@ -18830,6 +19057,7 @@ import {
|
|
|
18830
19057
|
} from "node:fs";
|
|
18831
19058
|
import { hostname as hostname5 } from "node:os";
|
|
18832
19059
|
import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join21 } from "node:path";
|
|
19060
|
+
init_state();
|
|
18833
19061
|
var ENGINE_LABEL2 = "ai.sleds.engine";
|
|
18834
19062
|
var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
|
|
18835
19063
|
${ENGINE_USAGE}`;
|
|
@@ -18948,7 +19176,7 @@ function readEngines2(dir, suffix, manager, parse) {
|
|
|
18948
19176
|
const path = join21(dir, name);
|
|
18949
19177
|
let parsed;
|
|
18950
19178
|
try {
|
|
18951
|
-
parsed = parse(
|
|
19179
|
+
parsed = parse(readFileSync19(path, "utf8"), name);
|
|
18952
19180
|
} catch {
|
|
18953
19181
|
parsed = null;
|
|
18954
19182
|
}
|
|
@@ -18973,6 +19201,50 @@ function installedEngines2(home) {
|
|
|
18973
19201
|
];
|
|
18974
19202
|
}
|
|
18975
19203
|
var unsupported2 = (platform) => `sleds engine install needs launchd (macOS) or systemd (Linux). On ${platform}, keep \`sleds engine\` running under your own service manager (a scheduled task, tmux).`;
|
|
19204
|
+
var REPOS_DIR_NEEDED = "Set the folder your repos are cloned in first: `sleds engine profile set reposDir=~/code` (Settings › Running work › Repos folders), or `sleds engine install --repos-dir ~/code`. An installed engine starts outside any checkout, so without one every node it takes parks on No repo.";
|
|
19205
|
+
async function ensureReposDir(o) {
|
|
19206
|
+
const log = o.log ?? console.log;
|
|
19207
|
+
const error = o.error ?? console.error;
|
|
19208
|
+
const parsed = parseEngineArgs(o.argv, o.host ?? hostname5());
|
|
19209
|
+
if ("error" in parsed)
|
|
19210
|
+
return true;
|
|
19211
|
+
if (parsed.given.includes("repos-dir") || parsed.given.includes("repo"))
|
|
19212
|
+
return true;
|
|
19213
|
+
const client = engineClient({ home: o.home, ...o.env ? { env: o.env } : {}, ...o.client ? { client: o.client } : {} }, parsed.flags.org);
|
|
19214
|
+
if ("error" in client)
|
|
19215
|
+
return true;
|
|
19216
|
+
const profile = await readProfile(client);
|
|
19217
|
+
if (!profile) {
|
|
19218
|
+
log("note: your engine profile could not be read — if it names no repos folder, the engine says so when it starts");
|
|
19219
|
+
return true;
|
|
19220
|
+
}
|
|
19221
|
+
if (profile.reposDir)
|
|
19222
|
+
return true;
|
|
19223
|
+
if (!o.ask) {
|
|
19224
|
+
error(REPOS_DIR_NEEDED);
|
|
19225
|
+
return false;
|
|
19226
|
+
}
|
|
19227
|
+
const answer = (await o.ask("Where are your repos cloned? Folders searched in order, split on : (~/code): ")).trim();
|
|
19228
|
+
const dirs = splitReposDirs(answer);
|
|
19229
|
+
if (dirs.length === 0) {
|
|
19230
|
+
error(REPOS_DIR_NEEDED);
|
|
19231
|
+
return false;
|
|
19232
|
+
}
|
|
19233
|
+
const exists = o.exists ?? existsSync17;
|
|
19234
|
+
const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join21(o.home, d.slice(1)) : d));
|
|
19235
|
+
if (missing.length) {
|
|
19236
|
+
error(`Not a folder on this machine: ${missing.join(", ")}. ${REPOS_DIR_NEEDED}`);
|
|
19237
|
+
return false;
|
|
19238
|
+
}
|
|
19239
|
+
const r = await client.postFull("/proxy/engine/profile", { profile: { reposDir: dirs.join(":") } }).catch(() => null);
|
|
19240
|
+
if (!r || r.status >= 300 || r.json?.ok === false) {
|
|
19241
|
+
const why = typeof r?.json?.error === "string" ? r.json.error : "SLEDS unreachable";
|
|
19242
|
+
error(`The repos folder was not saved (${why}). ${REPOS_DIR_NEEDED}`);
|
|
19243
|
+
return false;
|
|
19244
|
+
}
|
|
19245
|
+
log(`repos folders saved to your engine profile: ${dirs.join(" → ")}`);
|
|
19246
|
+
return true;
|
|
19247
|
+
}
|
|
18976
19248
|
function engineInstall2(o) {
|
|
18977
19249
|
const log = o.log ?? console.log;
|
|
18978
19250
|
const error = o.error ?? console.error;
|
|
@@ -19147,13 +19419,13 @@ function ago(at, now) {
|
|
|
19147
19419
|
|
|
19148
19420
|
// src/commands/engineList.ts
|
|
19149
19421
|
var ENGINE_LIST_USAGE = "usage: sleds engine list [--json]";
|
|
19150
|
-
var
|
|
19422
|
+
var str12 = (x) => typeof x === "string" ? x : "";
|
|
19151
19423
|
var numOrNull = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
|
|
19152
19424
|
function machineOf(x) {
|
|
19153
19425
|
if (!x || typeof x !== "object")
|
|
19154
19426
|
return null;
|
|
19155
19427
|
const m = x;
|
|
19156
|
-
const out = { pressure:
|
|
19428
|
+
const out = { pressure: str12(m.pressure) || "unknown" };
|
|
19157
19429
|
for (const k of ["cpuPct", "load1", "cores", "loadPerCore"]) {
|
|
19158
19430
|
const n = numOrNull(m[k]);
|
|
19159
19431
|
if (n !== null)
|
|
@@ -19169,8 +19441,8 @@ function workOf(x) {
|
|
|
19169
19441
|
if (!w || typeof w !== "object")
|
|
19170
19442
|
return [];
|
|
19171
19443
|
const o = w;
|
|
19172
|
-
const item =
|
|
19173
|
-
return item ? [{ item, node:
|
|
19444
|
+
const item = str12(o.item);
|
|
19445
|
+
return item ? [{ item, node: str12(o.node), title: typeof o.title === "string" ? o.title : null }] : [];
|
|
19174
19446
|
});
|
|
19175
19447
|
}
|
|
19176
19448
|
function engineRows(raw) {
|
|
@@ -19179,16 +19451,16 @@ function engineRows(raw) {
|
|
|
19179
19451
|
if (!r || typeof r !== "object")
|
|
19180
19452
|
return [];
|
|
19181
19453
|
const e = r;
|
|
19182
|
-
const name =
|
|
19454
|
+
const name = str12(e.name);
|
|
19183
19455
|
if (!name)
|
|
19184
19456
|
return [];
|
|
19185
19457
|
return [
|
|
19186
19458
|
{
|
|
19187
19459
|
name,
|
|
19188
|
-
email:
|
|
19189
|
-
os:
|
|
19190
|
-
runner:
|
|
19191
|
-
version:
|
|
19460
|
+
email: str12(e.email),
|
|
19461
|
+
os: str12(e.os),
|
|
19462
|
+
runner: str12(e.runner),
|
|
19463
|
+
version: str12(e.version),
|
|
19192
19464
|
running: typeof e.running === "number" ? e.running : 0,
|
|
19193
19465
|
items: Array.isArray(e.items) ? e.items.filter((i) => typeof i === "string") : [],
|
|
19194
19466
|
seatKind: typeof e.seatKind === "string" ? e.seatKind : null,
|
|
@@ -19241,7 +19513,7 @@ var engineListCmd = async (o) => {
|
|
|
19241
19513
|
return 1;
|
|
19242
19514
|
}
|
|
19243
19515
|
if (r.status !== 200 || !r.json || r.json.ok === false) {
|
|
19244
|
-
error(
|
|
19516
|
+
error(str12(r.json?.error) || `HTTP ${r.status}`);
|
|
19245
19517
|
return 1;
|
|
19246
19518
|
}
|
|
19247
19519
|
const rows = engineRows(r.json);
|
|
@@ -19258,7 +19530,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
|
|
|
19258
19530
|
import { hostname as hostname6 } from "node:os";
|
|
19259
19531
|
|
|
19260
19532
|
// src/commands/engineStatus.ts
|
|
19261
|
-
import { existsSync as existsSync18, readFileSync as
|
|
19533
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20 } from "node:fs";
|
|
19262
19534
|
init_state();
|
|
19263
19535
|
|
|
19264
19536
|
// src/engine/status.ts
|
|
@@ -19548,7 +19820,7 @@ var engineLogCmd = (o) => {
|
|
|
19548
19820
|
|
|
19549
19821
|
// src/commands/engineProfile.ts
|
|
19550
19822
|
init_src();
|
|
19551
|
-
import { existsSync as existsSync19, readFileSync as
|
|
19823
|
+
import { existsSync as existsSync19, readFileSync as readFileSync21 } from "node:fs";
|
|
19552
19824
|
import { hostname as hostname7 } from "node:os";
|
|
19553
19825
|
var PROFILE_USAGE = "usage: sleds engine profile [--json] | sleds engine profile set <field>=<value>… [--workspace] [--json] | sleds engine profile set --stdin [--workspace] [--json] | sleds engine profile --strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp [--workspace] [--json] | sleds engine profile --sandbox [--sandbox-allow <a,b>] | --no-sandbox [--workspace] [--json] | sleds engine profile set --hold on|off [--workspace] [--json] | sleds engine profile set cmuxPassword=<pw> | --cmux-password <pw> [--json]";
|
|
19554
19826
|
var PROFILE_FIELDS = [
|
|
@@ -19813,7 +20085,7 @@ async function engineProfileCmd(o) {
|
|
|
19813
20085
|
const rest = words.slice(1);
|
|
19814
20086
|
let stdin = null;
|
|
19815
20087
|
if (fromStdin) {
|
|
19816
|
-
const taken = cmuxPasswordFromJson((o.stdin ?? (() =>
|
|
20088
|
+
const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync21(0, "utf8")))());
|
|
19817
20089
|
if ("error" in taken || taken.password !== undefined && password !== undefined) {
|
|
19818
20090
|
error("error" in taken ? taken.error : "the cmux password is given once");
|
|
19819
20091
|
error(PROFILE_USAGE);
|
|
@@ -19898,7 +20170,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
|
|
|
19898
20170
|
|
|
19899
20171
|
// src/commands/engineStatus.ts
|
|
19900
20172
|
init_state();
|
|
19901
|
-
import { existsSync as existsSync20, readFileSync as
|
|
20173
|
+
import { existsSync as existsSync20, readFileSync as readFileSync22 } from "node:fs";
|
|
19902
20174
|
import { hostname as hostname8 } from "node:os";
|
|
19903
20175
|
var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
|
|
19904
20176
|
function parseStatusArgs(argv, host) {
|
|
@@ -19954,7 +20226,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
|
|
|
19954
20226
|
}
|
|
19955
20227
|
function safeRead(path) {
|
|
19956
20228
|
try {
|
|
19957
|
-
return
|
|
20229
|
+
return readFileSync22(path, "utf8");
|
|
19958
20230
|
} catch {
|
|
19959
20231
|
return "";
|
|
19960
20232
|
}
|
|
@@ -20075,28 +20347,28 @@ init_meter();
|
|
|
20075
20347
|
|
|
20076
20348
|
// src/owed.ts
|
|
20077
20349
|
init_config();
|
|
20078
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as
|
|
20350
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as readFileSync23, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
|
|
20079
20351
|
import { dirname as dirname14, join as join22 } from "node:path";
|
|
20080
20352
|
var owedPath = (home) => join22(configDir(home), "owed.json");
|
|
20081
20353
|
var sessionPath = (home) => join22(configDir(home), "session.json");
|
|
20082
20354
|
var OWED_TTL_MS = 3 * 24 * 60 * 60000;
|
|
20083
|
-
var
|
|
20355
|
+
var str13 = (v) => typeof v === "string" ? v : undefined;
|
|
20084
20356
|
function sourceKey(s) {
|
|
20085
20357
|
const trim = (x) => x.trim().slice(0, 120);
|
|
20086
|
-
const kind =
|
|
20087
|
-
const project = trim(
|
|
20088
|
-
const doc =
|
|
20089
|
-
const item =
|
|
20358
|
+
const kind = str13(s.kind) ?? "manual";
|
|
20359
|
+
const project = trim(str13(s.project) ?? "");
|
|
20360
|
+
const doc = str13(s.doc);
|
|
20361
|
+
const item = str13(s.item);
|
|
20090
20362
|
return `${kind}:${project}${doc ? `/${trim(doc)}` : ""}${item ? `#${trim(item)}` : ""}`;
|
|
20091
20363
|
}
|
|
20092
20364
|
function sourceText2(s) {
|
|
20093
|
-
return [`${
|
|
20365
|
+
return [`${str13(s.kind) ?? "?"} ${str13(s.project) ?? "?"}`, str13(s.doc), str13(s.item)].filter(Boolean).join(" › ");
|
|
20094
20366
|
}
|
|
20095
20367
|
var readJson = (p) => {
|
|
20096
20368
|
if (!existsSync21(p))
|
|
20097
20369
|
return null;
|
|
20098
20370
|
try {
|
|
20099
|
-
return JSON.parse(
|
|
20371
|
+
return JSON.parse(readFileSync23(p, "utf8"));
|
|
20100
20372
|
} catch {
|
|
20101
20373
|
return null;
|
|
20102
20374
|
}
|
|
@@ -21123,7 +21395,7 @@ var login = async (o) => {
|
|
|
21123
21395
|
// src/commands/logout.ts
|
|
21124
21396
|
init_config();
|
|
21125
21397
|
init_state();
|
|
21126
|
-
import { existsSync as existsSync22, readFileSync as
|
|
21398
|
+
import { existsSync as existsSync22, readFileSync as readFileSync24, rmSync as rmSync11 } from "node:fs";
|
|
21127
21399
|
import { hostname as hostname10 } from "node:os";
|
|
21128
21400
|
init_settings();
|
|
21129
21401
|
var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
|
|
@@ -21280,7 +21552,7 @@ async function logout(o) {
|
|
|
21280
21552
|
};
|
|
21281
21553
|
writeConfig(o.home, next);
|
|
21282
21554
|
const tp = tokenPath(o.home, env);
|
|
21283
|
-
if (existsSync22(tp) &&
|
|
21555
|
+
if (existsSync22(tp) && readFileSync24(tp, "utf8").trim() === seat.token)
|
|
21284
21556
|
rmSync11(tp, { force: true });
|
|
21285
21557
|
const active = next.activeOrg ?? next.org?.slug ?? null;
|
|
21286
21558
|
const result = { ok: true, email, org: slug, paused, active };
|
|
@@ -21358,7 +21630,7 @@ var meter = async (o) => {
|
|
|
21358
21630
|
init_config();
|
|
21359
21631
|
init_meter();
|
|
21360
21632
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
21361
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as
|
|
21633
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync25, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
|
|
21362
21634
|
import { hostname as hostname11 } from "node:os";
|
|
21363
21635
|
import { dirname as dirname15 } from "node:path";
|
|
21364
21636
|
var defaultWhich3 = (cmd) => {
|
|
@@ -21835,7 +22107,7 @@ var packCmd = async (o) => {
|
|
|
21835
22107
|
};
|
|
21836
22108
|
|
|
21837
22109
|
// src/commands/permissionEvent.ts
|
|
21838
|
-
import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as
|
|
22110
|
+
import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync26, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
|
|
21839
22111
|
import { dirname as dirname16 } from "node:path";
|
|
21840
22112
|
var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
21841
22113
|
var messageOf = (text) => {
|
|
@@ -21846,12 +22118,20 @@ var messageOf = (text) => {
|
|
|
21846
22118
|
return "";
|
|
21847
22119
|
}
|
|
21848
22120
|
};
|
|
22121
|
+
var NOT_A_TOOL2 = new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
|
|
22122
|
+
var isToolPermission2 = (message) => {
|
|
22123
|
+
const tool = /permission to use\s+(.+?)\.?\s*$/i.exec(message)?.[1];
|
|
22124
|
+
return !tool || !NOT_A_TOOL2.has(tool.replace(/[\s_-]/g, "").toLowerCase());
|
|
22125
|
+
};
|
|
21849
22126
|
var permissionEvent = async (o) => {
|
|
21850
22127
|
try {
|
|
21851
22128
|
const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
|
|
21852
22129
|
if (!o.mark || notificationType(input) !== "permission_prompt")
|
|
21853
22130
|
return 0;
|
|
21854
|
-
const
|
|
22131
|
+
const message = messageOf(input);
|
|
22132
|
+
if (!isToolPermission2(message))
|
|
22133
|
+
return 0;
|
|
22134
|
+
const mark = { message, at: (o.now ?? Date.now)() };
|
|
21855
22135
|
mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
|
|
21856
22136
|
const tmp = `${o.mark}.${process.pid}.tmp`;
|
|
21857
22137
|
writeFileSync24(tmp, `${JSON.stringify(mark)}
|
|
@@ -21946,7 +22226,7 @@ var promptEvent = async (o) => {
|
|
|
21946
22226
|
|
|
21947
22227
|
// src/commands/route.ts
|
|
21948
22228
|
init_src();
|
|
21949
|
-
import { readFileSync as
|
|
22229
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
21950
22230
|
import { hostname as hostname12 } from "node:os";
|
|
21951
22231
|
var ARM_USAGE = "usage: sleds route arm <key> [--off] [--json] [--name <engine>] [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--mode <permission-mode>] [--model <id>] [--effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--repos-dir <dir>]";
|
|
21952
22232
|
var USAGE = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | --ready | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
|
|
@@ -22119,7 +22399,7 @@ async function answerCmd(o, argv) {
|
|
|
22119
22399
|
const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
|
|
22120
22400
|
const ref = words[0] ?? "";
|
|
22121
22401
|
const slash = ref.lastIndexOf("/");
|
|
22122
|
-
const answer = (fromStdin ? (o.stdin ?? (() =>
|
|
22402
|
+
const answer = (fromStdin ? (o.stdin ?? (() => readFileSync27(0, "utf8")))() : words.slice(1).join(" ")).trim();
|
|
22123
22403
|
if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
|
|
22124
22404
|
error(ANSWER_USAGE);
|
|
22125
22405
|
return 2;
|
|
@@ -22316,7 +22596,7 @@ async function decisionCmd(o, argv) {
|
|
|
22316
22596
|
error(DECISION_USAGE);
|
|
22317
22597
|
return 2;
|
|
22318
22598
|
}
|
|
22319
|
-
const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() =>
|
|
22599
|
+
const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync27(0, "utf8")))() : rest.join(" ")).trim();
|
|
22320
22600
|
if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
|
|
22321
22601
|
error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
|
|
22322
22602
|
return 2;
|
|
@@ -22457,7 +22737,7 @@ function approvedLine(key, j) {
|
|
|
22457
22737
|
function readNodes(o, a) {
|
|
22458
22738
|
let text;
|
|
22459
22739
|
try {
|
|
22460
|
-
text = a.file ? (o.readFile ?? ((p) =>
|
|
22740
|
+
text = a.file ? (o.readFile ?? ((p) => readFileSync27(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync27(0, "utf8")))();
|
|
22461
22741
|
} catch (e) {
|
|
22462
22742
|
return { error: e instanceof Error ? e.message : String(e) };
|
|
22463
22743
|
}
|
|
@@ -22768,7 +23048,7 @@ var seatEvent = async (o) => {
|
|
|
22768
23048
|
|
|
22769
23049
|
// src/commands/seatSync.ts
|
|
22770
23050
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
22771
|
-
import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as
|
|
23051
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
|
|
22772
23052
|
import { platform as platform2 } from "node:os";
|
|
22773
23053
|
import { dirname as dirname17, join as join25 } from "node:path";
|
|
22774
23054
|
|
|
@@ -23118,7 +23398,7 @@ function findCookieDb(home, source) {
|
|
|
23118
23398
|
return db;
|
|
23119
23399
|
}
|
|
23120
23400
|
const localStatePath = join25(root, "Local State");
|
|
23121
|
-
const localState = existsSync25(localStatePath) ?
|
|
23401
|
+
const localState = existsSync25(localStatePath) ? readFileSync28(localStatePath, "utf8") : null;
|
|
23122
23402
|
const wanted = source.profile ?? "Default";
|
|
23123
23403
|
const { dir, ambiguous } = resolveProfileDir(localState, wanted);
|
|
23124
23404
|
if (ambiguous)
|
|
@@ -23298,7 +23578,7 @@ var seatSync = async (o) => {
|
|
|
23298
23578
|
};
|
|
23299
23579
|
function readSeatToken(home, source) {
|
|
23300
23580
|
try {
|
|
23301
|
-
return
|
|
23581
|
+
return readFileSync28(seatTokenPath(home, source), "utf8").trim() || undefined;
|
|
23302
23582
|
} catch {
|
|
23303
23583
|
return;
|
|
23304
23584
|
}
|
|
@@ -23381,7 +23661,7 @@ var sessionEvent = async (o) => {
|
|
|
23381
23661
|
|
|
23382
23662
|
// src/commands/specEvent.ts
|
|
23383
23663
|
init_meter();
|
|
23384
|
-
import { readFileSync as
|
|
23664
|
+
import { readFileSync as readFileSync29, statSync as statSync2 } from "node:fs";
|
|
23385
23665
|
import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
|
|
23386
23666
|
var HOOK_STDIN_TIMEOUT_MS3 = 1000;
|
|
23387
23667
|
var ESTIMATE_TIMEOUT_MS = 4000;
|
|
@@ -23437,7 +23717,7 @@ function readFileOrNull(path) {
|
|
|
23437
23717
|
try {
|
|
23438
23718
|
if (statSync2(path).size > 2000000)
|
|
23439
23719
|
return null;
|
|
23440
|
-
return
|
|
23720
|
+
return readFileSync29(path, "utf8");
|
|
23441
23721
|
} catch {
|
|
23442
23722
|
return null;
|
|
23443
23723
|
}
|
|
@@ -23496,9 +23776,9 @@ var specEvent = async (o) => {
|
|
|
23496
23776
|
};
|
|
23497
23777
|
|
|
23498
23778
|
// src/commands/status.ts
|
|
23779
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
23499
23780
|
init_config();
|
|
23500
23781
|
init_settings();
|
|
23501
|
-
import { existsSync as existsSync26 } from "node:fs";
|
|
23502
23782
|
var HEALTH_TIMEOUT_MS2 = 4000;
|
|
23503
23783
|
var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS2) => {
|
|
23504
23784
|
try {
|
|
@@ -23528,6 +23808,13 @@ var fetchProxyStatus = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_M
|
|
|
23528
23808
|
}
|
|
23529
23809
|
};
|
|
23530
23810
|
var row2 = (label, value) => `${label.padEnd(12)}${value}`;
|
|
23811
|
+
var claudeLine = (c, seatEmail) => {
|
|
23812
|
+
if (!c)
|
|
23813
|
+
return "no Claude login found (signed out, or on an API key)";
|
|
23814
|
+
const org = c.orgName && (c.plan === "Team" || c.plan === "Enterprise") ? ` (${c.orgName})` : "";
|
|
23815
|
+
const other = seatEmail && seatEmail.toLowerCase() !== c.email.toLowerCase() ? " — not the seat's account" : "";
|
|
23816
|
+
return `${c.email}${c.plan ? ` · ${c.plan}` : ""}${org}${other}`;
|
|
23817
|
+
};
|
|
23531
23818
|
var formatStatus = (s) => {
|
|
23532
23819
|
const lines = [];
|
|
23533
23820
|
const stale = s.mode === "on" && !s.pointed;
|
|
@@ -23560,6 +23847,8 @@ var formatStatus = (s) => {
|
|
|
23560
23847
|
lines.push(row2("Seat:", `${s.email}${s.agentKind ? ` · ${s.agentKind}` : ""}`));
|
|
23561
23848
|
else if (s.agentKind)
|
|
23562
23849
|
lines.push(row2("Agent:", s.agentKind));
|
|
23850
|
+
if (s.claude !== undefined)
|
|
23851
|
+
lines.push(row2("Claude:", claudeLine(s.claude, s.email)));
|
|
23563
23852
|
if (s.org)
|
|
23564
23853
|
lines.push(row2("Org:", `${s.org.name}${s.org.slug ? ` (${s.org.slug})` : ""} · ${s.org.plan} · ${s.org.upstream === "none" ? "no platform key (walls: hold or hand off)" : s.org.upstream === "own" ? "platform key" : "shared key"}${s.orgFrom === "env" ? " · from SLEDS_ORG" : ""}`));
|
|
23565
23854
|
if (s.otherOrgs)
|
|
@@ -23605,6 +23894,8 @@ var status3 = async (o) => {
|
|
|
23605
23894
|
info.agentKind = cfg.agentKind;
|
|
23606
23895
|
if (cfg.wiring)
|
|
23607
23896
|
info.wiring = cfg.wiring;
|
|
23897
|
+
if (cfg.agentKind !== "codex")
|
|
23898
|
+
info.claude = readClaudeAccount(o.home, o.env ?? process.env);
|
|
23608
23899
|
if (org)
|
|
23609
23900
|
info.org = {
|
|
23610
23901
|
name: org.name,
|
|
@@ -23726,7 +24017,7 @@ var rememberSeat = (o, key, window, session, now, input) => {
|
|
|
23726
24017
|
const posts = [];
|
|
23727
24018
|
if (src && shouldPostSeat(prior, window, now)) {
|
|
23728
24019
|
state = markPosted(state, window, now);
|
|
23729
|
-
posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
|
|
24020
|
+
posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window, readClaudeAccount(o.home, o.env ?? process.env)));
|
|
23730
24021
|
}
|
|
23731
24022
|
const events = seatWallEvents(prior, window);
|
|
23732
24023
|
const credits = creditsEvent(o, prior, input, now);
|
|
@@ -23766,7 +24057,7 @@ var announce = (o, events) => {
|
|
|
23766
24057
|
|
|
23767
24058
|
// src/commands/stopEvent.ts
|
|
23768
24059
|
init_meter();
|
|
23769
|
-
import { existsSync as existsSync27, readFileSync as
|
|
24060
|
+
import { existsSync as existsSync27, readFileSync as readFileSync30 } from "node:fs";
|
|
23770
24061
|
|
|
23771
24062
|
// src/transcript.ts
|
|
23772
24063
|
var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -23862,7 +24153,7 @@ async function postTranscript(o, payload, now) {
|
|
|
23862
24153
|
const src = meterSource(o.home);
|
|
23863
24154
|
if (!src)
|
|
23864
24155
|
return false;
|
|
23865
|
-
const read = o.readFile ?? ((p) => existsSync27(p) ?
|
|
24156
|
+
const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync30(p, "utf8") : null);
|
|
23866
24157
|
let text;
|
|
23867
24158
|
try {
|
|
23868
24159
|
text = read(path);
|
|
@@ -24083,7 +24374,7 @@ var verify = async (o) => {
|
|
|
24083
24374
|
|
|
24084
24375
|
// src/commands/webhooks.ts
|
|
24085
24376
|
var USAGE2 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
|
|
24086
|
-
var
|
|
24377
|
+
var str14 = (x) => typeof x === "string" ? x : "";
|
|
24087
24378
|
function ago3(at, now) {
|
|
24088
24379
|
const m = Math.max(0, Math.floor((now - at) / 60000));
|
|
24089
24380
|
if (m < 1)
|
|
@@ -24099,7 +24390,7 @@ function webhookLines(raw, now) {
|
|
|
24099
24390
|
if (!rows.length)
|
|
24100
24391
|
return ["no webhooks — add one in the app: Settings › Integrations › Webhooks"];
|
|
24101
24392
|
return rows.map((h) => {
|
|
24102
|
-
let host =
|
|
24393
|
+
let host = str14(h.url);
|
|
24103
24394
|
try {
|
|
24104
24395
|
host = new URL(host.replace(/\{[a-z.]+\}/g, "x")).host;
|
|
24105
24396
|
} catch {}
|
|
@@ -24107,21 +24398,21 @@ function webhookLines(raw, now) {
|
|
|
24107
24398
|
const last = h.lastDelivery;
|
|
24108
24399
|
const lastText = last?.at ? `last ${last.status ?? "—"} ${last.state ?? ""} ${ago3(last.at, now)}`.replace(/\s+/g, " ") : "nothing sent yet";
|
|
24109
24400
|
const off = h.enabled ? "on " : "off";
|
|
24110
|
-
const why = !h.enabled && h.disabledWhy ? ` (${
|
|
24111
|
-
return `${off} ${
|
|
24401
|
+
const why = !h.enabled && h.disabledWhy ? ` (${str14(h.disabledWhy)})` : "";
|
|
24402
|
+
return `${off} ${str14(h.name)} ${host} ${events.includes("*") ? "all events" : events.join(",")} ${lastText}${why} [${str14(h.id)}]`;
|
|
24112
24403
|
});
|
|
24113
24404
|
}
|
|
24114
24405
|
function deliveryLines(raw, now) {
|
|
24115
24406
|
const j = clean(raw);
|
|
24116
24407
|
const rows = Array.isArray(j.deliveries) ? j.deliveries : [];
|
|
24117
|
-
const name =
|
|
24408
|
+
const name = str14(j.webhook?.name);
|
|
24118
24409
|
if (!rows.length)
|
|
24119
24410
|
return [`${name}: nothing sent yet`];
|
|
24120
24411
|
return [
|
|
24121
24412
|
`${name} — the last ${rows.length}`,
|
|
24122
24413
|
...rows.map((d) => {
|
|
24123
|
-
const result = d.status !== null && d.status !== undefined ? String(d.status) :
|
|
24124
|
-
return `${
|
|
24414
|
+
const result = d.status !== null && d.status !== undefined ? String(d.status) : str14(d.error) || "—";
|
|
24415
|
+
return `${str14(d.state).padEnd(9)} ${result.padEnd(5)} ${str14(d.event).padEnd(20)} ${str14(d.key) || "—"} ${ago3(Number(d.at), now)} tries ${d.attempts} ${str14(d.deliveryId)}`;
|
|
24125
24416
|
})
|
|
24126
24417
|
];
|
|
24127
24418
|
}
|
|
@@ -24152,7 +24443,7 @@ var webhooksCmd = async (o) => {
|
|
|
24152
24443
|
return 1;
|
|
24153
24444
|
}
|
|
24154
24445
|
if (r.status !== 200 || !r.json) {
|
|
24155
|
-
const why =
|
|
24446
|
+
const why = str14(r.json?.error) || `HTTP ${r.status}`;
|
|
24156
24447
|
error(r.status === 404 && !r.json ? "SLEDS does not know this seat — run `sleds init`" : why);
|
|
24157
24448
|
return 1;
|
|
24158
24449
|
}
|
|
@@ -24166,7 +24457,7 @@ var webhooksCmd = async (o) => {
|
|
|
24166
24457
|
|
|
24167
24458
|
// src/config.ts
|
|
24168
24459
|
init_settings();
|
|
24169
|
-
import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as
|
|
24460
|
+
import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
|
|
24170
24461
|
import { join as join26, resolve as resolve10 } from "node:path";
|
|
24171
24462
|
var configDir2 = (home, env = process.env) => {
|
|
24172
24463
|
const o = env.SLEDS_HOME?.trim();
|
|
@@ -24306,7 +24597,7 @@ var readConfig2 = (home) => {
|
|
|
24306
24597
|
const p = configPath2(home);
|
|
24307
24598
|
if (!existsSync29(p))
|
|
24308
24599
|
return {};
|
|
24309
|
-
return parseConfig2(JSON.parse(
|
|
24600
|
+
return parseConfig2(JSON.parse(readFileSync31(p, "utf8")));
|
|
24310
24601
|
};
|
|
24311
24602
|
|
|
24312
24603
|
// src/runners/exec.ts
|
|
@@ -24666,8 +24957,12 @@ var main = async (argv) => {
|
|
|
24666
24957
|
return engineLogCmd({ home, argv: argv.slice(2) });
|
|
24667
24958
|
if (cmd === "engine" && argv[1] === "list")
|
|
24668
24959
|
return engineListCmd({ home, argv: argv.slice(2) });
|
|
24669
|
-
if (cmd === "engine" && argv[1] === "install")
|
|
24960
|
+
if (cmd === "engine" && argv[1] === "install") {
|
|
24961
|
+
const ask = process.stdin.isTTY ? promptStdin : undefined;
|
|
24962
|
+
if (!await ensureReposDir({ home, argv: argv.slice(2), ...ask ? { ask } : {} }))
|
|
24963
|
+
return 1;
|
|
24670
24964
|
return engineInstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
|
|
24965
|
+
}
|
|
24671
24966
|
if (cmd === "engine" && argv[1] === "uninstall")
|
|
24672
24967
|
return engineUninstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
|
|
24673
24968
|
if (cmd === "engine" && argv[1] === "profile")
|
|
@@ -24853,7 +25148,7 @@ ${renderHelpText(argv[1])}`);
|
|
|
24853
25148
|
case "version":
|
|
24854
25149
|
case "--version":
|
|
24855
25150
|
case "-v":
|
|
24856
|
-
console.log("sleds 0.1.
|
|
25151
|
+
console.log("sleds 0.1.43");
|
|
24857
25152
|
return 0;
|
|
24858
25153
|
default:
|
|
24859
25154
|
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.43",
|
|
4
4
|
"description": "SLEDS CLI — 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",
|