@sleds/cli 0.1.54 → 0.1.55

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 +175 -64
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -8558,6 +8558,7 @@ function parseParked(v) {
8558
8558
  question: v.question,
8559
8559
  at: v.at,
8560
8560
  ...typeof v.words === "string" ? { words: v.words, ...v.still === true ? { still: true } : {} } : {},
8561
+ ...typeof v.seatFull === "string" && v.seatFull ? { seatFull: v.seatFull } : {},
8561
8562
  ...answerer ? { answerer } : {}
8562
8563
  };
8563
8564
  }
@@ -11535,7 +11536,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
11535
11536
  name: flags.name,
11536
11537
  os: host.os ?? process.platform,
11537
11538
  runner: flags.runner,
11538
- version: host.version ?? "0.1.54" ?? "dev",
11539
+ version: host.version ?? "0.1.55" ?? "dev",
11539
11540
  running: live.length,
11540
11541
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
11541
11542
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -13207,9 +13208,12 @@ function sledsSource(client, o) {
13207
13208
  if (r.status >= 300 || !r.json || r.json.ok === false)
13208
13209
  throw new Error(serverMessage(r));
13209
13210
  const leaseUntil = typeof r.json.leaseUntil === "number" ? r.json.leaseUntil : undefined;
13211
+ const resumed = r.json.resumed === true;
13212
+ const seatFull = !resumed && typeof r.json.seatFull === "string" ? r.json.seatFull : "";
13210
13213
  return {
13211
- resumed: r.json.resumed === true,
13212
- ...leaseUntil !== undefined ? { leaseUntil } : {}
13214
+ resumed,
13215
+ ...leaseUntil !== undefined ? { leaseUntil } : {},
13216
+ ...seatFull ? { seatFull } : {}
13213
13217
  };
13214
13218
  },
