@sleds/cli 0.1.51 → 0.1.52

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.
Files changed (2) hide show
  1. package/dist/sleds.mjs +248 -30
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -5689,11 +5689,20 @@ var init_copy = __esm(() => {
5689
5689
  });
5690
5690
 
5691
5691
  // ../../packages/shared/src/slack/agent.ts
5692
+ var REPLY_RULES;
5692
5693
  var init_agent = __esm(() => {
5693
5694
  init_intake();
5694
5695
  init_brief();
5695
5696
  init_copy();
5696
5697
  init_mention();
5698
+ REPLY_RULES = [
5699
+ "How to answer this reply:",
5700
+ '- If it answers nothing and changes nothing — thanks, "noted", "copy", "will do", or people talking to each other — set quiet to true and post nothing.',
5701
+ "- Never ask a question the thread already asked. If it is still open, say only what is still needed, once, in a few words.",
5702
+ "- If someone in the thread already gave the cause or a fix, do not ask the poster for details it makes moot. Say in one line what is left, or set quiet.",
5703
+ "- Keep it to what changed and what is next. No recap of the ask."
5704
+ ].join(`
5705
+ `);
5697
5706
  });
5698
5707
 
5699
5708
  // ../../packages/shared/src/slack/channel.ts
@@ -8545,7 +8554,13 @@ function parseParked(v) {
8545
8554
  ...typeof a.effort === "string" ? { effort: a.effort } : {}
8546
8555
  };
8547
8556
  }
8548
- return { question: v.question, at: v.at, ...answerer ? { answerer } : {} };
8557
+ return {
8558
+ question: v.question,
8559
+ at: v.at,
8560
+ ...typeof v.screen === "string" ? { screen: v.screen } : {},
8561
+ ...v.still === true ? { still: true } : {},
8562
+ ...answerer ? { answerer } : {}
8563
+ };
8549
8564
  }
8550
8565
  function parseRun(v) {
8551
8566
  if (!isObject8(v))
@@ -8585,7 +8600,10 @@ function parseRun(v) {
8585
8600
  decisionId: v.permission.decisionId,
8586
8601
  options: Array.isArray(v.permission.options) ? v.permission.options.filter((x) => typeof x === "string") : [],
8587
8602
  at: v.permission.at,
8588
- ...v.permission.onScreen === true ? { onScreen: true } : {}
8603
+ ...v.permission.onScreen === true ? { onScreen: true } : {},
8604
+ ...typeof v.permission.pressedAt === "number" ? { pressedAt: v.permission.pressedAt } : {},
8605
+ ...typeof v.permission.presses === "number" ? { presses: v.permission.presses } : {},
8606
+ ...v.permission.gaveUp === true ? { gaveUp: true } : {}
8589
8607
  }
8590
8608
  } : {},
8591
8609
  ...isObject8(v.provider) && typeof v.provider.why === "string" && typeof v.provider.since === "number" && typeof v.provider.tries === "number" ? { provider: { why: v.provider.why, since: v.provider.since, tries: v.provider.tries } } : {},
@@ -11243,6 +11261,8 @@ var orcaLauncher = (o) => `#!/bin/sh
11243
11261
  ${execLine(agentShellCommand(o))}
11244
11262
  `;
11245
11263
  var orcaCommand = (launcher) => `sh ${shq2(launcher)}; exit`;
11264
+ var orcaRequestId = (env, text) => str8(obj(obj(env?.error)?.data)?.orchestrationRequestId) ?? text.match(/--retry-request ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i)?.[1] ?? null;
11265
+ var ESC = "\x1B";
11246
11266
  var GONE = /exit|clos|gone|dead|stopp|kill/i;
11247
11267
  var orcaTerminalLive = (status, stdout) => {
11248
11268
  const env = parseOrcaJson(stdout);
@@ -11327,6 +11347,7 @@ function orcaRunner(exec, deps = {}) {
11327
11347
  }
11328
11348
  return env;
11329
11349
  };
