@sleds/cli 0.1.53 → 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 +206 -77
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -8557,8 +8557,8 @@ function parseParked(v) {
8557
8557
  return {
8558
8558
  question: v.question,
8559
8559
  at: v.at,
8560
- ...typeof v.screen === "string" ? { screen: v.screen } : {},
8561
- ...v.still === true ? { still: true } : {},
8560
+ ...typeof v.words === "string" ? { words: v.words, ...v.still === true ? { still: true } : {} } : {},
8561
+ ...typeof v.seatFull === "string" && v.seatFull ? { seatFull: v.seatFull } : {},
8562
8562
  ...answerer ? { answerer } : {}
8563
8563
  };
8564
8564
  }
@@ -8603,6 +8603,7 @@ function parseRun(v) {
8603
8603
  ...v.permission.onScreen === true ? { onScreen: true } : {},
8604
8604
  ...typeof v.permission.dialog === "string" ? { dialog: v.permission.dialog } : {},
8605
8605
  ...typeof v.permission.pressedAt === "number" ? { pressedAt: v.permission.pressedAt } : {},
8606
+ ...typeof v.permission.pressedOn === "string" ? { pressedOn: v.permission.pressedOn } : {},
8606
8607
  ...typeof v.permission.presses === "number" ? { presses: v.permission.presses } : {},
8607
8608
  ...v.permission.gaveUp === true ? { gaveUp: true } : {}
8608
8609
  }
@@ -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.53" ?? "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) {
@@ -13733,6 +13737,9 @@ function fingerprint(text) {
13733
13737
  }
13734
13738
  return (h >>> 0).toString(16);
13735
13739
  }
13740
+ function wordsPrint(screen) {
13741
+ return fingerprint(stripTuiChrome(screen));
13742
+ }
13736
13743
  function sandboxFor(d, name, cwd, allow) {
13737
13744
  if (!d.sandbox)
13738
13745
  return {};
@@ -13877,14 +13884,18 @@ async function tickOnce(d) {
13877
13884
  try {
13878
13885
  open = runner.status(run.handle) === "running";
13879
13886
  } catch {}
13880
- if (open && await answeredInSession(d, runner, run)) {
13881
- keep.push(run);
13887
+ if (!open && !liveAnswerer(run)) {
13888
+ d.log(`${ref(run)}: the parked session closed — its answer will start a new one`);
13882
13889
  continue;
13883
13890
  }
13884
- if (open || liveAnswerer(run))
13885
- keep.push(run);
13886
- else
13887
- 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);
13888
13899
  continue;
13889
13900
  }
13890
13901
  if (run.pr) {
@@ -13999,7 +14010,7 @@ async function tickOnce(d) {
13999
14010
  if (beat === "parked") {
14000
14011
  const question = "the session parked itself; the question is on the route";
14001
14012
  d.log(`${ref(run)}: parked with a question — the session waits for the answer (${runner.watchHint(run.handle)})`);
14002
- holdParked(d, runner, run, question);
14013
+ holdParked(d, runner, run, question, shown);
14003
14014
  finished.push({ item: run.item, node: run.node, outcome: { kind: "park", question } });
14004
14015
  keep.push(run);
14005
14016
  continue;
@@ -14034,7 +14045,7 @@ async function tickOnce(d) {
14034
14045
  }
14035
14046
  d.log(`${ref(run)}: ${why}, tries spent — parked for a person (${runner.watchHint(run.handle)})`);
14036
14047
  run.provider = undefined;
14037
- holdParked(d, runner, run, question);
14048
+ holdParked(d, runner, run, question, shown);
14038
14049
  finished.push({ item: run.item, node: run.node, outcome });
14039
14050
  keep.push(run);
14040
14051
  continue;
@@ -14055,7 +14066,7 @@ async function tickOnce(d) {
14055
14066
  continue;
14056
14067
  }
14057
14068
  d.log(asked ? `${ref(run)}: stopped on a question with no PR — parked with its last message as the question (${runner.watchHint(run.handle)})` : `${ref(run)}: idle ${d.idleMin} min with no PR — parked with its last message as the question (${runner.watchHint(run.handle)})`);
14058
- holdParked(d, runner, run, question);
14069
+ holdParked(d, runner, run, question, shown);
14059
14070
  finished.push({ item: run.item, node: run.node, outcome });
14060
14071
  keep.push(run);
14061
14072
  continue;
@@ -14081,7 +14092,25 @@ async function tickOnce(d) {
14081
14092
  state.decisions = kept;
14082
14093
  save();
14083
14094
  }
14084
- 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
+ }
14085
14114
  const takeNone = seat.gone;
14086
14115
  for (const it of items)
14087
14116
  if (it.draft && !takeNone && !startsHeld(d))
@@ -14322,18 +14351,23 @@ async function tickOnce(d) {
14322
14351
  started.push({ item: claim.item, node: claim.node, watch, watchTarget: watchTarget(handle) });
14323
14352
  }
14324
14353
  const done = items.length > 0 && items.every((i) => i.landed) && state.runs.length === 0;
14325
- const following = state.runs.filter((r) => r.pr).length;
14326
- const parked = state.runs.filter((r) => r.parked).length;
14327
14354
  return {
14328
14355
  started,
14329
14356
  finished,
14330
- running: state.runs.length - following - parked,
14331
- following,
14357
+ ...counts(state),
14332
14358
  attention,
14333
14359
  done,
14334
14360
  items,
14335
14361
  changes,
14336
- 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,
14337
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 } : {} }] : [])
14338
14372
  };