13215
13219
  async decisionOf(item, decisionId) {
@@ -13880,14 +13884,18 @@ async function tickOnce(d) {
13880
13884
  try {
13881
13885
  open = runner.status(run.handle) === "running";
13882
13886
  } catch {}
13883
- if (open && await answeredInSession(d, runner, run)) {
13884
- keep.push(run);
13887
+ if (!open && !liveAnswerer(run)) {
13888
+ d.log(`${ref(run)}: the parked session closed — its answer will start a new one`);
13885
13889
  continue;
13886
13890
  }
13887
- if (open || liveAnswerer(run))
13888
- keep.push(run);
13889
- else
13890
- d.log(`${ref(run)}: the parked session closed — its answer will start a new one`);
13891
+ if (await parkedBeat(d, run, claim) === "done") {
13892
+ d.log(`${ref(run)}: no longer waits for an answer — its node was skipped`);
13893
+ stopAnswerer(d, run);
13894
+ continue;
13895
+ }
13896
+ if (open)
13897
+ await answeredInSession(d, runner, run);
13898
+ keep.push(run);
13891
13899
  continue;
13892
13900
  }
13893
13901
  if (run.pr) {
@@ -14084,7 +14092,25 @@ async function tickOnce(d) {
14084
14092
  state.decisions = kept;
14085
14093
  save();
14086
14094
  }
14087
- const items = await d.source.read();
14095
+ let items;
14096
+ try {
14097
+ items = await d.source.read();
14098
+ } catch (e) {
14099
+ if (e instanceof SeatTokenError)
14100
+ throw e;
14101
+ save();
14102
+ return {
14103
+ started: [],
14104
+ finished,
14105
+ ...counts(state),
14106
+ attention,
14107
+ done: false,
14108
+ items: [],
14109
+ changes,
14110
+ decisions: decided,
14111
+ readFailed: message(e)
14112
+ };
14113
+ }
14088
14114
  const takeNone = seat.gone;
14089
14115
  for (const it of items)
14090
14116
  if (it.draft && !takeNone && !startsHeld(d))
@@ -14325,18 +14351,23 @@ async function tickOnce(d) {
14325
14351
  started.push({ item: claim.item, node: claim.node, watch, watchTarget: watchTarget(handle) });
14326
14352
  }
14327
14353
  const done = items.length > 0 && items.every((i) => i.landed) && state.runs.length === 0;
14328
- const following = state.runs.filter((r) => r.pr).length;
14329
- const parked = state.runs.filter((r) => r.parked).length;
14330
14354
  return {
14331
14355
  started,
14332
14356
  finished,
14333
- running: state.runs.length - following - parked,
14334
- following,
14357
+ ...counts(state),
14335
14358
  attention,
14336
14359
  done,
14337
14360
  items,
14338
14361
  changes,
14339
- decisions: decided,
14362
+ decisions: decided
14363
+ };
14364
+ }
14365
+ function counts(state) {
14366
+ const following = state.runs.filter((r) => r.pr).length;
14367
+ const parked = state.runs.filter((r) => r.parked).length;
14368
+ return {
14369
+ running: state.runs.length - following - parked,
14370
+ following,
14340
14371
  followed: state.runs.flatMap((r) => r.pr ? [{ item: r.item, node: r.node, url: r.pr.url, ...r.pr.ci ? { ci: r.pr.ci } : {} }] : [])
14341
14372
  };
14342
14373
  }
@@ -14810,15 +14841,20 @@ async function answeredInSession(d, runner, run) {
14810
14841
  return false;
14811
14842
  }
14812
14843
  const words = wordsPrint(shown);
14813
- if (words === parked.words) {
14814
- parked.still = true;
14815
- return false;
14844
+ const waiting = parked.seatFull !== undefined;
14845
+ if (waiting) {
14846
+ parked.words = words;
14847
+ } else {
14848
+ if (words === parked.words) {
14849
+ parked.still = true;
14850
+ return false;
14851
+ }
14852
+ const moved = parked.still === true;
14853
+ parked.words = words;
14854
+ parked.still = false;
14855
+ if (!moved)
14856
+ return false;
14816
14857
  }
14817
- const moved = parked.still === true;
14818
- parked.words = words;
14819
- parked.still = false;
14820
- if (!moved)
14821
- return false;
14822
14858
  let r;
14823
14859
  try {
14824
14860
  r = await resumes.resumeParked(run.runId, "answered in the session", d.engine, d.leaseMin);
@@ -14826,7 +14862,14 @@ async function answeredInSession(d, runner, run) {
14826
14862
  d.log(`${ref(run)}: the parked session moved on, but SLEDS did not hear it: ${message(e)}`);
14827
14863
  return false;
14828
14864
  }
14865
+ if (!r.resumed && r.seatFull) {
14866
+ if (!waiting)
14867
+ d.log(`${ref(run)}: answered in the session, but ${r.seatFull} — stays in Needs you until a slot frees`);
14868
+ parked.seatFull = r.seatFull;
14869
+ return false;
14870
+ }
14829
14871
  if (!r.resumed) {
14872
+ parked.seatFull = undefined;
14830
14873
  d.log(`${ref(run)}: the parked session moved on, but the node is no longer parked on its run`);
14831
14874
  return false;
14832
14875
  }
@@ -15203,6 +15246,14 @@ async function endUnposted(d, answers, a, runner, at, over) {
15203
15246
  return false;
15204
15247
  }
15205
15248
  }
15249
+ async function parkedBeat(d, run, claim) {
15250
+ try {
15251
+ return await d.source.heartbeat(claim);
15252
+ } catch (e) {
15253
+ d.log(`${ref(run)}: heartbeat failed, retrying next tick: ${message(e)}`);
15254
+ return "running";
15255
+ }
15256
+ }
15206
15257
  function stopAnswerer(d, run) {
15207
15258
  const a = liveAnswerer(run);
15208
15259
  if (!a)
@@ -16305,6 +16356,41 @@ async function settleEvent(d, events, state, dec, attention, decided) {
16305
16356
 
16306
16357
  // src/commands/engine.ts
16307
16358
  init_machine();
16359
+
16360
+ // src/engine/sledsBreaker.ts
16361
+ var SLEDS_DOWN_ERROR = "SledsDownError";
16362
+ function why(e) {
16363
+ const name = e instanceof Error ? e.name : "";
16364
+ return name === "TimeoutError" || name === "AbortError" ? "timed out" : "could not be reached";
16365
+ }
16366
+ function sledsBreaker(fetchFn = fetch) {
16367
+ let down = null;
16368
+ let pass = 0;
16369
+ return {
16370
+ async fetch(input, init) {
16371
+ if (down !== null) {
16372
+ const e = new Error(`skipped — SLEDS ${down} earlier this tick`);
16373
+ e.name = SLEDS_DOWN_ERROR;
16374
+ throw e;
16375
+ }
16376
+ const started = pass;
16377
+ try {
16378
+ return await fetchFn(input, init);
16379
+ } catch (e) {
16380
+ if (started === pass && down === null)
16381
+ down = why(e);
16382
+ throw e;
16383
+ }
16384
+ },
16385
+ reset() {
16386
+ down = null;
16387
+ pass++;
16388
+ },
16389
+ tripped: () => down
16390
+ };
16391
+ }
16392
+
16393
+ // src/commands/engine.ts
16308
16394
  init_state();
16309
16395
  init_meter();
16310
16396
 
@@ -17428,7 +17514,7 @@ async function fetchVersion(o) {
17428
17514
  const platform = o.platform ?? process.platform;
17429
17515
  const arch = o.arch ?? process.arch;
17430
17516
  const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
17431
- const running = o.running ?? "0.1.54";
17517
+ const running = o.running ?? "0.1.55";
17432
17518
  if (!publicKey)
17433
17519
  return { ok: false, why: "no release key yet" };
17434
17520
  if (!VERSION_RE2.test(o.version))
@@ -19003,7 +19089,7 @@ async function withProfile(client, flags, given, home, retry) {
19003
19089
  }
19004
19090
  var NOT_SET_UP2 = "SLEDS is not set up on this machine. Run `sleds init` first.";
19005
19091
  var ENGINE_TIMEOUT_MS = 15000;
19006
- function engineClient(o, org) {
19092
+ function engineClient(o, org, breaker) {
19007
19093
  if (o.client)
19008
19094
  return o.client;
19009
19095
  const env = o.env ?? process.env;
@@ -19011,8 +19097,9 @@ function engineClient(o, org) {
19011
19097
  if (src)
19012
19098
  return fetchClient({
19013
19099
  ...src,
19100
+ ...breaker ? { fetchFn: breaker.fetch } : {},
19014
19101
  timeoutMs: ENGINE_TIMEOUT_MS,
19015
- version: "0.1.54"
19102
+ version: "0.1.55"
19016
19103
  });
19017
19104
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
19018
19105
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -19030,9 +19117,9 @@ function engineOrg(o, flag) {
19030
19117
  known.delete(slug);
19031
19118
  return { slug, seated: Boolean(cfg.orgs?.[slug]), sole: known.size === 0 };
19032
19119
  }
19033
- function orgClient(o, flag) {
19120
+ function orgClient(o, flag, breaker) {
19034
19121
  const org = engineOrg(o, flag);
19035
- return engineClient(o, org.seated ? org.slug ?? undefined : flag);
19122
+ return engineClient(o, org.seated ? org.slug ?? undefined : flag, breaker);
19036
19123
  }
19037
19124
  function realPidAlive2(pid) {
19038
19125
  try {
@@ -19303,7 +19390,7 @@ async function driveEngine(o, flags, deps, until = {}) {
19303
19390
  runner: deps.runner.name,
19304
19391
  atOnce: flags.atOnce,
19305
19392
  interval: flags.interval,
19306
- cliVersion: "0.1.54"
19393
+ cliVersion: "0.1.55"
19307
19394
  };
19308
19395
  const beat = (checkedAt) => {
19309
19396
  const m = deps.machine;
@@ -19384,35 +19471,42 @@ async function drive(o, flags, deps, until, beat) {
19384
19471
  await until.beforeFirstTick?.();
19385
19472
  for (;; ) {
19386
19473
  let report = null;
19474
+ let why;
19475
+ until.breaker?.reset();
19387
19476
  try {
19388
19477
  report = await tick(deps);
19478
+ why = report.readFailed;
19389
19479
  } catch (e) {
19480
+ why = e instanceof Error ? e.message : String(e);
19481
+ }
19482
+ if (why !== undefined) {
19390
19483
  down++;
19391
- const why = e instanceof Error ? e.message : String(e);
19392
19484
  if (down === 1 || flags.once)
19393
19485
  event({
19394
19486
  kind: "sleds-down",
19395
19487
  text: why,
19396
19488
  ...flags.once ? {} : { aside: `· retrying every ${flags.interval} s` }
19397
19489
  });
19398
- if (flags.once)
19490
+ if (flags.once && !report)
19399
19491
  return 1;
19400
19492
  }
19401
19493
  beat(now());
19402
- const leave = await until.afterTick?.(report !== null);
19494
+ const leave = await until.afterTick?.(report !== null && !report.readFailed);
19403
19495
  if (typeof leave === "number") {
19404
19496
  if (statusShown)
19405
19497
  write("\r\x1B[2K");
19406
19498
  return leave;
19407
19499
  }
19408
19500
  if (report) {
19409
- if (down > 0)
19410
- event({
19411
- kind: "sleds-back",
19412
- text: "back",
19413
- aside: `after ${down} failed ${down === 1 ? "read" : "reads"}`
19414
- });
19415
- down = 0;
19501
+ if (!report.readFailed) {
19502
+ if (down > 0)
19503
+ event({
19504
+ kind: "sleds-back",
19505
+ text: "back",
19506
+ aside: `after ${down} failed ${down === 1 ? "read" : "reads"}`
19507
+ });
19508
+ down = 0;
19509
+ }
19416
19510
  running = report.running;
19417
19511
  if (report.seatChanged || report.tokenChanged) {
19418
19512
  if (statusShown)
@@ -19462,6 +19556,8 @@ async function drive(o, flags, deps, until, beat) {
19462
19556
  pr: a.url
19463
19557
  });
19464
19558
  }
19559
+ if (flags.once && report.readFailed)
19560
+ return 1;
19465
19561
  if (until.done && report.done) {
19466
19562
  if (statusShown)
19467
19563
  write("\r\x1B[2K");
@@ -21151,7 +21247,7 @@ async function withProfile2(client, flags, given, home, retry) {
21151
21247
  }
21152
21248
  var NOT_SET_UP3 = "SLEDS is not set up on this machine. Run `sleds init` first.";
21153
21249
  var ENGINE_TIMEOUT_MS2 = 15000;
21154
- function engineClient2(o, org) {
21250
+ function engineClient2(o, org, breaker) {
21155
21251
  if (o.client)
21156
21252
  return o.client;
21157
21253
  const env = o.env ?? process.env;
@@ -21159,8 +21255,9 @@ function engineClient2(o, org) {
21159
21255
  if (src)
21160
21256
  return fetchClient({
21161
21257
  ...src,
21258
+ ...breaker ? { fetchFn: breaker.fetch } : {},
21162
21259
  timeoutMs: ENGINE_TIMEOUT_MS2,
21163
- version: "0.1.54"
21260
+ version: "0.1.55"
21164
21261
  });
21165
21262
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
21166
21263
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -21178,9 +21275,9 @@ function engineOrg2(o, flag) {
21178
21275
  known.delete(slug);
21179
21276
  return { slug, seated: Boolean(cfg.orgs?.[slug]), sole: known.size === 0 };
21180
21277
  }
21181
- function orgClient2(o, flag) {
21278
+ function orgClient2(o, flag, breaker) {
21182
21279
  const org = engineOrg2(o, flag);
21183
- return engineClient2(o, org.seated ? org.slug ?? undefined : flag);
21280
+ return engineClient2(o, org.seated ? org.slug ?? undefined : flag, breaker);
21184
21281
  }
21185
21282
  async function jiraWritesAtStart(gate, epic) {
21186
21283
  const no = await gate.ask({
@@ -21486,7 +21583,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
21486
21583
  runner: deps.runner.name,
21487
21584
  atOnce: flags.atOnce,
21488
21585
  interval: flags.interval,
21489
- cliVersion: "0.1.54"
21586
+ cliVersion: "0.1.55"
21490
21587
  };
21491
21588
  const beat = (checkedAt) => {
21492
21589
  const m = deps.machine;
@@ -21567,35 +21664,42 @@ async function drive2(o, flags, deps, until, beat) {
21567
21664
  await until.beforeFirstTick?.();
21568
21665
  for (;; ) {
21569
21666
  let report = null;
21667
+ let why;
21668
+ until.breaker?.reset();
21570
21669
  try {
21571
21670
  report = await tick(deps);
21671
+ why = report.readFailed;
21572
21672
  } catch (e) {
21673
+ why = e instanceof Error ? e.message : String(e);
21674
+ }
21675
+ if (why !== undefined) {
21573
21676
  down++;
21574
- const why = e instanceof Error ? e.message : String(e);
21575
21677
  if (down === 1 || flags.once)
21576
21678
  event({
21577
21679
  kind: "sleds-down",
21578
21680
  text: why,
21579
21681
  ...flags.once ? {} : { aside: `· retrying every ${flags.interval} s` }
21580
21682
  });
21581
- if (flags.once)
21683
+ if (flags.once && !report)
21582
21684
  return 1;
21583
21685
  }
21584
21686
  beat(now());
21585
- const leave = await until.afterTick?.(report !== null);
21687
+ const leave = await until.afterTick?.(report !== null && !report.readFailed);
21586
21688
  if (typeof leave === "number") {
21587
21689
  if (statusShown)
21588
21690
  write("\r\x1B[2K");
21589
21691
  return leave;
21590
21692
  }
21591
21693
  if (report) {
21592
- if (down > 0)
21593
- event({
21594
- kind: "sleds-back",
21595
- text: "back",
21596
- aside: `after ${down} failed ${down === 1 ? "read" : "reads"}`
21597
- });
21598
- down = 0;
21694
+ if (!report.readFailed) {
21695
+ if (down > 0)
21696
+ event({
21697
+ kind: "sleds-back",
21698
+ text: "back",
21699
+ aside: `after ${down} failed ${down === 1 ? "read" : "reads"}`
21700
+ });
21701
+ down = 0;
21702
+ }
21599
21703
  running = report.running;
21600
21704
  if (report.seatChanged || report.tokenChanged) {
21601
21705
  if (statusShown)
@@ -21645,6 +21749,8 @@ async function drive2(o, flags, deps, until, beat) {
21645
21749
  pr: a.url
21646
21750
  });
21647
21751
  }
21752
+ if (flags.once && report.readFailed)
21753
+ return 1;
21648
21754
  if (until.done && report.done) {
21649
21755
  if (statusShown)
21650
21756
  write("\r\x1B[2K");
@@ -21720,17 +21826,22 @@ var engineCmd = async (o) => {
21720
21826
  error(kept.refused);
21721
21827
  return 1;
21722
21828
  }
21829
+ const breaker = sledsBreaker(o.fetch);
21723
21830
  let client;
21724
21831
  let pickup;
21725
21832
  if (!flags.jira && (o.client || !o.source)) {
21726
- const c = orgClient2(o, given);
21833
+ const c = orgClient2(o, given, breaker);
21727
21834
  if ("error" in c) {
21728
21835
  error(c.error);
21729
21836
  return 1;
21730
21837
  }
21731
21838
  client = c;
21839
+ const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
21732
21840
  const p = await withProfile2(c, flags, parsed.given, o.home, {
21733
- sleep: o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)))
21841
+ sleep: async (ms) => {
21842
+ await sleep(ms);
21843
+ breaker.reset();
21844
+ }
21734
21845
  });
21735
21846
  flags = p.flags;
21736
21847
  pickup = p.pickup;
@@ -21766,7 +21877,7 @@ var engineCmd = async (o) => {
21766
21877
  error(missing);
21767
21878
  return 1;
21768
21879
  }
21769
- const c = o.client ?? orgClient2(o, given);
21880
+ const c = o.client ?? orgClient2(o, given, breaker);
21770
21881
  if ("error" in c) {
21771
21882
  error(`--jira asks SLEDS before every write to Jira (spec 39), so it needs a seat: ${c.error}`);
21772
21883
  return 1;
@@ -21899,7 +22010,7 @@ var engineCmd = async (o) => {
21899
22010
  };
21900
22011
  }
21901
22012
  try {
21902
- return await driveEngine2(o, flags, deps, { afterTick, beforeFirstTick });
22013
+ return await driveEngine2(o, flags, deps, { afterTick, beforeFirstTick, breaker });
21903
22014
  } finally {
21904
22015
  watch.stop();
21905
22016
  }
@@ -23574,7 +23685,7 @@ function contextFor(deps) {
23574
23685
  ...src,
23575
23686
  fetchFn: deps.fetchFn,
23576
23687
  timeoutMs: deps.timeoutMs,
23577
- version: "0.1.54"
23688
+ version: "0.1.55"
23578
23689
  }), deps.home, now) : null;
23579
23690
  return {
23580
23691
  channel: "code",
@@ -26299,7 +26410,7 @@ function parseSeatRevokeArgs(argv) {
26299
26410
  }
26300
26411
  return { box, yes, json };
26301
26412
  }
26302
- function why(j, status) {
26413
+ function why2(j, status) {
26303
26414
  if (typeof j?.reason === "string")
26304
26415
  return j.reason;
26305
26416
  if (typeof j?.error === "string")
@@ -26337,7 +26448,7 @@ async function seatRevokeCmd(o) {
26337
26448
  return ok ? 0 : 1;
26338
26449
  }
26339
26450
  if (!ok) {
26340
- error(clean(`nothing revoked — ${why(r.json, r.status)}`));
26451
+ error(clean(`nothing revoked — ${why2(r.json, r.status)}`));
26341
26452
  return 1;
26342
26453
  }
26343
26454
  const label = typeof r.json?.label === "string" ? r.json.label : a.box ? `box:${a.box}` : "";
@@ -27692,7 +27803,7 @@ var realPidAlive4 = (pid) => {
27692
27803
  };
27693
27804
  function machineClient(home, env) {
27694
27805
  const src = meterSource(home, undefined, { env });
27695
- return src ? fetchClient({ ...src, version: "0.1.54" }) : null;
27806
+ return src ? fetchClient({ ...src, version: "0.1.55" }) : null;
27696
27807
  }
27697
27808
  async function noteAnswer2(home, a, now) {
27698
27809
  try {
@@ -28552,7 +28663,7 @@ var main = async (argv) => {
28552
28663
  now: Date.now(),
28553
28664
  stderr: (line) => console.error(line),
28554
28665
  isTTY: !!process.stderr.isTTY,
28555
- version: "0.1.54"
28666
+ version: "0.1.55"
28556
28667
  });
28557
28668
  switch (cmd) {
28558
28669
  case "update":
@@ -28785,7 +28896,7 @@ ${renderHelpText(argv[1])}`);
28785
28896
  case "version":
28786
28897
  case "--version":
28787
28898
  case "-v":
28788
- console.log("sleds 0.1.54");
28899
+ console.log("sleds 0.1.55");
28789
28900
  return 0;
28790
28901
  default:
28791
28902
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.54",
3
+ "version": "0.1.55",
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",