11350
+ const retries = new Map;
11330
11351
  return {
11331
11352
  name: "orca",
11332
11353
  makesWorktree: true,
@@ -11378,10 +11399,51 @@ function orcaRunner(exec, deps = {}) {
11378
11399
  return r.status === 0 ? r.stdout : "";
11379
11400
  },
11380
11401
  send(h, text) {
11381
- const r = exec("orca", ["terminal", "send", "--terminal", h.id, "--text", text, "--enter"]);
11382
- if (r.status !== 0) {
11383
- throw new Error(`orca terminal send ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
11402
+ const key = `${h.id}
11403
+ ${text}`;
11404
+ const attempt = (retry) => {
11405
+ const r = exec("orca", [
11406
+ "terminal",
11407
+ "send",
11408
+ "--terminal",
11409
+ h.id,
11410
+ "--text",
11411
+ text,
11412
+ "--enter",
11413
+ ...retry ? ["--retry-request", retry] : [],
11414
+ "--json"
11415
+ ]);
11416
+ const env = parseOrcaJson(r.stdout);
11417
+ const ok = r.status === 0 && env?.ok !== false;
11418
+ return { r, env, ok, id: ok ? null : orcaRequestId(env, `${r.stderr}
11419
+ ${r.stdout}`) };
11420
+ };
11421
+ const prior = retries.get(key);
11422
+ let a = attempt(prior);
11423
+ if (!a.ok && a.id && a.id !== prior)
11424
+ a = attempt(a.id);
11425
+ if (a.ok) {
11426
+ retries.delete(key);
11427
+ return;
11384
11428
  }
11429
+ if (a.id)
11430
+ retries.set(key, a.id);
11431
+ else
11432
+ retries.delete(key);
11433
+ const why = orcaError(a.env) ?? (a.r.stderr.trim() || `exit ${a.r.status}`);
11434
+ throw new Error(`orca terminal send ${h.id} failed: ${why}`);
11435
+ },
11436
+ sendKey(h, key) {
11437
+ if (!isRunnerKey(key))
11438
+ throw new Error(`orca terminal send ${h.id}: no key ${JSON.stringify(key)}`);
11439
+ const what = `orca terminal send ${h.id} (key ${key})`;
11440
+ const send = (args) => orca(["terminal", "send", "--terminal", h.id, ...args, "--json"], what);
11441
+ if (key === "esc") {
11442
+ send(["--text", ESC]);
11443
+ return;
11444
+ }
11445
+ send(["--text", key]);
11446
+ send(["--enter"]);
11385
11447
  },
11386
11448
  stop(h) {
11387
11449
  exec("orca", ["terminal", "close", "--terminal", h.id]);
@@ -11449,7 +11511,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
11449
11511
  name: flags.name,
11450
11512
  os: host.os ?? process.platform,
11451
11513
  runner: flags.runner,
11452
- version: host.version ?? "0.1.51" ?? "dev",
11514
+ version: host.version ?? "0.1.52" ?? "dev",
11453
11515
  running: live.length,
11454
11516
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
11455
11517
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -12454,6 +12516,10 @@ function permissionsOf(s) {
12454
12516
  const p = s;
12455
12517
  return p && typeof p.openPermission === "function" && typeof p.clearPermission === "function" && typeof p.decisionOf === "function" ? s : null;
12456
12518
  }
12519
+ function resumesOf(s) {
12520
+ const r = s;
12521
+ return r && typeof r.resumeParked === "function" ? s : null;
12522
+ }
12457
12523
  var FAIL_LOG_MAX = 2000;
12458
12524
  var UNREACHABLE3 = "SLEDS unreachable";
12459
12525
  function keysIn(states, state) {
@@ -12638,6 +12704,9 @@ function profileOf(r) {
12638
12704
  const atOnce = num5(p.atOnce);
12639
12705
  if (atOnce !== undefined && atOnce >= 1)
12640
12706
  out.atOnce = Math.round(atOnce);
12707
+ const seatAtOnce = num5(r.seatAtOnce);
12708
+ if (seatAtOnce !== undefined && Number.isInteger(seatAtOnce) && seatAtOnce >= 1)
12709
+ out.seatAtOnce = seatAtOnce;
12641
12710
  if (typeof p.mcpStrict === "boolean")
12642
12711
  out.mcpStrict = p.mcpStrict;
12643
12712
  const holdCpu = num5(p.holdCpu);
@@ -13100,6 +13169,18 @@ function sledsSource(client, o) {
13100
13169
  throw new Error(serverMessage(r));
13101
13170
  return r.json.cleared === true;
13102
13171
  },
13172
+ async resumeParked(runId, why, engine, leaseMin) {
13173
+ const r = await client.postFull("/proxy/run", { runId, engine, leaseMin, resumed: { why } });
13174
+ if (!r)
13175
+ throw new Error(`${UNREACHABLE3} — the resume not recorded`);
13176
+ if (r.status >= 300 || !r.json || r.json.ok === false)
13177
+ throw new Error(serverMessage(r));
13178
+ const leaseUntil = typeof r.json.leaseUntil === "number" ? r.json.leaseUntil : undefined;
13179
+ return {
13180
+ resumed: r.json.resumed === true,
13181
+ ...leaseUntil !== undefined ? { leaseUntil } : {}
13182
+ };
13183
+ },
13103
13184
  async decisionOf(item, decisionId) {
13104
13185
  const route = await routeOf(item);
13105
13186
  const decisions = Array.isArray(route?.decisions) ? route.decisions.filter(isObject8) : [];
@@ -13692,6 +13773,7 @@ async function tick(deps) {
13692
13773
  deps.providerHold = d.providerHold;
13693
13774
  deps.draining = d.draining;
13694
13775
  deps.atOnce = d.atOnce;
13776
+ deps.seatAtOnceNoted = d.seatAtOnceNoted;
13695
13777
  deps.runner = d.runner;
13696
13778
  deps.runnerRefused = d.runnerRefused;
13697
13779
  }
@@ -13763,6 +13845,10 @@ async function tickOnce(d) {
13763
13845
  try {
13764
13846
  open = runner.status(run.handle) === "running";
13765
13847
  } catch {}
13848
+ if (open && await answeredInSession(d, runner, run)) {
13849
+ keep.push(run);
13850
+ continue;
13851
+ }
13766
13852
  if (open || liveAnswerer(run))
13767
13853
  keep.push(run);
13768
13854
  else
@@ -14287,7 +14373,20 @@ async function settlePermission(d, runner, run, shown) {
14287
14373
  const dialog = runner.readsSession ? permissionDialog(shown) : null;
14288
14374
  const p = run.permission;
14289
14375
  if (p) {
14290
- const gone = runner.readsSession && (p.onScreen ? !dialog : (run.seenAt ?? 0) > p.at);
14376
+ const gone = runner.readsSession && (p.onScreen ? !dialog : (run.seenAt ?? 0) > (p.pressedAt ?? p.at));
14377
+ const answered = () => {
14378
+ run.permission = undefined;
14379
+ run.answeredAt = d.now();
14380
+ if (markFile)
14381
+ clearPermissionMark(markFile);
14382
+ };
14383
+ if (p.gaveUp) {
14384
+ if (!gone)
14385
+ return true;
14386
+ d.log(`${at}: the permission dialog was answered in the terminal`);
14387
+ answered();
14388
+ return false;
14389
+ }
14291
14390
  let now;
14292
14391
  try {
14293
14392
  now = await perms.decisionOf(run.item, p.decisionId);
@@ -14296,14 +14395,42 @@ async function settlePermission(d, runner, run, shown) {
14296
14395
  return true;
14297
14396
  }
14298
14397
  if (now?.state === "answered" && now.answer) {
14299
- if (gone)
14300
- d.log(`${at}: the permission was answered after its dialog closed — not pressed`);
14301
- else if (!pressAnswer(d, runner, run, p.options, now.answer))
14398
+ if (p.pressedAt === undefined) {
14399
+ if (gone)
14400
+ d.log(`${at}: the permission was answered after its dialog closed — not pressed`);
14401
+ else {
14402
+ const pressed = pressAnswer(d, runner, run, p.options, now.answer);
14403
+ if (!pressed)
14404
+ return true;
14405
+ if (pressed === "key") {
14406
+ run.permission = { ...p, pressedAt: d.now(), presses: 1 };
14407
+ return true;
14408
+ }
14409
+ }
14410
+ answered();
14302
14411
  return true;
14303
- run.permission = undefined;
14304
- run.answeredAt = d.now();
14305
- if (markFile)
14306
- clearPermissionMark(markFile);
14412
+ }
14413
+ const key = permissionKey(now.answer) ?? now.answer;
14414
+ if (gone) {
14415
+ d.log(`${at}: the permission is answered — pressed ${key}; the session goes on`);
14416
+ answered();
14417
+ return false;
14418
+ }
14419
+ const presses = p.presses ?? 1;
14420
+ if (presses >= PRESS_TRIES) {
14421
+ const why = `pressed ${key} ${presses} times; the dialog stayed — answer it in the terminal`;
14422
+ try {
14423
+ await perms.clearPermission(run.runId, why, d.engine);
14424
+ } catch (e) {
14425
+ d.log(`${at}: could not clear the permission, trying next tick: ${message(e)}`);
14426
+ return true;
14427
+ }
14428
+ d.log(`${at}: ${why}`);
14429
+ run.permission = { ...p, gaveUp: true };
14430
+ return true;
14431
+ }
14432
+ if (pressAnswer(d, runner, run, p.options, now.answer) === "key")
14433
+ run.permission = { ...p, pressedAt: d.now(), presses: presses + 1 };
14307
14434
  return true;
14308
14435
  }
14309
14436
  if (now?.state !== "open" && now?.state !== "escalated") {
@@ -14353,6 +14480,7 @@ async function settlePermission(d, runner, run, shown) {
14353
14480
  d.log(`${at}: stopped at a permission dialog — it waits for a person (${runner.watchHint(run.handle)})`);
14354
14481
  return true;
14355
14482
  }
14483
+ var PRESS_TRIES = 3;
14356
14484
  function pressAnswer(d, runner, run, options, answer) {
14357
14485
  const at = ref(run);
14358
14486
  const key = permissionKey(answer);
@@ -14363,13 +14491,15 @@ function pressAnswer(d, runner, run, options, answer) {
14363
14491
  runner.sendKey(run.handle, key);
14364
14492
  else
14365
14493
  runner.send(run.handle, key);
14494
+ if (runner.readsSession)
14495
+ return "key";
14366
14496
  d.log(`${at}: the permission is answered — pressed ${key}; the session goes on`);
14367
14497
  } else {
14368
14498
  runner.sendKey?.(run.handle, "esc");
14369
14499
  const note = runner.send(run.handle, answer);
14370
14500
  d.log(`${at}: the permission is answered in words — Esc, then typed in${note ? ` (${note})` : ""}`);
14371
14501
  }
14372
- return true;
14502
+ return "done";
14373
14503
  } catch (e) {
14374
14504
  d.log(`${at}: could not press the permission's answer, trying next tick: ${message(e)}`);
14375
14505
  return false;
@@ -14538,10 +14668,25 @@ async function rereadAtOnce(d) {
14538
14668
  try {
14539
14669
  next = await d.rereadAtOnce(profileOf2(d));
14540
14670
  } catch {}
14541
- if (next === null || next === d.atOnce)
14671
+ if (next !== null && next !== d.atOnce) {
14672
+ d.atOnce = next;
14673
+ d.log(`at once now ${next}, from your engine profile`);
14674
+ }
14675
+ await sayAtOnceDisagrees(d);
14676
+ }
14677
+ async function sayAtOnceDisagrees(d) {
14678
+ const seat = (await profileOf2(d)())?.seatAtOnce;
14679
+ if (seat === undefined)
14680
+ return;
14681
+ if (seat === d.atOnce) {
14682
+ d.seatAtOnceNoted = undefined;
14542
14683
  return;
14543
- d.atOnce = next;
14544
- d.log(`at once now ${next}, from your engine profile`);
14684
+ }
14685
+ const pair = `${d.atOnce}/${seat}`;
14686
+ if (d.seatAtOnceNoted === pair)
14687
+ return;
14688
+ d.seatAtOnceNoted = pair;
14689
+ d.log(seat < d.atOnce ? `at once ${d.atOnce} on the engine, but this seat allows ${seat} — the server refuses a claim past ${seat} on it; set it in Settings` : `at once ${d.atOnce} on the engine, but this seat allows ${seat} — the engine takes only ${d.atOnce}; set it in Settings`);
14545
14690
  }
14546
14691
  async function rereadRunner(d) {
14547
14692
  if (!d.rereadRunner)
@@ -14595,10 +14740,53 @@ function stopOpen(d, run, runner) {
14595
14740
  }
14596
14741
  }
14597
14742
  function holdParked(d, runner, run, question) {
14598
- run.parked = { question, at: d.now() };
14743
+ run.parked = { question, at: d.now(), ...run.seen ? { screen: run.seen } : {} };
14599
14744
  run.leaseUntil = undefined;
14600
14745
  setLane(d, runner, run.handle, "parked");
14601
14746
  }
14747
+ async function answeredInSession(d, runner, run) {
14748
+ const parked = run.parked;
14749
+ const resumes = resumesOf(d.source);
14750
+ if (!resumes || !runner.readsSession || !run.runId)
14751
+ return false;
14752
+ let print;
14753
+ try {
14754
+ print = fingerprint(runner.read(run.handle));
14755
+ } catch {
14756
+ return false;
14757
+ }
14758
+ if (print === parked.screen) {
14759
+ parked.still = true;
14760
+ return false;
14761
+ }
14762
+ const moved = parked.still === true;
14763
+ parked.screen = print;
14764
+ parked.still = false;
14765
+ if (!moved)
14766
+ return false;
14767
+ let r;
14768
+ try {
14769
+ r = await resumes.resumeParked(run.runId, "answered in the session", d.engine, d.leaseMin);
14770
+ } catch (e) {
14771
+ d.log(`${ref(run)}: the parked session moved on, but SLEDS did not hear it: ${message(e)}`);
14772
+ return false;
14773
+ }
14774
+ if (!r.resumed) {
14775
+ d.log(`${ref(run)}: the parked session moved on, but the node is no longer parked on its run`);
14776
+ return false;
14777
+ }
14778
+ stopAnswerer(d, run);
14779
+ const now = d.now();
14780
+ run.parked = undefined;
14781
+ run.seen = print;
14782
+ run.seenAt = now;
14783
+ run.seenTicks = 0;
14784
+ run.answeredAt = now;
14785
+ run.leaseUntil = r.leaseUntil ?? now + d.leaseMin * 60000;
14786
+ setLane(d, runner, run.handle, "working");
14787
+ d.log(`${ref(run)}: answered in the session — out of Needs you, running again`);
14788
+ return true;
14789
+ }
14602
14790
  function setLane(d, runner, h, lane) {
14603
14791
  try {
14604
14792
  runner.lane?.(h, lane);
@@ -17174,7 +17362,7 @@ async function fetchVersion(o) {
17174
17362
  const platform = o.platform ?? process.platform;
17175
17363
  const arch = o.arch ?? process.arch;
17176
17364
  const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
17177
- const running = o.running ?? "0.1.51";
17365
+ const running = o.running ?? "0.1.52";
17178
17366
  if (!publicKey)
17179
17367
  return { ok: false, why: "no release key yet" };
17180
17368
  if (!VERSION_RE2.test(o.version))
@@ -18758,7 +18946,7 @@ function engineClient(o, org) {
18758
18946
  return fetchClient({
18759
18947
  ...src,
18760
18948
  timeoutMs: ENGINE_TIMEOUT_MS,
18761
- version: "0.1.51"
18949
+ version: "0.1.52"
18762
18950
  });
18763
18951
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
18764
18952
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -19049,7 +19237,7 @@ async function driveEngine(o, flags, deps, until = {}) {
19049
19237
  runner: deps.runner.name,
19050
19238
  atOnce: flags.atOnce,
19051
19239
  interval: flags.interval,
19052
- cliVersion: "0.1.51"
19240
+ cliVersion: "0.1.52"
19053
19241
  };
19054
19242
  const beat = (checkedAt) => {
19055
19243
  const m = deps.machine;
@@ -20906,7 +21094,7 @@ function engineClient2(o, org) {
20906
21094
  return fetchClient({
20907
21095
  ...src,
20908
21096
  timeoutMs: ENGINE_TIMEOUT_MS2,
20909
- version: "0.1.51"
21097
+ version: "0.1.52"
20910
21098
  });
20911
21099
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
20912
21100
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -21232,7 +21420,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
21232
21420
  runner: deps.runner.name,
21233
21421
  atOnce: flags.atOnce,
21234
21422
  interval: flags.interval,
21235
- cliVersion: "0.1.51"
21423
+ cliVersion: "0.1.52"
21236
21424
  };
21237
21425
  const beat = (checkedAt) => {
21238
21426
  const m = deps.machine;
@@ -23320,7 +23508,7 @@ function contextFor(deps) {
23320
23508
  ...src,
23321
23509
  fetchFn: deps.fetchFn,
23322
23510
  timeoutMs: deps.timeoutMs,
23323
- version: "0.1.51"
23511
+ version: "0.1.52"
23324
23512
  }), deps.home, now) : null;
23325
23513
  return {
23326
23514
  channel: "code",
@@ -25834,14 +26022,16 @@ function routeReader(o, org) {
25834
26022
  read: (k) => readOrSay(client, `/proxy/route?q=${encodeURIComponent(k)}`, k)
25835
26023
  };
25836
26024
  }
25837
- var SLED_USAGE = "usage: sleds sled --json | sleds sled top <KEY> | sleds sled order <KEY>…";
26025
+ var SLED_USAGE = "usage: sleds sled --json | sleds sled top <KEY> | sleds sled order <KEY>… | sleds sled off <KEY> [--json]";
25838
26026
  async function sledCmd(o) {
25839
26027
  const log = o.log ?? console.log;
25840
26028
  const error = o.error ?? console.error;
25841
26029
  const top = o.argv[0] === "top" && o.argv.length === 2 && !o.argv[1]?.startsWith("-");
25842
26030
  const keys = o.argv.slice(1);
25843
26031
  const order = o.argv[0] === "order" && keys.length > 0 && keys.every((k) => k.trim() && !k.startsWith("-"));
25844
- if (!top && !order && (o.argv.length !== 1 || o.argv[0] !== "--json")) {
26032
+ const offArgs = o.argv.slice(1).filter((a) => a !== "--json");
26033
+ const off = o.argv[0] === "off" && offArgs.length === 1 && o.argv.length <= 3 && !!offArgs[0]?.trim() && !offArgs[0].startsWith("-");
26034
+ if (!top && !order && !off && (o.argv.length !== 1 || o.argv[0] !== "--json")) {
25845
26035
  error(SLED_USAGE);
25846
26036
  return 2;
25847
26037
  }
@@ -25854,6 +26044,8 @@ async function sledCmd(o) {
25854
26044
  return sledTop(client, o.argv[1], log, error);
25855
26045
  if (order)
25856
26046
  return sledOrder(client, keys, log, error);
26047
+ if (off)
26048
+ return sledOff(client, offArgs[0], o.argv.includes("--json"), log, error);
25857
26049
  try {
25858
26050
  log(JSON.stringify(await readOrSay(client, "/proxy/sled", "the sled")));
25859
26051
  return 0;
@@ -25888,6 +26080,31 @@ async function sledOrder(client, keys, log, error) {
25888
26080
  log(`${typeof r.json?.ordered === "number" ? r.json.ordered : keys.length} ordered on your sled`);
25889
26081
  return 0;
25890
26082
  }
26083
+ async function sledOff(client, key, json, log, error) {
26084
+ const r = await client.postFull("/proxy/sled/off", { project: key });
26085
+ if (!r) {
26086
+ error(`${key}: SLEDS unreachable — the sled is unchanged`);
26087
+ return 1;
26088
+ }
26089
+ if (r.status >= 300 || r.json?.ok === false) {
26090
+ error(typeof r.json?.error === "string" ? r.json.error : `${key}: HTTP ${r.status}`);
26091
+ return 1;
26092
+ }
26093
+ if (json) {
26094
+ log(JSON.stringify(r.json));
26095
+ return 0;
26096
+ }
26097
+ const k = typeof r.json?.key === "string" ? r.json.key : key;
26098
+ if (r.json?.changed === false) {
26099
+ log(`${k} was not on a sled`);
26100
+ return 0;
26101
+ }
26102
+ log(`${k} is off your sled — back in the pool`);
26103
+ const running = typeof r.json?.running === "number" ? r.json.running : 0;
26104
+ if (running > 0)
26105
+ log(`${running} running node${running === 1 ? "" : "s"} stopped`);
26106
+ return 0;
26107
+ }
25891
26108
 
25892
26109
  // src/commands/run.ts
25893
26110
  init_config();
@@ -27406,7 +27623,7 @@ var realPidAlive4 = (pid) => {
27406
27623
  };
27407
27624
  function machineClient(home, env) {
27408
27625
  const src = meterSource(home, undefined, { env });
27409
- return src ? fetchClient({ ...src, version: "0.1.51" }) : null;
27626
+ return src ? fetchClient({ ...src, version: "0.1.52" }) : null;
27410
27627
  }
27411
27628
  async function noteAnswer2(home, a, now) {
27412
27629
  try {
@@ -28026,6 +28243,7 @@ commands:
28026
28243
  sled --json What is on your sled (mine with each route, proposed, sent), for a front end.
28027
28244
  sleds sled top KEY put KEY first on your sled; the engine picks it next
28028
28245
  sleds sled order KEY\u2026 those items in this order, in the places they hold; the rest stay
28246
+ sleds sled off KEY take KEY off your sled, back in the pool; its running nodes stop
28029
28247
  route <key> Print an item's route: its nodes, what each waits on, what is ready, where each
28030
28248
  run can be watched.
28031
28249
  --dot Graphviz instead: \`sleds route QW-142 --dot | dot -Tpng -o route.png\`
@@ -28251,7 +28469,7 @@ var main = async (argv) => {
28251
28469
  now: Date.now(),
28252
28470
  stderr: (line) => console.error(line),
28253
28471
  isTTY: !!process.stderr.isTTY,
28254
- version: "0.1.51"
28472
+ version: "0.1.52"
28255
28473
  });
28256
28474
  switch (cmd) {
28257
28475
  case "update":
@@ -28484,7 +28702,7 @@ ${renderHelpText(argv[1])}`);
28484
28702
  case "version":
28485
28703
  case "--version":
28486
28704
  case "-v":
28487
- console.log("sleds 0.1.51");
28705
+ console.log("sleds 0.1.52");
28488
28706
  return 0;
28489
28707
  default:
28490
28708
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
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",