14339
14373
  }
@@ -14409,7 +14443,7 @@ async function settlePermission(d, runner, run, shown) {
14409
14443
  const dialog = runner.readsSession ? permissionDialog(shown) : null;
14410
14444
  const p = run.permission;
14411
14445
  if (p) {
14412
- const gone = runner.readsSession && (p.onScreen ? !dialog || p.dialog !== undefined && dialogPrint(dialog) !== p.dialog : (run.seenAt ?? 0) > (p.pressedAt ?? p.at));
14446
+ const gone = runner.readsSession && (p.onScreen ? !dialog || p.dialog !== undefined && dialogPrint(dialog) !== p.dialog || p.pressedOn !== undefined && fingerprint(shown) !== p.pressedOn : (run.seenAt ?? 0) > (p.pressedAt ?? p.at));
14413
14447
  const answered = () => {
14414
14448
  run.permission = undefined;
14415
14449
  run.answeredAt = d.now();
@@ -14440,7 +14474,12 @@ async function settlePermission(d, runner, run, shown) {
14440
14474
  if (!pressed)
14441
14475
  return true;
14442
14476
  if (pressed === "key") {
14443
- run.permission = { ...p, pressedAt: d.now(), presses: 1 };
14477
+ run.permission = {
14478
+ ...p,
14479
+ pressedAt: d.now(),
14480
+ pressedOn: fingerprint(shown),
14481
+ presses: 1
14482
+ };
14444
14483
  return true;
14445
14484
  }
14446
14485
  }
@@ -14467,7 +14506,12 @@ async function settlePermission(d, runner, run, shown) {
14467
14506
  return true;
14468
14507
  }
14469
14508
  if (pressAnswer(d, runner, run, p.options, now.answer) === "key")
14470
- run.permission = { ...p, pressedAt: d.now(), presses: presses + 1 };
14509
+ run.permission = {
14510
+ ...p,
14511
+ pressedAt: d.now(),
14512
+ pressedOn: fingerprint(shown),
14513
+ presses: presses + 1
14514
+ };
14471
14515
  return true;
14472
14516
  }
14473
14517
  if (now?.state !== "open" && now?.state !== "escalated") {
@@ -14776,8 +14820,12 @@ function stopOpen(d, run, runner) {
14776
14820
  d.log(`${ref(run)}: could not stop the left-open session: ${message(e)}`);
14777
14821
  }
14778
14822
  }
14779
- function holdParked(d, runner, run, question) {
14780
- run.parked = { question, at: d.now(), ...run.seen ? { screen: run.seen } : {} };
14823
+ function holdParked(d, runner, run, question, shown) {
14824
+ run.parked = {
14825
+ question,
14826
+ at: d.now(),
14827
+ ...runner.readsSession ? { words: wordsPrint(shown) } : {}
14828
+ };
14781
14829
  run.leaseUntil = undefined;
14782
14830
  setLane(d, runner, run.handle, "parked");
14783
14831
  }
@@ -14786,21 +14834,27 @@ async function answeredInSession(d, runner, run) {
14786
14834
  const resumes = resumesOf(d.source);
14787
14835
  if (!resumes || !runner.readsSession || !run.runId)
14788
14836
  return false;
14789
- let print;
14837
+ let shown;
14790
14838
  try {
14791
- print = fingerprint(runner.read(run.handle));
14839
+ shown = runner.read(run.handle);
14792
14840
  } catch {
14793
14841
  return false;
14794
14842
  }
14795
- if (print === parked.screen) {
14796
- parked.still = true;
14797
- return false;
14843
+ const words = wordsPrint(shown);
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;
14798
14857
  }
14799
- const moved = parked.still === true;
14800
- parked.screen = print;
14801
- parked.still = false;
14802
- if (!moved)
14803
- return false;
14804
14858
  let r;
14805
14859
  try {
14806
14860
  r = await resumes.resumeParked(run.runId, "answered in the session", d.engine, d.leaseMin);
@@ -14808,14 +14862,21 @@ async function answeredInSession(d, runner, run) {
14808
14862
  d.log(`${ref(run)}: the parked session moved on, but SLEDS did not hear it: ${message(e)}`);
14809
14863
  return false;
14810
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
+ }
14811
14871
  if (!r.resumed) {
14872
+ parked.seatFull = undefined;
14812
14873
  d.log(`${ref(run)}: the parked session moved on, but the node is no longer parked on its run`);
14813
14874
  return false;
14814
14875
  }
14815
14876
  stopAnswerer(d, run);
14816
14877
  const now = d.now();
14817
14878
  run.parked = undefined;
14818
- run.seen = print;
14879
+ run.seen = fingerprint(shown);
14819
14880
  run.seenAt = now;
14820
14881
  run.seenTicks = 0;
14821
14882
  run.answeredAt = now;
@@ -15185,6 +15246,14 @@ async function endUnposted(d, answers, a, runner, at, over) {
15185
15246
  return false;
15186
15247
  }
15187
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
+ }
15188
15257
  function stopAnswerer(d, run) {
15189
15258
  const a = liveAnswerer(run);
15190
15259
  if (!a)
@@ -16287,6 +16356,41 @@ async function settleEvent(d, events, state, dec, attention, decided) {
16287
16356
 
16288
16357
  // src/commands/engine.ts
16289
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
16290
16394
  init_state();
16291
16395
  init_meter();
16292
16396
 
@@ -17410,7 +17514,7 @@ async function fetchVersion(o) {
17410
17514
  const platform = o.platform ?? process.platform;
17411
17515
  const arch = o.arch ?? process.arch;
17412
17516
  const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
17413
- const running = o.running ?? "0.1.53";
17517
+ const running = o.running ?? "0.1.55";
17414
17518
  if (!publicKey)
17415
17519
  return { ok: false, why: "no release key yet" };
17416
17520
  if (!VERSION_RE2.test(o.version))
@@ -18985,7 +19089,7 @@ async function withProfile(client, flags, given, home, retry) {
18985
19089
  }
18986
19090
  var NOT_SET_UP2 = "SLEDS is not set up on this machine. Run `sleds init` first.";
18987
19091
  var ENGINE_TIMEOUT_MS = 15000;
18988
- function engineClient(o, org) {
19092
+ function engineClient(o, org, breaker) {
18989
19093
  if (o.client)
18990
19094
  return o.client;
18991
19095
  const env = o.env ?? process.env;
@@ -18993,8 +19097,9 @@ function engineClient(o, org) {
18993
19097
  if (src)
18994
19098
  return fetchClient({
18995
19099
  ...src,
19100
+ ...breaker ? { fetchFn: breaker.fetch } : {},
18996
19101
  timeoutMs: ENGINE_TIMEOUT_MS,
18997
- version: "0.1.53"
19102
+ version: "0.1.55"
18998
19103
  });
18999
19104
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
19000
19105
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -19012,9 +19117,9 @@ function engineOrg(o, flag) {
19012
19117
  known.delete(slug);
19013
19118
  return { slug, seated: Boolean(cfg.orgs?.[slug]), sole: known.size === 0 };
19014
19119
  }
19015
- function orgClient(o, flag) {
19120
+ function orgClient(o, flag, breaker) {
19016
19121
  const org = engineOrg(o, flag);
19017
- return engineClient(o, org.seated ? org.slug ?? undefined : flag);
19122
+ return engineClient(o, org.seated ? org.slug ?? undefined : flag, breaker);
19018
19123
  }
19019
19124
  function realPidAlive2(pid) {
19020
19125
  try {
@@ -19285,7 +19390,7 @@ async function driveEngine(o, flags, deps, until = {}) {
19285
19390
  runner: deps.runner.name,
19286
19391
  atOnce: flags.atOnce,
19287
19392
  interval: flags.interval,
19288
- cliVersion: "0.1.53"
19393
+ cliVersion: "0.1.55"
19289
19394
  };
19290
19395
  const beat = (checkedAt) => {
19291
19396
  const m = deps.machine;
@@ -19366,35 +19471,42 @@ async function drive(o, flags, deps, until, beat) {
19366
19471
  await until.beforeFirstTick?.();
19367
19472
  for (;; ) {
19368
19473
  let report = null;
19474
+ let why;
19475
+ until.breaker?.reset();
19369
19476
  try {
19370
19477
  report = await tick(deps);
19478
+ why = report.readFailed;
19371
19479
  } catch (e) {
19480
+ why = e instanceof Error ? e.message : String(e);
19481
+ }
19482
+ if (why !== undefined) {
19372
19483
  down++;
19373
- const why = e instanceof Error ? e.message : String(e);
19374
19484
  if (down === 1 || flags.once)
19375
19485
  event({
19376
19486
  kind: "sleds-down",
19377
19487
  text: why,
19378
19488
  ...flags.once ? {} : { aside: `· retrying every ${flags.interval} s` }
19379
19489
  });
19380
- if (flags.once)
19490
+ if (flags.once && !report)
19381
19491
  return 1;
19382
19492
  }
19383
19493
  beat(now());
19384
- const leave = await until.afterTick?.(report !== null);
19494
+ const leave = await until.afterTick?.(report !== null && !report.readFailed);
19385
19495
  if (typeof leave === "number") {
19386
19496
  if (statusShown)
19387
19497
  write("\r\x1B[2K");
19388
19498
  return leave;
19389
19499
  }
19390
19500
  if (report) {
19391
- if (down > 0)
19392
- event({
19393
- kind: "sleds-back",
19394
- text: "back",
19395
- aside: `after ${down} failed ${down === 1 ? "read" : "reads"}`
19396
- });
19397
- 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
+ }
19398
19510
  running = report.running;
19399
19511
  if (report.seatChanged || report.tokenChanged) {
19400
19512
  if (statusShown)
@@ -19444,6 +19556,8 @@ async function drive(o, flags, deps, until, beat) {
19444
19556
  pr: a.url
19445
19557
  });
19446
19558
  }
19559
+ if (flags.once && report.readFailed)
19560
+ return 1;
19447
19561
  if (until.done && report.done) {
19448
19562
  if (statusShown)
19449
19563
  write("\r\x1B[2K");
@@ -21133,7 +21247,7 @@ async function withProfile2(client, flags, given, home, retry) {
21133
21247
  }
21134
21248
  var NOT_SET_UP3 = "SLEDS is not set up on this machine. Run `sleds init` first.";
21135
21249
  var ENGINE_TIMEOUT_MS2 = 15000;
21136
- function engineClient2(o, org) {
21250
+ function engineClient2(o, org, breaker) {
21137
21251
  if (o.client)
21138
21252
  return o.client;
21139
21253
  const env = o.env ?? process.env;
@@ -21141,8 +21255,9 @@ function engineClient2(o, org) {
21141
21255
  if (src)
21142
21256
  return fetchClient({
21143
21257
  ...src,
21258
+ ...breaker ? { fetchFn: breaker.fetch } : {},
21144
21259
  timeoutMs: ENGINE_TIMEOUT_MS2,
21145
- version: "0.1.53"
21260
+ version: "0.1.55"
21146
21261
  });
21147
21262
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
21148
21263
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -21160,9 +21275,9 @@ function engineOrg2(o, flag) {
21160
21275
  known.delete(slug);
21161
21276
  return { slug, seated: Boolean(cfg.orgs?.[slug]), sole: known.size === 0 };
21162
21277
  }
21163
- function orgClient2(o, flag) {
21278
+ function orgClient2(o, flag, breaker) {
21164
21279
  const org = engineOrg2(o, flag);
21165
- return engineClient2(o, org.seated ? org.slug ?? undefined : flag);
21280
+ return engineClient2(o, org.seated ? org.slug ?? undefined : flag, breaker);
21166
21281
  }
21167
21282
  async function jiraWritesAtStart(gate, epic) {
21168
21283
  const no = await gate.ask({
@@ -21468,7 +21583,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
21468
21583
  runner: deps.runner.name,
21469
21584
  atOnce: flags.atOnce,
21470
21585
  interval: flags.interval,
21471
- cliVersion: "0.1.53"
21586
+ cliVersion: "0.1.55"
21472
21587
  };
21473
21588
  const beat = (checkedAt) => {
21474
21589
  const m = deps.machine;
@@ -21549,35 +21664,42 @@ async function drive2(o, flags, deps, until, beat) {
21549
21664
  await until.beforeFirstTick?.();
21550
21665
  for (;; ) {
21551
21666
  let report = null;
21667
+ let why;
21668
+ until.breaker?.reset();
21552
21669
  try {
21553
21670
  report = await tick(deps);
21671
+ why = report.readFailed;
21554
21672
  } catch (e) {
21673
+ why = e instanceof Error ? e.message : String(e);
21674
+ }
21675
+ if (why !== undefined) {
21555
21676
  down++;
21556
- const why = e instanceof Error ? e.message : String(e);
21557
21677
  if (down === 1 || flags.once)
21558
21678
  event({
21559
21679
  kind: "sleds-down",
21560
21680
  text: why,
21561
21681
  ...flags.once ? {} : { aside: `· retrying every ${flags.interval} s` }
21562
21682
  });
21563
- if (flags.once)
21683
+ if (flags.once && !report)
21564
21684
  return 1;
21565
21685
  }
21566
21686
  beat(now());
21567
- const leave = await until.afterTick?.(report !== null);
21687
+ const leave = await until.afterTick?.(report !== null && !report.readFailed);
21568
21688
  if (typeof leave === "number") {
21569
21689
  if (statusShown)
21570
21690
  write("\r\x1B[2K");
21571
21691
  return leave;
21572
21692
  }
21573
21693
  if (report) {
21574
- if (down > 0)
21575
- event({
21576
- kind: "sleds-back",
21577
- text: "back",
21578
- aside: `after ${down} failed ${down === 1 ? "read" : "reads"}`
21579
- });
21580
- 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
+ }
21581
21703
  running = report.running;
21582
21704
  if (report.seatChanged || report.tokenChanged) {
21583
21705
  if (statusShown)
@@ -21627,6 +21749,8 @@ async function drive2(o, flags, deps, until, beat) {
21627
21749
  pr: a.url
21628
21750
  });
21629
21751
  }
21752
+ if (flags.once && report.readFailed)
21753
+ return 1;
21630
21754
  if (until.done && report.done) {
21631
21755
  if (statusShown)
21632
21756
  write("\r\x1B[2K");
@@ -21702,17 +21826,22 @@ var engineCmd = async (o) => {
21702
21826
  error(kept.refused);
21703
21827
  return 1;
21704
21828
  }
21829
+ const breaker = sledsBreaker(o.fetch);
21705
21830
  let client;
21706
21831
  let pickup;
21707
21832
  if (!flags.jira && (o.client || !o.source)) {
21708
- const c = orgClient2(o, given);
21833
+ const c = orgClient2(o, given, breaker);
21709
21834
  if ("error" in c) {
21710
21835
  error(c.error);
21711
21836
  return 1;
21712
21837
  }
21713
21838
  client = c;
21839
+ const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
21714
21840
  const p = await withProfile2(c, flags, parsed.given, o.home, {
21715
- sleep: o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)))
21841
+ sleep: async (ms) => {
21842
+ await sleep(ms);
21843
+ breaker.reset();
21844
+ }
21716
21845
  });
21717
21846
  flags = p.flags;
21718
21847
  pickup = p.pickup;
@@ -21748,7 +21877,7 @@ var engineCmd = async (o) => {
21748
21877
  error(missing);
21749
21878
  return 1;
21750
21879
  }
21751
- const c = o.client ?? orgClient2(o, given);
21880
+ const c = o.client ?? orgClient2(o, given, breaker);
21752
21881
  if ("error" in c) {
21753
21882
  error(`--jira asks SLEDS before every write to Jira (spec 39), so it needs a seat: ${c.error}`);
21754
21883
  return 1;
@@ -21881,7 +22010,7 @@ var engineCmd = async (o) => {
21881
22010
  };
21882
22011
  }
21883
22012
  try {
21884
- return await driveEngine2(o, flags, deps, { afterTick, beforeFirstTick });
22013
+ return await driveEngine2(o, flags, deps, { afterTick, beforeFirstTick, breaker });
21885
22014
  } finally {
21886
22015
  watch.stop();
21887
22016
  }
@@ -23556,7 +23685,7 @@ function contextFor(deps) {
23556
23685
  ...src,
23557
23686
  fetchFn: deps.fetchFn,
23558
23687
  timeoutMs: deps.timeoutMs,
23559
- version: "0.1.53"
23688
+ version: "0.1.55"
23560
23689
  }), deps.home, now) : null;
23561
23690
  return {
23562
23691
  channel: "code",
@@ -26281,7 +26410,7 @@ function parseSeatRevokeArgs(argv) {
26281
26410
  }
26282
26411
  return { box, yes, json };
26283
26412
  }
26284
- function why(j, status) {
26413
+ function why2(j, status) {
26285
26414
  if (typeof j?.reason === "string")
26286
26415
  return j.reason;
26287
26416
  if (typeof j?.error === "string")
@@ -26319,7 +26448,7 @@ async function seatRevokeCmd(o) {
26319
26448
  return ok ? 0 : 1;
26320
26449
  }
26321
26450
  if (!ok) {
26322
- error(clean(`nothing revoked — ${why(r.json, r.status)}`));
26451
+ error(clean(`nothing revoked — ${why2(r.json, r.status)}`));
26323
26452
  return 1;
26324
26453
  }
26325
26454
  const label = typeof r.json?.label === "string" ? r.json.label : a.box ? `box:${a.box}` : "";
@@ -27674,7 +27803,7 @@ var realPidAlive4 = (pid) => {
27674
27803
  };
27675
27804
  function machineClient(home, env) {
27676
27805
  const src = meterSource(home, undefined, { env });
27677
- return src ? fetchClient({ ...src, version: "0.1.53" }) : null;
27806
+ return src ? fetchClient({ ...src, version: "0.1.55" }) : null;
27678
27807
  }
27679
27808
  async function noteAnswer2(home, a, now) {
27680
27809
  try {
@@ -28534,7 +28663,7 @@ var main = async (argv) => {
28534
28663
  now: Date.now(),
28535
28664
  stderr: (line) => console.error(line),
28536
28665
  isTTY: !!process.stderr.isTTY,
28537
- version: "0.1.53"
28666
+ version: "0.1.55"
28538
28667
  });
28539
28668
  switch (cmd) {
28540
28669
  case "update":
@@ -28767,7 +28896,7 @@ ${renderHelpText(argv[1])}`);
28767
28896
  case "version":
28768
28897
  case "--version":
28769
28898
  case "-v":
28770
- console.log("sleds 0.1.53");
28899
+ console.log("sleds 0.1.55");
28771
28900
  return 0;
28772
28901
  default:
28773
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.53",
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",