@sleds/cli 0.1.44 → 0.1.46
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 +472 -59
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -802,6 +802,13 @@ var init_verbs = __esm(() => {
|
|
|
802
802
|
app: "Work › the item › click a field",
|
|
803
803
|
how: "The filer, an approver or an admin changes an item from chat or by clicking the field in the app; every change says who and through what (via Claude · Cowork, via ChatGPT, in the app) and the record keeps before and after. A change to the ask, done or smallest marks the band stale with a Re-size at hand — never re-sized silently. Worth is the person's word in their words ('high · Acme renewal', '$5k–20k'; null clears it); on an epic, `carry: true` puts it on each child that has none, each child's record signed the same way. A mirrored ticket is the tracker's word and is not changed here."
|
|
804
804
|
},
|
|
805
|
+
{
|
|
806
|
+
id: "reopen",
|
|
807
|
+
group: "meter",
|
|
808
|
+
want: "Put back an item that landed by mistake",
|
|
809
|
+
mcp: "reopen {project, reason}",
|
|
810
|
+
how: "SL-1819. For an item a skipped node or a wrong actual landed. It goes back on the sled it was on — or to ready on no sled, when that sled is gone from the org — the false actual is cleared and the band stands. A reopen always says why; the why goes on the item's record. The filer, an approver or an admin. Only a landed item reopens."
|
|
811
|
+
},
|
|
805
812
|
{
|
|
806
813
|
id: "archive",
|
|
807
814
|
group: "meter",
|
|
@@ -2079,7 +2086,7 @@ ${state}`)}, fillcolor="${fill}", color="${border}"${style}];`);
|
|
|
2079
2086
|
`)}
|
|
2080
2087
|
`;
|
|
2081
2088
|
}
|
|
2082
|
-
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}`.";
|
|
2089
|
+
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. An item with children takes no nodes: its children are the work, and their order lives in their blockedBy. Then `route {q, approach, nodes: [{key, title, touches, repos, blockedBy, plan: {approach, provides, expects, doneChecks}}], confirm: true}`.";
|
|
2083
2090
|
var init_route = __esm(() => {
|
|
2084
2091
|
CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
2085
2092
|
AREA_ROOTS = new Set(["apps", "packages", "services", "libs", "crates", "modules"]);
|
|
@@ -2151,6 +2158,7 @@ var init_annotations = __esm(() => {
|
|
|
2151
2158
|
finish: add("Land the work"),
|
|
2152
2159
|
buddy: add("Pair with a buddy"),
|
|
2153
2160
|
pickup: add("Pick up a hand-off"),
|
|
2161
|
+
reopen: add("Reopen an item", true),
|
|
2154
2162
|
update: change("Change an item", true),
|
|
2155
2163
|
archive: change("Archive an item", true),
|
|
2156
2164
|
withdraw: change("Withdraw a hand-off", true)
|
|
@@ -3223,6 +3231,27 @@ async function updateTool(ctx, args) {
|
|
|
3223
3231
|
text: `${key} updated: ${changed.join(", ")}.${carried}${stale ? " The band was sized before this change — say so, and offer to re-size (estimate with the same source)." : ""}`
|
|
3224
3232
|
};
|
|
3225
3233
|
}
|
|
3234
|
+
async function reopenTool(ctx, args) {
|
|
3235
|
+
if (!ctx.client)
|
|
3236
|
+
return NOT_SET_UP;
|
|
3237
|
+
const project = str5(args.project)?.trim();
|
|
3238
|
+
if (!project)
|
|
3239
|
+
return { text: "Which item? Pass project: its key (QW-142).", isError: true };
|
|
3240
|
+
const reason = str5(args.reason)?.trim();
|
|
3241
|
+
if (!reason)
|
|
3242
|
+
return {
|
|
3243
|
+
text: "Say why it is reopened — pass reason, one line. It goes on the item's record.",
|
|
3244
|
+
isError: true
|
|
3245
|
+
};
|
|
3246
|
+
const res = await postSaying(ctx, "/proxy/reopen", { project, reason }, "Could not reopen the item.");
|
|
3247
|
+
if ("fail" in res)
|
|
3248
|
+
return res.fail;
|
|
3249
|
+
const key = str5(res.json.key) ?? project;
|
|
3250
|
+
const sled = str5(res.json.sled);
|
|
3251
|
+
return {
|
|
3252
|
+
text: `Reopened ${key} — ${sled ? `back on ${sled}'s sled` : "back to ready, on no sled"}. The false actual is cleared; the band stands.`
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3226
3255
|
function carriedLine(c) {
|
|
3227
3256
|
if (!isObject4(c))
|
|
3228
3257
|
return "";
|
|
@@ -3531,9 +3560,21 @@ async function itemTool(ctx, args) {
|
|
|
3531
3560
|
text: `Nothing on the sled answers to "${q}". Try the key (QW-142) or a word from the title with status.`,
|
|
3532
3561
|
isError: true
|
|
3533
3562
|
};
|
|
3534
|
-
const md = str5(r.markdown) ?? "";
|
|
3535
3563
|
if (asFile)
|
|
3536
|
-
return { text:
|
|
3564
|
+
return { text: str5(r.markdown) ?? "", structured: { key: str5(r.key) ?? q, export: true } };
|
|
3565
|
+
const envs = isObject4(r.whereLive) && Array.isArray(r.whereLive.envs) ? r.whereLive.envs : [];
|
|
3566
|
+
const live = envs.filter(isObject4).filter((e) => {
|
|
3567
|
+
const s = isObject4(e.state) ? e.state : null;
|
|
3568
|
+
return !(s && (s.kind === "no_code" || s.state === "no_code"));
|
|
3569
|
+
}).map((e) => `- ${str5(e.name) ?? str5(e.slug) ?? "?"}: ${str5(e.words) ?? ""}`);
|
|
3570
|
+
const body = str5(r.markdown) ?? "";
|
|
3571
|
+
const section = live.length ? `## Where it's live
|
|
3572
|
+
${live.join(`
|
|
3573
|
+
`)}
|
|
3574
|
+
|
|
3575
|
+
` : "";
|
|
3576
|
+
const title = body.match(/^# [^\n]*\n+/)?.[0] ?? "";
|
|
3577
|
+
const md = `${title}${section}${body.slice(title.length)}`;
|
|
3537
3578
|
const readiness = isObject4(r.readiness) ? r.readiness : null;
|
|
3538
3579
|
const line = readiness ? str5(readiness.line) : undefined;
|
|
3539
3580
|
const state = readiness ? str5(readiness.state) : undefined;
|
|
@@ -4274,6 +4315,8 @@ async function callTool(name, args, ctx) {
|
|
|
4274
4315
|
return attachTool(ctx, args);
|
|
4275
4316
|
case "update":
|
|
4276
4317
|
return updateTool(ctx, args);
|
|
4318
|
+
case "reopen":
|
|
4319
|
+
return reopenTool(ctx, args);
|
|
4277
4320
|
case "archive":
|
|
4278
4321
|
return archiveTool(ctx, args);
|
|
4279
4322
|
case "comment":
|
|
@@ -4679,7 +4722,7 @@ var init_server = __esm(() => {
|
|
|
4679
4722
|
},
|
|
4680
4723
|
{
|
|
4681
4724
|
name: "route",
|
|
4682
|
-
description: "The route — an item's internal order as PR-sized nodes with blockers (spec 26). With only `q`: the route as filed (each node's state, what it waits on, the ready set, where each run can be watched), or, when none is filed, the pack and the drafting rule so you draft one. Drafting: 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: an `approach` for the route and a `plan` on each node (its approach, what it provides to later nodes, what it expects from the nodes it waits on, its done-checks), so each node's session follows them instead of re-planning. Show the graph and the plans as text and ask for a yes; file nothing before it. Then call again with `nodes` and `confirm: true`. Re-drafting shows what changed; a node with a run or a PR is never removed. Ready is computed from landed blockers, never set. With nobody to ask (a drafting session an engine started, spec 26 §5.1): call with `nodes` and `propose: true` instead — the route is held on the item for a person's yes and nothing is filed; then stop. A read of an item with a proposal shows it as drafted, not filed.",
|
|
4725
|
+
description: "The route — an item's internal order as PR-sized nodes with blockers (spec 26). With only `q`: the route as filed (each node's state, what it waits on, the ready set, where each run can be watched), or, when none is filed, the pack and the drafting rule so you draft one. Drafting: 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: an `approach` for the route and a `plan` on each node (its approach, what it provides to later nodes, what it expects from the nodes it waits on, its done-checks), so each node's session follows them instead of re-planning. Show the graph and the plans as text and ask for a yes; file nothing before it. Then call again with `nodes` and `confirm: true`. An item with children is refused nodes — its children are the work: set their order with blockedBy on them. The item lands only when every child has landed. Re-drafting shows what changed; a node with a run or a PR is never removed. Ready is computed from landed blockers, never set. With nobody to ask (a drafting session an engine started, spec 26 §5.1): call with `nodes` and `propose: true` instead — the route is held on the item for a person's yes and nothing is filed; then stop. A read of an item with a proposal shows it as drafted, not filed.",
|
|
4683
4726
|
inputSchema: {
|
|
4684
4727
|
type: "object",
|
|
4685
4728
|
properties: {
|
|
@@ -4967,6 +5010,21 @@ var init_server = __esm(() => {
|
|
|
4967
5010
|
required: ["project"]
|
|
4968
5011
|
}
|
|
4969
5012
|
},
|
|
5013
|
+
{
|
|
5014
|
+
name: "reopen",
|
|
5015
|
+
description: "Put back an item that landed by mistake — a skipped node closed its route, or an actual was sent against the wrong item. It goes back on the sled it was on (or to ready, when that sled is gone), the false actual is cleared and the band stands. Pass the item's key and why: a reopen always says why, and the why goes on the item's record. The filer, an approver or an admin may. Only a landed item can be reopened.",
|
|
5016
|
+
inputSchema: {
|
|
5017
|
+
type: "object",
|
|
5018
|
+
properties: {
|
|
5019
|
+
project: { type: "string", description: "The item's key — QW-142 — or its project key." },
|
|
5020
|
+
reason: {
|
|
5021
|
+
type: "string",
|
|
5022
|
+
description: "Why it is reopened, one line — e.g. 'a skipped node landed it; six children are open'."
|
|
5023
|
+
}
|
|
5024
|
+
},
|
|
5025
|
+
required: ["project", "reason"]
|
|
5026
|
+
}
|
|
5027
|
+
},
|
|
4970
5028
|
{
|
|
4971
5029
|
name: "item",
|
|
4972
5030
|
description: "Read one item whole, as one markdown document — the pack: everything an engineer or an agent needs to finish the work from this document alone. Eight parts: the ask, where the code is, how to run it (the repo's setup profile), how to know it is done (the gate), how to ship it (the playbook), what the org expects, what is known already (the Field Book — notes from earlier runs on this item, its feature and what landed beside it, the questions, the thread), and the rules of the road. Carries its readiness and whether it is packable. Pass the item's key (QW-142) or its title. Call this before building from an item, and before `check`.",
|
|
@@ -6416,7 +6474,54 @@ var init_pack2 = __esm(() => {
|
|
|
6416
6474
|
});
|
|
6417
6475
|
|
|
6418
6476
|
// ../../packages/shared/src/deploys/deploys.ts
|
|
6419
|
-
|
|
6477
|
+
function parseDeployBody(json) {
|
|
6478
|
+
if (!json || typeof json !== "object" || Array.isArray(json))
|
|
6479
|
+
return bad("body must be a JSON object");
|
|
6480
|
+
const j = json;
|
|
6481
|
+
const repo = typeof j.repo === "string" ? j.repo.trim().toLowerCase() : "";
|
|
6482
|
+
if (!REPO_RE.test(repo))
|
|
6483
|
+
return bad("repo must be owner/name");
|
|
6484
|
+
const sha = typeof j.sha === "string" ? j.sha.trim().toLowerCase() : "";
|
|
6485
|
+
if (!SHA_RE.test(sha))
|
|
6486
|
+
return bad("sha must be 7–40 hex characters");
|
|
6487
|
+
const environment = typeof j.environment === "string" ? j.environment.trim() : "";
|
|
6488
|
+
if (!ENV_SLUG_RE.test(environment))
|
|
6489
|
+
return bad("environment must be a slug: lower-case letters, digits, - and _");
|
|
6490
|
+
const body = { repo, sha, environment };
|
|
6491
|
+
if (j.service !== undefined && j.service !== null) {
|
|
6492
|
+
const service = typeof j.service === "string" ? j.service.trim() : "";
|
|
6493
|
+
if (!SERVICE_RE.test(service))
|
|
6494
|
+
return bad("service must be a short name");
|
|
6495
|
+
body.service = service;
|
|
6496
|
+
}
|
|
6497
|
+
if (j.at !== undefined && j.at !== null) {
|
|
6498
|
+
const at = typeof j.at === "string" ? Date.parse(j.at) : typeof j.at === "number" ? j.at : Number.NaN;
|
|
6499
|
+
if (!Number.isFinite(at))
|
|
6500
|
+
return bad("at must be an ISO time");
|
|
6501
|
+
body.at = at;
|
|
6502
|
+
}
|
|
6503
|
+
if (j.url !== undefined && j.url !== null) {
|
|
6504
|
+
let ok = false;
|
|
6505
|
+
if (typeof j.url === "string") {
|
|
6506
|
+
try {
|
|
6507
|
+
ok = new URL(j.url).protocol === "https:";
|
|
6508
|
+
} catch {
|
|
6509
|
+
ok = false;
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6512
|
+
if (!ok)
|
|
6513
|
+
return bad("url must be an https link");
|
|
6514
|
+
body.url = j.url;
|
|
6515
|
+
}
|
|
6516
|
+
return { ok: true, body };
|
|
6517
|
+
}
|
|
6518
|
+
var REPO_RE, SHA_RE, ENV_SLUG_RE, SERVICE_RE, bad = (error) => ({ ok: false, error });
|
|
6519
|
+
var init_deploys = __esm(() => {
|
|
6520
|
+
REPO_RE = /^[a-z0-9_.-]+\/[a-z0-9_.-]+$/;
|
|
6521
|
+
SHA_RE = /^[0-9a-f]{7,40}$/;
|
|
6522
|
+
ENV_SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,39}$/;
|
|
6523
|
+
SERVICE_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/;
|
|
6524
|
+
});
|
|
6420
6525
|
|
|
6421
6526
|
// ../../packages/shared/src/newrelic/newrelic.ts
|
|
6422
6527
|
var init_newrelic = () => {};
|
|
@@ -6817,7 +6922,7 @@ function nodePrompt(o) {
|
|
|
6817
6922
|
...claim.pack.includes(PR_HEADER_OPEN) ? [
|
|
6818
6923
|
`- 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.`
|
|
6819
6924
|
] : [],
|
|
6820
|
-
"- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
|
|
6925
|
+
o.gate ? `- Run the repo's gate before you push: exactly \`${o.gate}\`, as given. Never wrap it — no \`$?\`, \`$(...)\`, \`bash -c\` or \`; echo\` around it — or it stops at a permission dialog; read its result from the exit and the output. Never merge: a person (or the musher, when the attachment says so) does.` : "- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
|
|
6821
6926
|
"- Stay inside this node's scope. Work another node needs goes in the PR description, not in this branch.",
|
|
6822
6927
|
`- End every comment and reply you post on the PR with the line \`${ENGINE_MARK}\`, so the engine knows it is yours and not a reviewer's.`,
|
|
6823
6928
|
"- The approved brief and this route are the go-ahead: a person said yes to them before this session started. Build, commit, push and open the PR; a park is for a question the record does not settle, not for a go-ahead to build or push.",
|
|
@@ -9693,9 +9798,127 @@ var defer = async (o) => {
|
|
|
9693
9798
|
return 0;
|
|
9694
9799
|
};
|
|
9695
9800
|
|
|
9801
|
+
// src/commands/deploy.ts
|
|
9802
|
+
init_src();
|
|
9803
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
9804
|
+
var DEPLOYS_URL = "https://hooks.sleds.ai/deploys";
|
|
9805
|
+
var USAGE = "usage: sleds deploy --env <slug> [--service <name>] [--sha <sha>] [--repo <owner/name>]";
|
|
9806
|
+
var FLAGS = ["env", "service", "sha", "repo"];
|
|
9807
|
+
var set = (x) => x?.trim() ? x.trim() : undefined;
|
|
9808
|
+
function repoFromRemote(remote) {
|
|
9809
|
+
const m = /^(?:[a-z+]+:\/\/)?(?:[^@/]+@)?[^/:]+[:/](.+?)(?:\.git)?\/?$/i.exec(remote.trim());
|
|
9810
|
+
const parts = m?.[1]?.split("/").filter(Boolean) ?? [];
|
|
9811
|
+
if (parts.length < 2)
|
|
9812
|
+
return null;
|
|
9813
|
+
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`.toLowerCase();
|
|
9814
|
+
}
|
|
9815
|
+
function resolveDeploy(argv, env, git) {
|
|
9816
|
+
const flags = {};
|
|
9817
|
+
for (let i = 0;i < argv.length; i++) {
|
|
9818
|
+
const a = argv[i] ?? "";
|
|
9819
|
+
const m = /^--([a-z]+)(?:=(.*))?$/.exec(a);
|
|
9820
|
+
const name = m?.[1];
|
|
9821
|
+
if (!m || !name || !FLAGS.includes(name))
|
|
9822
|
+
return { ok: false, error: `unknown argument ${a}` };
|
|
9823
|
+
const value = m[2] ?? argv[++i];
|
|
9824
|
+
if (!set(value))
|
|
9825
|
+
return { ok: false, error: `--${name} needs a value` };
|
|
9826
|
+
flags[name] = value?.trim();
|
|
9827
|
+
}
|
|
9828
|
+
if (!flags.env)
|
|
9829
|
+
return { ok: false, error: "--env <slug> is required" };
|
|
9830
|
+
const vercelRepo = set(env.VERCEL_GIT_REPO_OWNER) && set(env.VERCEL_GIT_REPO_SLUG) ? `${set(env.VERCEL_GIT_REPO_OWNER)}/${set(env.VERCEL_GIT_REPO_SLUG)}` : undefined;
|
|
9831
|
+
const remote = () => {
|
|
9832
|
+
const url = git(["remote", "get-url", "origin"]);
|
|
9833
|
+
return url && repoFromRemote(url) || undefined;
|
|
9834
|
+
};
|
|
9835
|
+
const repo = flags.repo ?? set(env.GITHUB_REPOSITORY) ?? vercelRepo ?? remote();
|
|
9836
|
+
if (!repo)
|
|
9837
|
+
return { ok: false, error: "no repo: pass --repo owner/name (no origin remote)" };
|
|
9838
|
+
const sha = flags.sha ?? set(env.SOURCE_VERSION) ?? set(env.HEROKU_SLUG_COMMIT) ?? set(env.VERCEL_GIT_COMMIT_SHA) ?? set(env.GITHUB_SHA) ?? set(git(["rev-parse", "HEAD"]) ?? undefined);
|
|
9839
|
+
if (!sha)
|
|
9840
|
+
return { ok: false, error: "no sha: pass --sha (not a git checkout)" };
|
|
9841
|
+
return parseDeployBody({
|
|
9842
|
+
repo,
|
|
9843
|
+
sha,
|
|
9844
|
+
environment: flags.env,
|
|
9845
|
+
...flags.service ? { service: flags.service } : {}
|
|
9846
|
+
});
|
|
9847
|
+
}
|
|
9848
|
+
var defaultGit = (args) => {
|
|
9849
|
+
try {
|
|
9850
|
+
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
9851
|
+
} catch {
|
|
9852
|
+
return null;
|
|
9853
|
+
}
|
|
9854
|
+
};
|
|
9855
|
+
var defaultPost = async (url, token, body) => {
|
|
9856
|
+
try {
|
|
9857
|
+
const res = await fetch(url, {
|
|
9858
|
+
method: "POST",
|
|
9859
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
9860
|
+
body: JSON.stringify(body),
|
|
9861
|
+
signal: AbortSignal.timeout(15000)
|
|
9862
|
+
});
|
|
9863
|
+
let json = null;
|
|
9864
|
+
try {
|
|
9865
|
+
json = await res.json();
|
|
9866
|
+
} catch {}
|
|
9867
|
+
return { status: res.status, json };
|
|
9868
|
+
} catch (e) {
|
|
9869
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
9870
|
+
}
|
|
9871
|
+
};
|
|
9872
|
+
function deployLine(body, json) {
|
|
9873
|
+
const where = `${body.repo}${body.service ? ` (${body.service})` : ""} → ${body.environment}`;
|
|
9874
|
+
if (typeof json.ignored === "string")
|
|
9875
|
+
return `ignored: ${json.ignored}`;
|
|
9876
|
+
if (json.duplicate)
|
|
9877
|
+
return `${where}: already at ${body.sha.slice(0, 7)}`;
|
|
9878
|
+
const prs = Array.isArray(json.newlyDeployed) ? json.newlyDeployed : [];
|
|
9879
|
+
const list = prs.map((p) => `#${p.number}`).join(", ");
|
|
9880
|
+
if (json.rollback)
|
|
9881
|
+
return `${where}: rollback to ${body.sha.slice(0, 7)}${list ? ` (carries ${list} again)` : ""}`;
|
|
9882
|
+
return prs.length ? `${where}: newly deployed ${list}` : `${where}: ${body.sha.slice(0, 7)} recorded, no newly deployed PRs`;
|
|
9883
|
+
}
|
|
9884
|
+
var deployCmd = async (o) => {
|
|
9885
|
+
const log = o.log ?? console.log;
|
|
9886
|
+
const error = o.error ?? console.error;
|
|
9887
|
+
const env = o.env ?? process.env;
|
|
9888
|
+
const token = set(env.SLEDS_DEPLOY_TOKEN);
|
|
9889
|
+
if (!token) {
|
|
9890
|
+
error("SLEDS_DEPLOY_TOKEN is not set — mint one in Settings › Deploys");
|
|
9891
|
+
return 2;
|
|
9892
|
+
}
|
|
9893
|
+
const resolved = resolveDeploy(o.argv, env, o.git ?? defaultGit);
|
|
9894
|
+
if (!resolved.ok) {
|
|
9895
|
+
error(resolved.error);
|
|
9896
|
+
error(USAGE);
|
|
9897
|
+
return 2;
|
|
9898
|
+
}
|
|
9899
|
+
const body = resolved.body;
|
|
9900
|
+
const url = set(env.SLEDS_DEPLOYS_URL) ?? DEPLOYS_URL;
|
|
9901
|
+
const r = await (o.post ?? defaultPost)(url, token, body);
|
|
9902
|
+
if ("error" in r) {
|
|
9903
|
+
error(`warning: SLEDS unreachable, deploy not recorded — ${r.error}`);
|
|
9904
|
+
return 0;
|
|
9905
|
+
}
|
|
9906
|
+
if (r.status === 200 && r.json) {
|
|
9907
|
+
log(deployLine(body, r.json));
|
|
9908
|
+
return 0;
|
|
9909
|
+
}
|
|
9910
|
+
const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
|
|
9911
|
+
if (r.status >= 400 && r.status < 500) {
|
|
9912
|
+
error(r.status === 401 ? `SLEDS refused the deploy token (${why})` : why);
|
|
9913
|
+
return 1;
|
|
9914
|
+
}
|
|
9915
|
+
error(`warning: SLEDS answered ${why}, deploy not recorded`);
|
|
9916
|
+
return 0;
|
|
9917
|
+
};
|
|
9918
|
+
|
|
9696
9919
|
// src/commands/doctor.ts
|
|
9697
9920
|
init_src();
|
|
9698
|
-
import { execFileSync as
|
|
9921
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync5 } from "node:child_process";
|
|
9699
9922
|
import { randomBytes } from "node:crypto";
|
|
9700
9923
|
import {
|
|
9701
9924
|
existsSync as existsSync17,
|
|
@@ -9710,7 +9933,7 @@ import { tmpdir } from "node:os";
|
|
|
9710
9933
|
import { join as join20, resolve as resolve9 } from "node:path";
|
|
9711
9934
|
|
|
9712
9935
|
// src/agentMcp.ts
|
|
9713
|
-
import { execFileSync as
|
|
9936
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
9714
9937
|
import { existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "node:fs";
|
|
9715
9938
|
import { dirname as dirname8, join as join10, resolve as resolve6 } from "node:path";
|
|
9716
9939
|
init_settings();
|
|
@@ -9778,7 +10001,7 @@ function applyCodexMcp2(toml, home, env = process.env) {
|
|
|
9778
10001
|
`;
|
|
9779
10002
|
}
|
|
9780
10003
|
var defaultRunClaude2 = (args) => {
|
|
9781
|
-
|
|
10004
|
+
execFileSync3("claude", args, { stdio: ["ignore", "ignore", "pipe"], timeout: 20000 });
|
|
9782
10005
|
};
|
|
9783
10006
|
async function offerMcp2(o) {
|
|
9784
10007
|
const env = o.env ?? process.env;
|
|
@@ -10233,6 +10456,7 @@ var agentShellCommand = (o) => {
|
|
|
10233
10456
|
const effort = o.effort ? ` --effort ${shq2(o.effort)}` : "";
|
|
10234
10457
|
return `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)}${hookSettingsFlag(o)}${mcpConfigFlag(o)} --permission-mode ${shq2(o.permissionMode)}${model}${effort} ${prompt}`;
|
|
10235
10458
|
};
|
|
10459
|
+
var execLine = (cmd) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(cmd) ? `exec env ${cmd}` : `exec ${cmd}`;
|
|
10236
10460
|
var sandboxedAgent = (agent, sandbox, mainCheckout) => {
|
|
10237
10461
|
const kind = sandboxAgentKind(agent);
|
|
10238
10462
|
const line = kind === "claude" ? `${agent}${claudeAddDirFlag(mainCheckout)}` : agent;
|
|
@@ -10705,7 +10929,7 @@ var parseOrcaTerminal = (env) => {
|
|
|
10705
10929
|
};
|
|
10706
10930
|
var orcaLauncher = (o) => `#!/bin/sh
|
|
10707
10931
|
# sleds engine: ${o.name}
|
|
10708
|
-
|
|
10932
|
+
${execLine(agentShellCommand(o))}
|
|
10709
10933
|
`;
|
|
10710
10934
|
var orcaCommand = (launcher) => `sh ${shq2(launcher)}; exit`;
|
|
10711
10935
|
var GONE = /exit|clos|gone|dead|stopp|kill/i;
|
|
@@ -10907,7 +11131,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10907
11131
|
name: flags.name,
|
|
10908
11132
|
os: host.os ?? process.platform,
|
|
10909
11133
|
runner: flags.runner,
|
|
10910
|
-
version: host.version ?? "0.1.
|
|
11134
|
+
version: host.version ?? "0.1.46" ?? "dev",
|
|
10911
11135
|
running: live.length,
|
|
10912
11136
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
10913
11137
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -11900,7 +12124,7 @@ function keysIn(states, state) {
|
|
|
11900
12124
|
}
|
|
11901
12125
|
function statesLanded(states) {
|
|
11902
12126
|
const all = Object.values(states);
|
|
11903
|
-
return all.length > 0 && all.every((s) => s === "landed" || s === "skipped");
|
|
12127
|
+
return all.length > 0 && all.every((s) => s === "landed" || s === "skipped") && all.some((s) => s === "landed");
|
|
11904
12128
|
}
|
|
11905
12129
|
function armedTerms(armed) {
|
|
11906
12130
|
if (!isObject8(armed))
|
|
@@ -12098,21 +12322,17 @@ async function readProfile(client) {
|
|
|
12098
12322
|
}
|
|
12099
12323
|
function nextPickup(r, o) {
|
|
12100
12324
|
const mine = Array.isArray(r.mine) ? r.mine.filter(isObject8) : [];
|
|
12101
|
-
let running = 0;
|
|
12102
12325
|
for (const row of mine) {
|
|
12103
12326
|
const route = row.route;
|
|
12104
|
-
if (isObject8(route)
|
|
12105
|
-
running++;
|
|
12106
|
-
if (!isObject8(route) || !isObject8(route.armed))
|
|
12327
|
+
if (!isObject8(route) || !armedFor(route.armed, o.engine))
|
|
12107
12328
|
continue;
|
|
12108
12329
|
const states = isObject8(route.states) ? route.states : {};
|
|
12109
|
-
|
|
12110
|
-
if (!armedFor(route.armed, o.engine) || statesLanded(states))
|
|
12330
|
+
if (statesLanded(states))
|
|
12111
12331
|
continue;
|
|
12112
12332
|
if (strings(route.ready).length > 0)
|
|
12113
12333
|
return null;
|
|
12114
12334
|
}
|
|
12115
|
-
if (
|
|
12335
|
+
if (sledRunning(mine) >= o.atOnce)
|
|
12116
12336
|
return null;
|
|
12117
12337
|
for (const row of mine) {
|
|
12118
12338
|
const route = row.route;
|
|
@@ -12128,6 +12348,33 @@ function nextPickup(r, o) {
|
|
|
12128
12348
|
}
|
|
12129
12349
|
return null;
|
|
12130
12350
|
}
|
|
12351
|
+
function sledRunning(r) {
|
|
12352
|
+
const mine = Array.isArray(r) ? r : Array.isArray(r.mine) ? r.mine.filter(isObject8) : [];
|
|
12353
|
+
let running = 0;
|
|
12354
|
+
for (const row of mine) {
|
|
12355
|
+
const route = row.route;
|
|
12356
|
+
if (isObject8(route) && route.drafting === true)
|
|
12357
|
+
running++;
|
|
12358
|
+
if (!isObject8(route) || !isObject8(route.armed))
|
|
12359
|
+
continue;
|
|
12360
|
+
running += keysIn(isObject8(route.states) ? route.states : {}, "running").length;
|
|
12361
|
+
}
|
|
12362
|
+
return running;
|
|
12363
|
+
}
|
|
12364
|
+
function armedDrafts(r, engine) {
|
|
12365
|
+
const mine = Array.isArray(r.mine) ? r.mine.filter(isObject8) : [];
|
|
12366
|
+
return mine.filter((row) => {
|
|
12367
|
+
const route = row.route;
|
|
12368
|
+
if (!isObject8(route) || !armedFor(route.armed, engine) || route.implicit !== true)
|
|
12369
|
+
return false;
|
|
12370
|
+
if (route.drafting === true || route.proposed === true || route.draftParked)
|
|
12371
|
+
return false;
|
|
12372
|
+
if (strings(route.needsRoute).length === 0 || isPrMirror(row.source))
|
|
12373
|
+
return false;
|
|
12374
|
+
const states = isObject8(route.states) ? route.states : {};
|
|
12375
|
+
return !statesLanded(states) && keysIn(states, "running").length === 0;
|
|
12376
|
+
});
|
|
12377
|
+
}
|
|
12131
12378
|
function redraftDue(route) {
|
|
12132
12379
|
return isObject8(route) && isObject8(route.redraft) && route.drafting !== true && route.proposed !== true;
|
|
12133
12380
|
}
|
|
@@ -12141,14 +12388,15 @@ function routeText(route, states) {
|
|
|
12141
12388
|
}
|
|
12142
12389
|
function sledsSource(client, o) {
|
|
12143
12390
|
const routeOf = (q) => client.get(`/proxy/route?q=${encodeURIComponent(q)}`);
|
|
12144
|
-
async function pickup(sled) {
|
|
12391
|
+
async function pickup(sled, drafted) {
|
|
12145
12392
|
const engine = o.engine;
|
|
12146
12393
|
const profile = await readProfile(client);
|
|
12147
12394
|
if (profile?.pickup !== "auto")
|
|
12148
12395
|
return null;
|
|
12149
12396
|
if (profile.engine && !sameEngine(profile.engine, engine))
|
|
12150
12397
|
return null;
|
|
12151
|
-
const
|
|
12398
|
+
const atOnce = (o.atOnce ?? profile.atOnce ?? 1) - drafted;
|
|
12399
|
+
const row = nextPickup(sled, { atOnce, engine });
|
|
12152
12400
|
if (!row)
|
|
12153
12401
|
return null;
|
|
12154
12402
|
const item = str6(row.key) ?? String(row.project);
|
|
@@ -12176,7 +12424,7 @@ function sledsSource(client, o) {
|
|
|
12176
12424
|
...armedTerms(armed)
|
|
12177
12425
|
};
|
|
12178
12426
|
}
|
|
12179
|
-
async function draft(row, item, why) {
|
|
12427
|
+
async function draft(row, item, why, started = false) {
|
|
12180
12428
|
const project = str6(row.project) ?? item;
|
|
12181
12429
|
const pending = isObject8(row.route) && isObject8(row.route.redraft) ? row.route.redraft : null;
|
|
12182
12430
|
const runner = typeof o.runner === "function" ? o.runner() : o.runner;
|
|
@@ -12188,7 +12436,7 @@ function sledsSource(client, o) {
|
|
|
12188
12436
|
...o.leaseMin !== undefined ? { leaseMin: o.leaseMin } : {}
|
|
12189
12437
|
});
|
|
12190
12438
|
if (!r || r.status >= 300 || !r.json || r.json.ok === false) {
|
|
12191
|
-
o.log?.(`${pending ? `${item}: could not claim the re-draft` : `Pickup: Auto could not claim ${item} for drafting`} — ${r ? serverMessage(r) : UNREACHABLE2}`);
|
|
12439
|
+
o.log?.(`${pending ? `${item}: could not claim the re-draft` : started ? `${item}: Start could not claim it for drafting` : `Pickup: Auto could not claim ${item} for drafting`} — ${r ? serverMessage(r) : UNREACHABLE2}`);
|
|
12192
12440
|
return null;
|
|
12193
12441
|
}
|
|
12194
12442
|
const j = r.json;
|
|
@@ -12210,7 +12458,7 @@ function sledsSource(client, o) {
|
|
|
12210
12458
|
route: routeText(brief, isObject8(row.route) ? row.route.states : null)
|
|
12211
12459
|
};
|
|
12212
12460
|
}
|
|
12213
|
-
o.log?.(redraft ? `${item}: re-drafting the route from ${redraft.by}'s answer — it waits for a yes` : `Pickup: Auto is drafting ${item}'s route first (${why.join(", ")}) — it waits for a yes`);
|
|
12461
|
+
o.log?.(redraft ? `${item}: re-drafting the route from ${redraft.by}'s answer — it waits for a yes` : `${started ? `${item}: Start` : "Pickup: Auto"} is drafting ${started ? "its" : `${item}'s`} route first (${why.join(", ")}) — it waits for a yes`);
|
|
12214
12462
|
const leaseUntil = num5(j.leaseUntil);
|
|
12215
12463
|
const claim = {
|
|
12216
12464
|
item,
|
|
@@ -12250,7 +12498,28 @@ function sledsSource(client, o) {
|
|
|
12250
12498
|
if (claimed?.draft && it)
|
|
12251
12499
|
it.draft = claimed.draft;
|
|
12252
12500
|
}
|
|
12253
|
-
const
|
|
12501
|
+
const waiting = armedDrafts(r, o.engine);
|
|
12502
|
+
let drafted = 0;
|
|
12503
|
+
if (waiting.length > 0) {
|
|
12504
|
+
const cap = o.atOnce ?? (await readProfile(client))?.atOnce ?? 1;
|
|
12505
|
+
let room = cap - sledRunning(r);
|
|
12506
|
+
for (const row of waiting) {
|
|
12507
|
+
const item = str6(row.key) ?? String(row.project);
|
|
12508
|
+
const it = items.find((x) => x.item === item);
|
|
12509
|
+
if (it)
|
|
12510
|
+
it.ready = [];
|
|
12511
|
+
if (room <= 0)
|
|
12512
|
+
continue;
|
|
12513
|
+
const route = isObject8(row.route) ? row.route : {};
|
|
12514
|
+
const claimed = await draft(row, item, strings(route.needsRoute), true);
|
|
12515
|
+
if (claimed?.draft && it) {
|
|
12516
|
+
it.draft = claimed.draft;
|
|
12517
|
+
drafted++;
|
|
12518
|
+
room--;
|
|
12519
|
+
}
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
12522
|
+
const picked = await pickup(r, drafted);
|
|
12254
12523
|
if (picked)
|
|
12255
12524
|
items.push(picked);
|
|
12256
12525
|
}
|
|
@@ -12865,6 +13134,37 @@ function packInstallOf(pack, repo) {
|
|
|
12865
13134
|
}
|
|
12866
13135
|
return first;
|
|
12867
13136
|
}
|
|
13137
|
+
function packGateOf(pack, repo) {
|
|
13138
|
+
let inDone = false;
|
|
13139
|
+
let first;
|
|
13140
|
+
const want = repo?.trim().toLowerCase();
|
|
13141
|
+
for (const line of pack.split(`
|
|
13142
|
+
`)) {
|
|
13143
|
+
if (/^##\s/.test(line)) {
|
|
13144
|
+
inDone = /^##\s+4\./.test(line);
|
|
13145
|
+
continue;
|
|
13146
|
+
}
|
|
13147
|
+
if (!inDone)
|
|
13148
|
+
continue;
|
|
13149
|
+
const m = line.match(/^\s*-\s*([^:`]+?):\s*`(.+)`\s*$/);
|
|
13150
|
+
if (!m?.[1] || !m[2])
|
|
13151
|
+
continue;
|
|
13152
|
+
const name = m[1].trim().toLowerCase();
|
|
13153
|
+
if (name === "done means" || name.startsWith("try it"))
|
|
13154
|
+
continue;
|
|
13155
|
+
if (want && name === want)
|
|
13156
|
+
return m[2];
|
|
13157
|
+
first ??= m[2];
|
|
13158
|
+
}
|
|
13159
|
+
return first;
|
|
13160
|
+
}
|
|
13161
|
+
function gateAllowRules(gate) {
|
|
13162
|
+
const whole = gate?.trim();
|
|
13163
|
+
if (!whole)
|
|
13164
|
+
return [];
|
|
13165
|
+
const parts = whole.split(/&&|\|\||;/).map((p) => p.trim()).filter(Boolean);
|
|
13166
|
+
return [...new Set([whole, ...parts])].map((c) => `Bash(${c})`);
|
|
13167
|
+
}
|
|
12868
13168
|
function ensureInstalled(exec, path, fs, o = {}) {
|
|
12869
13169
|
if (!fs.exists(join15(path, "package.json")) || fs.exists(join15(path, "node_modules")))
|
|
12870
13170
|
return null;
|
|
@@ -12957,17 +13257,17 @@ function fingerprint(text) {
|
|
|
12957
13257
|
}
|
|
12958
13258
|
return (h >>> 0).toString(16);
|
|
12959
13259
|
}
|
|
12960
|
-
function sandboxFor(d, name, cwd) {
|
|
13260
|
+
function sandboxFor(d, name, cwd, allow) {
|
|
12961
13261
|
if (!d.sandbox)
|
|
12962
13262
|
return {};
|
|
12963
13263
|
if (!d.writeSandboxSettings)
|
|
12964
13264
|
throw new Error("sessions are sandboxed, but nothing writes their settings — not started");
|
|
12965
|
-
return { sandbox: d.writeSandboxSettings(name, cwd) };
|
|
13265
|
+
return { sandbox: d.writeSandboxSettings(name, cwd, allow) };
|
|
12966
13266
|
}
|
|
12967
|
-
function hooksFor(d, name) {
|
|
13267
|
+
function hooksFor(d, name, allow) {
|
|
12968
13268
|
if (d.sandbox || !d.writeHookSettings)
|
|
12969
13269
|
return {};
|
|
12970
|
-
return { settingsFile: d.writeHookSettings(name) };
|
|
13270
|
+
return { settingsFile: d.writeHookSettings(name, allow) };
|
|
12971
13271
|
}
|
|
12972
13272
|
function mainCheckoutFor(d, cwd) {
|
|
12973
13273
|
const main = d.mainCheckoutOf?.(cwd);
|
|
@@ -13425,6 +13725,8 @@ async function tickOnce(d) {
|
|
|
13425
13725
|
const worktree = d.runner.makesWorktree ? repoPath : d.ensureWorktree(d.exec, repoPath, branch, worktreePath(repoPath, name), base, packInstallOf(claim.pack, claim.repos[0])) ?? worktreePath(repoPath, name);
|
|
13426
13726
|
const prTitle = d.prTitleOf?.(claim);
|
|
13427
13727
|
mcpConfig = engineMcpConfigFor(d, name, claim.runToken, at, worktree);
|
|
13728
|
+
const gate = packGateOf(claim.pack, claim.repos[0]);
|
|
13729
|
+
const allow = gateAllowRules(gate);
|
|
13428
13730
|
const prompt = nodePrompt({
|
|
13429
13731
|
claim,
|
|
13430
13732
|
branch,
|
|
@@ -13433,6 +13735,7 @@ async function tickOnce(d) {
|
|
|
13433
13735
|
sledsTools: d.source.name === "sleds",
|
|
13434
13736
|
engineMcp: Boolean(mcpConfig),
|
|
13435
13737
|
...prTitle ? { prTitle } : {},
|
|
13738
|
+
...gate ? { gate } : {},
|
|
13436
13739
|
...nodePlanContext(await briefsOf(claim.item), claim.node)
|
|
13437
13740
|
});
|
|
13438
13741
|
const promptFile = d.writePrompt(name, prompt);
|
|
@@ -13449,8 +13752,8 @@ async function tickOnce(d) {
|
|
|
13449
13752
|
...launch.effort && !launch.agent ? { effort: launch.effort } : {},
|
|
13450
13753
|
...launch.agent ? { agent: launch.agent } : {},
|
|
13451
13754
|
...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
|
|
13452
|
-
...sandboxFor(d, name, worktree),
|
|
13453
|
-
...hooksFor(d, name),
|
|
13755
|
+
...sandboxFor(d, name, worktree, allow),
|
|
13756
|
+
...hooksFor(d, name, allow),
|
|
13454
13757
|
...mainCheckoutFor(d, worktree),
|
|
13455
13758
|
...claim.group ? { group: claim.group } : {}
|
|
13456
13759
|
};
|
|
@@ -13708,6 +14011,24 @@ function pressAnswer(d, runner, run, options, answer) {
|
|
|
13708
14011
|
return false;
|
|
13709
14012
|
}
|
|
13710
14013
|
}
|
|
14014
|
+
function readFollowed(d, runner, run) {
|
|
14015
|
+
if (!runner.readsSession)
|
|
14016
|
+
return "";
|
|
14017
|
+
let shown = "";
|
|
14018
|
+
try {
|
|
14019
|
+
shown = runner.read(run.handle);
|
|
14020
|
+
} catch {
|
|
14021
|
+
shown = "";
|
|
14022
|
+
}
|
|
14023
|
+
const print = fingerprint(shown);
|
|
14024
|
+
if (run.seen !== print) {
|
|
14025
|
+
run.seen = print;
|
|
14026
|
+
run.seenAt = d.now();
|
|
14027
|
+
run.seenTicks = 0;
|
|
14028
|
+
} else
|
|
14029
|
+
run.seenTicks = (run.seenTicks ?? 0) + 1;
|
|
14030
|
+
return shown;
|
|
14031
|
+
}
|
|
13711
14032
|
async function dropPermission(d, run, why) {
|
|
13712
14033
|
const perms = permissionsOf(d.source);
|
|
13713
14034
|
if (!run.permission || !perms || !run.runId)
|
|
@@ -14532,11 +14853,13 @@ async function follow(d, state, run, claim, runner, finished, attention, changes
|
|
|
14532
14853
|
return true;
|
|
14533
14854
|
await judgeLook(d, run, view);
|
|
14534
14855
|
if (view.state === "MERGED") {
|
|
14856
|
+
await dropPermission(d, run, "the PR merged");
|
|
14535
14857
|
const landed = await ripple(d, state, run, claim, runner, pr.url, finished, decided, view.head, view.mergeCommit);
|
|
14536
14858
|
return !landed;
|
|
14537
14859
|
}
|
|
14538
14860
|
if (view.state === "CLOSED") {
|
|
14539
14861
|
d.log(`${ref(run)}: ${pr.url} was closed without a merge — let go, its session stopped`);
|
|
14862
|
+
await dropPermission(d, run, "the PR closed without a merge");
|
|
14540
14863
|
stopOpen(d, run, runner);
|
|
14541
14864
|
return false;
|
|
14542
14865
|
}
|
|
@@ -14556,6 +14879,8 @@ async function follow(d, state, run, claim, runner, finished, attention, changes
|
|
|
14556
14879
|
kind: "review",
|
|
14557
14880
|
what: `${view.feedback - pr.feedback} new comment${view.feedback - pr.feedback === 1 ? "" : "s"}`
|
|
14558
14881
|
});
|
|
14882
|
+
if (await settlePermission(d, runner, run, readFollowed(d, runner, run)))
|
|
14883
|
+
return true;
|
|
14559
14884
|
const tell = (text, what) => {
|
|
14560
14885
|
let alive = false;
|
|
14561
14886
|
try {
|
|
@@ -15650,7 +15975,7 @@ var headlessScript = (o, log) => {
|
|
|
15650
15975
|
...o.model ? [`--model ${shq2(o.model)}`] : [],
|
|
15651
15976
|
...o.effort ? [`--effort ${shq2(o.effort)}`] : []
|
|
15652
15977
|
].join(" ");
|
|
15653
|
-
return
|
|
15978
|
+
return `${execLine(cmd)} > ${shq2(log)} 2>&1`;
|
|
15654
15979
|
};
|
|
15655
15980
|
var transcriptTail = (log) => {
|
|
15656
15981
|
const lines = log.trimEnd().split(`
|
|
@@ -16567,7 +16892,7 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16567
16892
|
...flags.strictMcp ? { strictMcp: true } : {},
|
|
16568
16893
|
...flags.sandbox ? {
|
|
16569
16894
|
sandbox: true,
|
|
16570
|
-
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
|
|
16895
|
+
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
|
|
16571
16896
|
const gitCommonDir = gitCommonDirOf(exec, cwd);
|
|
16572
16897
|
const dir = join16(stateDir, "sandbox");
|
|
16573
16898
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
@@ -16576,6 +16901,9 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16576
16901
|
...SANDBOX_DEFAULT_ALLOW,
|
|
16577
16902
|
...flags.sandboxAllow ?? []
|
|
16578
16903
|
]);
|
|
16904
|
+
walls.permissions.allow = [
|
|
16905
|
+
...new Set([...walls.permissions.allow, ...allow ?? []])
|
|
16906
|
+
];
|
|
16579
16907
|
const settings = withHook(name, { ...walls });
|
|
16580
16908
|
writeFileSync13(p, `${JSON.stringify(settings, null, 2)}
|
|
16581
16909
|
`, { mode: 384 });
|
|
@@ -16583,10 +16911,11 @@ function engineDeps(o, flags, source, runner, stateName) {
|
|
|
16583
16911
|
return { settingsFile: p, gitCommonDir };
|
|
16584
16912
|
})
|
|
16585
16913
|
} : {},
|
|
16586
|
-
writeHookSettings: (name) => {
|
|
16914
|
+
writeHookSettings: (name, allow) => {
|
|
16587
16915
|
mkdirSync12(hooksDir, { recursive: true, mode: 448 });
|
|
16588
16916
|
const p = join16(hooksDir, `${name}.json`);
|
|
16589
|
-
|
|
16917
|
+
const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
|
|
16918
|
+
writeFileSync13(p, `${JSON.stringify(withHook(name, base), null, 2)}
|
|
16590
16919
|
`, { mode: 384 });
|
|
16591
16920
|
chmodSync4(p, 384);
|
|
16592
16921
|
return p;
|
|
@@ -16717,7 +17046,7 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
16717
17046
|
runner: deps.runner.name,
|
|
16718
17047
|
atOnce: flags.atOnce,
|
|
16719
17048
|
interval: flags.interval,
|
|
16720
|
-
cliVersion: "0.1.
|
|
17049
|
+
cliVersion: "0.1.46"
|
|
16721
17050
|
};
|
|
16722
17051
|
const beat = (checkedAt) => {
|
|
16723
17052
|
const m = deps.machine;
|
|
@@ -17619,7 +17948,7 @@ function formatDoctor(checks) {
|
|
|
17619
17948
|
}
|
|
17620
17949
|
function defaultWhich2(cmd) {
|
|
17621
17950
|
try {
|
|
17622
|
-
return
|
|
17951
|
+
return execFileSync4("which", ["-a", cmd], {
|
|
17623
17952
|
encoding: "utf8",
|
|
17624
17953
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17625
17954
|
}).split(`
|
|
@@ -18156,7 +18485,7 @@ function parsePmset(text) {
|
|
|
18156
18485
|
}
|
|
18157
18486
|
var defaultPmset = () => {
|
|
18158
18487
|
try {
|
|
18159
|
-
return parsePmset(
|
|
18488
|
+
return parsePmset(execFileSync4("pmset", ["-g", "custom"], { encoding: "utf8", timeout: 2000 }));
|
|
18160
18489
|
} catch {
|
|
18161
18490
|
return null;
|
|
18162
18491
|
}
|
|
@@ -18691,7 +19020,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18691
19020
|
...flags.strictMcp ? { strictMcp: true } : {},
|
|
18692
19021
|
...flags.sandbox ? {
|
|
18693
19022
|
sandbox: true,
|
|
18694
|
-
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
|
|
19023
|
+
writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
|
|
18695
19024
|
const gitCommonDir = gitCommonDirOf2(exec, cwd);
|
|
18696
19025
|
const dir = join21(stateDir, "sandbox");
|
|
18697
19026
|
mkdirSync16(dir, { recursive: true, mode: 448 });
|
|
@@ -18700,6 +19029,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18700
19029
|
...SANDBOX_DEFAULT_ALLOW,
|
|
18701
19030
|
...flags.sandboxAllow ?? []
|
|
18702
19031
|
]);
|
|
19032
|
+
walls.permissions.allow = [
|
|
19033
|
+
...new Set([...walls.permissions.allow, ...allow ?? []])
|
|
19034
|
+
];
|
|
18703
19035
|
const settings = withHook(name, { ...walls });
|
|
18704
19036
|
writeFileSync18(p, `${JSON.stringify(settings, null, 2)}
|
|
18705
19037
|
`, { mode: 384 });
|
|
@@ -18707,10 +19039,11 @@ function engineDeps2(o, flags, source, runner, stateName) {
|
|
|
18707
19039
|
return { settingsFile: p, gitCommonDir };
|
|
18708
19040
|
})
|
|
18709
19041
|
} : {},
|
|
18710
|
-
writeHookSettings: (name) => {
|
|
19042
|
+
writeHookSettings: (name, allow) => {
|
|
18711
19043
|
mkdirSync16(hooksDir, { recursive: true, mode: 448 });
|
|
18712
19044
|
const p = join21(hooksDir, `${name}.json`);
|
|
18713
|
-
|
|
19045
|
+
const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
|
|
19046
|
+
writeFileSync18(p, `${JSON.stringify(withHook(name, base), null, 2)}
|
|
18714
19047
|
`, { mode: 384 });
|
|
18715
19048
|
chmodSync5(p, 384);
|
|
18716
19049
|
return p;
|
|
@@ -18850,7 +19183,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
18850
19183
|
runner: deps.runner.name,
|
|
18851
19184
|
atOnce: flags.atOnce,
|
|
18852
19185
|
interval: flags.interval,
|
|
18853
|
-
cliVersion: "0.1.
|
|
19186
|
+
cliVersion: "0.1.46"
|
|
18854
19187
|
};
|
|
18855
19188
|
const beat = (checkedAt) => {
|
|
18856
19189
|
const m = deps.machine;
|
|
@@ -22498,12 +22831,81 @@ var promptEvent = async (o) => {
|
|
|
22498
22831
|
return code;
|
|
22499
22832
|
};
|
|
22500
22833
|
|
|
22834
|
+
// src/commands/reopen.ts
|
|
22835
|
+
var REOPEN_USAGE = 'usage: sleds reopen <key> --reason "<why it is reopened>" [--json]';
|
|
22836
|
+
function parseReopenArgs(argv) {
|
|
22837
|
+
let key;
|
|
22838
|
+
let reason = "";
|
|
22839
|
+
let json = false;
|
|
22840
|
+
for (let i = 0;i < argv.length; i++) {
|
|
22841
|
+
const a = argv[i];
|
|
22842
|
+
if (a === "--json")
|
|
22843
|
+
json = true;
|
|
22844
|
+
else if (a === "--reason") {
|
|
22845
|
+
const value = argv[i + 1];
|
|
22846
|
+
if (value === undefined || value.startsWith("--"))
|
|
22847
|
+
return { error: "--reason needs a value" };
|
|
22848
|
+
reason = value;
|
|
22849
|
+
i++;
|
|
22850
|
+
} else if (a.startsWith("--"))
|
|
22851
|
+
return { error: `unknown flag ${a}` };
|
|
22852
|
+
else if (key === undefined)
|
|
22853
|
+
key = a;
|
|
22854
|
+
else
|
|
22855
|
+
return { error: `one item at a time — "${a}" is extra` };
|
|
22856
|
+
}
|
|
22857
|
+
if (!key)
|
|
22858
|
+
return { error: "which item?" };
|
|
22859
|
+
return { key, reason: reason.trim(), json };
|
|
22860
|
+
}
|
|
22861
|
+
function reopenedLine(key, j) {
|
|
22862
|
+
const shown = typeof j.key === "string" ? j.key : key;
|
|
22863
|
+
return typeof j.sled === "string" && j.sled ? `${shown} reopened — back on ${j.sled}'s sled` : `${shown} reopened — back to ready, on no sled`;
|
|
22864
|
+
}
|
|
22865
|
+
async function reopenCmd(o) {
|
|
22866
|
+
const log = o.log ?? console.log;
|
|
22867
|
+
const error = o.error ?? console.error;
|
|
22868
|
+
const a = parseReopenArgs(o.argv);
|
|
22869
|
+
if ("error" in a) {
|
|
22870
|
+
error(a.error);
|
|
22871
|
+
error(REOPEN_USAGE);
|
|
22872
|
+
return 2;
|
|
22873
|
+
}
|
|
22874
|
+
if (!a.reason) {
|
|
22875
|
+
error(`${a.key}: say why it is reopened — it goes on the item's thread`);
|
|
22876
|
+
error(REOPEN_USAGE);
|
|
22877
|
+
return 1;
|
|
22878
|
+
}
|
|
22879
|
+
const client = engineClient({ home: o.home, ...o.client ? { client: o.client } : {} });
|
|
22880
|
+
if ("error" in client) {
|
|
22881
|
+
error(client.error);
|
|
22882
|
+
return 1;
|
|
22883
|
+
}
|
|
22884
|
+
const r = await client.postFull("/proxy/reopen", { project: a.key, reason: a.reason });
|
|
22885
|
+
if (!r) {
|
|
22886
|
+
error(`${a.key}: SLEDS unreachable — nothing reopened`);
|
|
22887
|
+
return 1;
|
|
22888
|
+
}
|
|
22889
|
+
const ok = r.status < 300 && r.json?.ok !== false;
|
|
22890
|
+
if (a.json)
|
|
22891
|
+
log(JSON.stringify(r.json ?? { ok: false, error: `HTTP ${r.status}` }));
|
|
22892
|
+
if (!ok) {
|
|
22893
|
+
const why = typeof r.json?.error === "string" ? r.json.error : `HTTP ${r.status}`;
|
|
22894
|
+
if (!a.json)
|
|
22895
|
+
error(r.status === 404 && !r.json ? `${a.key}: SLEDS does not know this seat — run \`sleds init\`` : `${a.key}: ${why}`);
|
|
22896
|
+
return 1;
|
|
22897
|
+
}
|
|
22898
|
+
if (!a.json)
|
|
22899
|
+
log(reopenedLine(a.key, r.json ?? {}));
|
|
22900
|
+
return 0;
|
|
22901
|
+
}
|
|
22902
|
+
|
|
22501
22903
|
// src/commands/route.ts
|
|
22502
22904
|
init_src();
|
|
22503
22905
|
import { readFileSync as readFileSync27 } from "node:fs";
|
|
22504
22906
|
import { hostname as hostname12 } from "node:os";
|
|
22505
22907
|
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>]";
|
|
22506
|
-
var
|
|
22908
|
+
var USAGE2 = "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]";
|
|
22507
22909
|
var ANSWER_USAGE = "usage: sleds route answer <key>/<node> <answer…> | sleds route answer <key>/<node> --stdin [--json]";
|
|
22508
22910
|
var PROPOSE_USAGE = "usage: sleds route propose <key> (--file <nodes.json> | --stdin) [--name <engine>] [--json]";
|
|
22509
22911
|
var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--json]";
|
|
@@ -23058,14 +23460,14 @@ var routeCmd = async (o) => {
|
|
|
23058
23460
|
const parsed = parseEngineArgs(o.argv.filter((a) => a !== "--dot" && a !== "--long" && a !== "--json"), o.host ?? hostname12());
|
|
23059
23461
|
if ("error" in parsed) {
|
|
23060
23462
|
error(parsed.error);
|
|
23061
|
-
error(
|
|
23463
|
+
error(USAGE2);
|
|
23062
23464
|
return 2;
|
|
23063
23465
|
}
|
|
23064
23466
|
const { positional } = parsed;
|
|
23065
23467
|
const isRun = positional[0] === "run";
|
|
23066
23468
|
const key = isRun ? positional[1] : positional[0];
|
|
23067
23469
|
if (!key) {
|
|
23068
|
-
error(
|
|
23470
|
+
error(USAGE2);
|
|
23069
23471
|
return 2;
|
|
23070
23472
|
}
|
|
23071
23473
|
if (asJson) {
|
|
@@ -23321,7 +23723,7 @@ var seatEvent = async (o) => {
|
|
|
23321
23723
|
};
|
|
23322
23724
|
|
|
23323
23725
|
// src/commands/seatSync.ts
|
|
23324
|
-
import { execFileSync as
|
|
23726
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
23325
23727
|
import { existsSync as existsSync26, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
|
|
23326
23728
|
import { platform as platform2 } from "node:os";
|
|
23327
23729
|
import { dirname as dirname17, join as join26 } from "node:path";
|
|
@@ -23689,7 +24091,7 @@ async function readCookieRowsBun(home, source) {
|
|
|
23689
24091
|
}
|
|
23690
24092
|
function keychainKey(service) {
|
|
23691
24093
|
try {
|
|
23692
|
-
return
|
|
24094
|
+
return execFileSync5("security", ["find-generic-password", "-w", "-s", service], {
|
|
23693
24095
|
encoding: "utf8"
|
|
23694
24096
|
}).trim();
|
|
23695
24097
|
} catch {
|
|
@@ -23701,7 +24103,7 @@ function decryptJar(rows, safeStoragePw) {
|
|
|
23701
24103
|
}
|
|
23702
24104
|
var curlUsage = (org, cookie) => {
|
|
23703
24105
|
try {
|
|
23704
|
-
const out =
|
|
24106
|
+
const out = execFileSync5("curl", usageCurlArgs(), {
|
|
23705
24107
|
input: usageCurlConfig(org, cookie),
|
|
23706
24108
|
encoding: "utf8",
|
|
23707
24109
|
maxBuffer: 8000000
|
|
@@ -23766,16 +24168,16 @@ function install(home, argv, source, log) {
|
|
|
23766
24168
|
}
|
|
23767
24169
|
writeFileSync25(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
|
|
23768
24170
|
try {
|
|
23769
|
-
|
|
24171
|
+
execFileSync5("launchctl", ["unload", path], { stdio: "ignore" });
|
|
23770
24172
|
} catch {}
|
|
23771
|
-
|
|
24173
|
+
execFileSync5("launchctl", ["load", "-w", path]);
|
|
23772
24174
|
log(`seat-sync timer installed (every ${SYNC_INTERVAL_S / 60} min) → ${path}`);
|
|
23773
24175
|
return 0;
|
|
23774
24176
|
}
|
|
23775
24177
|
function uninstall(home, source, log) {
|
|
23776
24178
|
const path = plistPath2(home, source);
|
|
23777
24179
|
try {
|
|
23778
|
-
|
|
24180
|
+
execFileSync5("launchctl", ["unload", "-w", path], { stdio: "ignore" });
|
|
23779
24181
|
} catch {}
|
|
23780
24182
|
rmSync16(seatTokenPath(home, source), { force: true });
|
|
23781
24183
|
log(existsSync26(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
|
|
@@ -24647,7 +25049,7 @@ var verify = async (o) => {
|
|
|
24647
25049
|
};
|
|
24648
25050
|
|
|
24649
25051
|
// src/commands/webhooks.ts
|
|
24650
|
-
var
|
|
25052
|
+
var USAGE3 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
|
|
24651
25053
|
var str14 = (x) => typeof x === "string" ? x : "";
|
|
24652
25054
|
function ago3(at, now) {
|
|
24653
25055
|
const m = Math.max(0, Math.floor((now - at) / 60000));
|
|
@@ -24698,7 +25100,7 @@ var webhooksCmd = async (o) => {
|
|
|
24698
25100
|
const now = (o.now ?? Date.now)();
|
|
24699
25101
|
const sub = words[0];
|
|
24700
25102
|
if (sub && (sub !== "deliveries" || words.length !== 2)) {
|
|
24701
|
-
error(
|
|
25103
|
+
error(USAGE3);
|
|
24702
25104
|
return 2;
|
|
24703
25105
|
}
|
|
24704
25106
|
const path = sub === "deliveries" ? `/proxy/webhooks/deliveries?hook=${encodeURIComponent(words[1] ?? "")}` : "/proxy/webhooks";
|
|
@@ -24958,7 +25360,7 @@ var isSledsStopHook2 = isSledsHookFor2("stop-event");
|
|
|
24958
25360
|
var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
|
|
24959
25361
|
|
|
24960
25362
|
// src/index.ts
|
|
24961
|
-
var
|
|
25363
|
+
var USAGE4 = `usage: sleds <command> [options]
|
|
24962
25364
|
|
|
24963
25365
|
commands:
|
|
24964
25366
|
init Sign in through the browser, make this machine a seat, install the /sled skill
|
|
@@ -25013,6 +25415,10 @@ commands:
|
|
|
25013
25415
|
--run start Claude Code with the pack as the first prompt
|
|
25014
25416
|
webhooks The org's outbound webhooks (admins); deliveries <id or name> lists one hook's
|
|
25015
25417
|
last hundred deliveries. Registering and Redeliver stay in the app.
|
|
25418
|
+
deploy A release step's one line: tell SLEDS this commit is now running in an environment.
|
|
25419
|
+
--env <slug> the environment (required); --service <name> for a monorepo
|
|
25420
|
+
--sha / --repo else the CI's own variables, then git
|
|
25421
|
+
Token from SLEDS_DEPLOY_TOKEN (Settings \u203A Deploys). SLEDS down never fails a release.
|
|
25016
25422
|
sled --json What is on your sled (mine with each route, proposed, sent), for a front end.
|
|
25017
25423
|
sleds sled top KEY put KEY first on your sled; the engine picks it next
|
|
25018
25424
|
route <key> Print an item's route: its nodes, what each waits on, what is ready, where each
|
|
@@ -25040,6 +25446,9 @@ commands:
|
|
|
25040
25446
|
--mode <permission-mode> Claude Code's (default auto; acceptEdits stops an unattended
|
|
25041
25447
|
session at git push; an account without auto passes acceptEdits)
|
|
25042
25448
|
--model <id> Claude Code's model
|
|
25449
|
+
reopen <key> Put back an item that landed by mistake (a skipped node closed its route): its
|
|
25450
|
+
landing is cleared and it goes back on the sled it came off, or to ready.
|
|
25451
|
+
--reason "<text>" why \u2014 required; it goes on the item's thread (--json)
|
|
25043
25452
|
engine The long-running loop over the routes Run armed on your sled: takes each ready
|
|
25044
25453
|
node, starts a session on its own branch, follows its PR to the merge (CI failures
|
|
25045
25454
|
and review feedback go back to the session; a merge lands the node, closes the
|
|
@@ -25161,11 +25570,11 @@ var wantsHelp = (argv) => {
|
|
|
25161
25570
|
return false;
|
|
25162
25571
|
};
|
|
25163
25572
|
var commandUsage = (cmd) => {
|
|
25164
|
-
const lines =
|
|
25573
|
+
const lines = USAGE4.split(`
|
|
25165
25574
|
`);
|
|
25166
25575
|
const start = lines.findIndex((l) => l === ` ${cmd}` || l.startsWith(` ${cmd} `));
|
|
25167
25576
|
if (start === -1)
|
|
25168
|
-
return
|
|
25577
|
+
return USAGE4;
|
|
25169
25578
|
const section = [lines[start]];
|
|
25170
25579
|
for (const l of lines.slice(start + 1)) {
|
|
25171
25580
|
if (!/^ {3,}\S/.test(l))
|
|
@@ -25221,8 +25630,12 @@ var main = async (argv) => {
|
|
|
25221
25630
|
return packCmd({ home, argv: argv.slice(1) });
|
|
25222
25631
|
case "webhooks":
|
|
25223
25632
|
return webhooksCmd({ home, argv: argv.slice(1) });
|
|
25633
|
+
case "deploy":
|
|
25634
|
+
return deployCmd({ argv: argv.slice(1) });
|
|
25224
25635
|
case "sled":
|
|
25225
25636
|
return sledCmd({ home, argv: argv.slice(1) });
|
|
25637
|
+
case "reopen":
|
|
25638
|
+
return reopenCmd({ home, argv: argv.slice(1) });
|
|
25226
25639
|
case "engine":
|
|
25227
25640
|
case "route": {
|
|
25228
25641
|
if (cmd === "engine" && argv[1] === "status")
|
|
@@ -25415,19 +25828,19 @@ var main = async (argv) => {
|
|
|
25415
25828
|
case "help":
|
|
25416
25829
|
case "--help":
|
|
25417
25830
|
case "-h":
|
|
25418
|
-
console.log(
|
|
25831
|
+
console.log(USAGE4);
|
|
25419
25832
|
console.log(`
|
|
25420
25833
|
${renderHelpText(argv[1])}`);
|
|
25421
25834
|
return 0;
|
|
25422
25835
|
case "version":
|
|
25423
25836
|
case "--version":
|
|
25424
25837
|
case "-v":
|
|
25425
|
-
console.log("sleds 0.1.
|
|
25838
|
+
console.log("sleds 0.1.46");
|
|
25426
25839
|
return 0;
|
|
25427
25840
|
default:
|
|
25428
25841
|
console.error(`unknown command: ${cmd}
|
|
25429
25842
|
`);
|
|
25430
|
-
console.error(
|
|
25843
|
+
console.error(USAGE4);
|
|
25431
25844
|
return 1;
|
|
25432
25845
|
}
|
|
25433
25846
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleds/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
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",
|