@sleds/cli 0.1.48 → 0.1.49

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 +264 -60
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -4246,7 +4246,7 @@ async function actualTool(ctx, args, to = { path: "/proxy/estimate/actual" }) {
4246
4246
  const from = str5(r.elapsedFrom);
4247
4247
  const unsized = r.unsized === true;
4248
4248
  const bits = [
4249
- recorded !== undefined ? `${recorded} min${from ? ` measured from the ${from}` : ""}${num3(r.saidMinutes) !== undefined && num3(r.saidMinutes) !== recorded ? ` (you said ${num3(r.saidMinutes)})` : ""}` : phases ? "phases recorded" : null,
4249
+ recorded !== undefined ? `${recorded} min${from === "runs" ? " of engine run time" : from ? ` measured from the ${from}` : ""}${num3(r.saidMinutes) !== undefined && num3(r.saidMinutes) !== recorded ? ` (you said ${num3(r.saidMinutes)})` : ""}` : phases ? "phases recorded" : null,
4250
4250
  num3(args.usd) !== undefined ? usd2(num3(args.usd)) : args.usd === null ? "$ cleared" : null,
4251
4251
  num3(args.tokensIn) !== undefined || num3(args.tokensOut) !== undefined ? `${(num3(args.tokensIn) ?? 0) + (num3(args.tokensOut) ?? 0)} tokens` : null,
4252
4252
  str5(args.model),
@@ -9951,8 +9951,8 @@ import {
9951
9951
  rmSync as rmSync13,
9952
9952
  writeFileSync as writeFileSync19
9953
9953
  } from "node:fs";
9954
- import { tmpdir } from "node:os";
9955
- import { delimiter as delimiter3, dirname as dirname16, join as join25, resolve as resolve9 } from "node:path";
9954
+ import { tmpdir, userInfo as userInfo2 } from "node:os";
9955
+ import { delimiter as delimiter3, dirname as dirname17, join as join25, resolve as resolve9 } from "node:path";
9956
9956
 
9957
9957
  // src/agentMcp.ts
9958
9958
  import { execFileSync as execFileSync3 } from "node:child_process";
@@ -11161,10 +11161,11 @@ import {
11161
11161
  readFileSync as readFileSync15,
11162
11162
  existsSync as realExists,
11163
11163
  rmSync as rmSync10,
11164
+ statSync as statSync5,
11164
11165
  writeFileSync as writeFileSync16
11165
11166
  } from "node:fs";
11166
- import { hostname as hostname2 } from "node:os";
11167
- import { join as join23, resolve as resolve8 } from "node:path";
11167
+ import { hostname as hostname2, userInfo } from "node:os";
11168
+ import { dirname as dirname15, isAbsolute as isAbsolute3, join as join23, resolve as resolve8 } from "node:path";
11168
11169
 
11169
11170
  // src/engine/beat.ts
11170
11171
  var BEAT_EVERY_MS = 60000;
@@ -11176,7 +11177,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
11176
11177
  name: flags.name,
11177
11178
  os: host.os ?? process.platform,
11178
11179
  runner: flags.runner,
11179
- version: host.version ?? "0.1.48" ?? "dev",
11180
+ version: host.version ?? "0.1.49" ?? "dev",
11180
11181
  running: live.length,
11181
11182
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
11182
11183
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -16900,7 +16901,7 @@ async function fetchVersion(o) {
16900
16901
  const platform = o.platform ?? process.platform;
16901
16902
  const arch = o.arch ?? process.arch;
16902
16903
  const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
16903
- const running = o.running ?? "0.1.48";
16904
+ const running = o.running ?? "0.1.49";
16904
16905
  if (!publicKey)
16905
16906
  return { ok: false, why: "no release key yet" };
16906
16907
  if (!VERSION_RE2.test(o.version))
@@ -18166,10 +18167,11 @@ var VALUE_FLAGS = [
18166
18167
  "hold-cpu",
18167
18168
  "hold-load",
18168
18169
  "hold-pressure",
18169
- "hold"
18170
+ "hold",
18171
+ "lent-by"
18170
18172
  ];
18171
18173
  var BOOL_FLAGS = ["once", "strict-mcp", "no-strict-mcp", "sandbox", "no-sandbox"];
18172
- var ENGINE_USAGE = "usage: sleds engine [--name <n>] [--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[:dir…]>] [--jira <EPIC>] [--mode <permission-mode>] [--model <id>] [--decider-model <id>] [--effort <e>] [--decider-effort <e>] [--drafter-model <id>] [--drafter-effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--events <file>] [--org <slug>] [--strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp] [--sandbox [--sandbox-allow <a,b>] | --no-sandbox] [--hold-cpu <%>] [--hold-load <per-core>] [--hold-pressure warn|critical] [--hold on|off] [--once]";
18174
+ var ENGINE_USAGE = "usage: sleds engine [--name <n>] [--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[:dir…]>] [--jira <EPIC>] [--mode <permission-mode>] [--model <id>] [--decider-model <id>] [--effort <e>] [--decider-effort <e>] [--drafter-model <id>] [--drafter-effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--events <file>] [--org <slug>] [--strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp] [--sandbox [--sandbox-allow <a,b>] | --no-sandbox] [--lent-by <lender> --sandbox] [--hold-cpu <%>] [--hold-load <per-core>] [--hold-pressure warn|critical] [--hold on|off] [--once]";
18173
18175
  function templateProblem(flag, template, jira = false) {
18174
18176
  const known = flag === "branch" ? BRANCH_FIELDS : PR_TITLE_FIELDS;
18175
18177
  const used = templateFields(template);
@@ -18267,6 +18269,9 @@ function parseEngineArgs(argv, host) {
18267
18269
  return {
18268
18270
  error: `--sandbox-allow takes hosts like pypi.org or *.example.com — no scheme, port or path (got ${badHost})`
18269
18271
  };
18272
+ const lentBy = s("lent-by")?.trim();
18273
+ if (lentBy && !raw.sandbox)
18274
+ return { error: LENT_NEEDS_SANDBOX };
18270
18275
  const opt = (k, v) => v ? { [k]: v } : {};
18271
18276
  return {
18272
18277
  flags: {
@@ -18297,6 +18302,7 @@ function parseEngineArgs(argv, host) {
18297
18302
  ...mcpAllow.length ? { mcpAllow } : {},
18298
18303
  ...raw.sandbox ? { sandbox: true } : raw["no-sandbox"] ? { sandbox: false } : {},
18299
18304
  ...sandboxAllow.length ? { sandboxAllow: [...new Set(sandboxAllow)] } : {},
18305
+ ...opt("lentBy", lentBy),
18300
18306
  ...holdCpu !== undefined ? { holdCpu } : {},
18301
18307
  ...holdLoad !== undefined ? { holdLoad } : {},
18302
18308
  ...holdPressure ? { holdPressure } : {},
@@ -18479,7 +18485,7 @@ function engineClient(o, org) {
18479
18485
  return fetchClient({
18480
18486
  ...src,
18481
18487
  timeoutMs: ENGINE_TIMEOUT_MS,
18482
- version: "0.1.48"
18488
+ version: "0.1.49"
18483
18489
  });
18484
18490
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
18485
18491
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -18700,6 +18706,36 @@ function gitCommonDirOf(exec, cwd) {
18700
18706
  throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
18701
18707
  return resolve8(cwd, dir);
18702
18708
  }
18709
+ var LENT_NEEDS_SANDBOX = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
18710
+ function lenderHome(accountHome, lentBy) {
18711
+ return isAbsolute3(lentBy) ? resolve8(lentBy) : join23(dirname15(accountHome), lentBy);
18712
+ }
18713
+ function lentHomeProblems(accountHome, lentBy, modeOf) {
18714
+ const lender = lenderHome(accountHome, lentBy);
18715
+ if (lender === resolve8(accountHome))
18716
+ return [
18717
+ `--lent-by ${lentBy} is this account — a lent engine runs as the borrower's own OS user, not the lender's`
18718
+ ];
18719
+ const problems = [];
18720
+ for (const [who, home] of [
18721
+ ["the borrower's", resolve8(accountHome)],
18722
+ ["the lender's", lender]
18723
+ ]) {
18724
+ const mode = modeOf(home);
18725
+ if (mode === null)
18726
+ problems.push(`${who} home ${home} cannot be read — is ${lentBy} right?`);
18727
+ else if ((mode & 63) !== 0)
18728
+ problems.push(`${who} home ${home} is ${(mode & 511).toString(8)}, open to other users — run: sudo chmod 700 ${home}`);
18729
+ }
18730
+ return problems;
18731
+ }
18732
+ var homeMode = (path) => {
18733
+ try {
18734
+ return statSync5(path).mode & 4095;
18735
+ } catch {
18736
+ return null;
18737
+ }
18738
+ };
18703
18739
  function pinOrg2(configJson, org) {
18704
18740
  if (!org)
18705
18741
  return configJson;
@@ -18740,7 +18776,7 @@ async function driveEngine(o, flags, deps, until = {}) {
18740
18776
  runner: deps.runner.name,
18741
18777
  atOnce: flags.atOnce,
18742
18778
  interval: flags.interval,
18743
- cliVersion: "0.1.48"
18779
+ cliVersion: "0.1.49"
18744
18780
  };
18745
18781
  const beat = (checkedAt) => {
18746
18782
  const m = deps.machine;
@@ -18818,6 +18854,7 @@ async function drive(o, flags, deps, until, beat) {
18818
18854
  deps.log = (line) => event(noteEvent(line));
18819
18855
  let running = 0;
18820
18856
  let down = 0;
18857
+ await until.beforeFirstTick?.();
18821
18858
  for (;; ) {
18822
18859
  let report = null;
18823
18860
  try {
@@ -19085,7 +19122,7 @@ import {
19085
19122
  writeFileSync as writeFileSync17
19086
19123
  } from "node:fs";
19087
19124
  import { hostname as hostname3 } from "node:os";
19088
- import { basename as basename6, dirname as dirname15, isAbsolute as isAbsolute3, join as join24 } from "node:path";
19125
+ import { basename as basename6, dirname as dirname16, isAbsolute as isAbsolute4, join as join24 } from "node:path";
19089
19126
  init_state();
19090
19127
  var ENGINE_LABEL = "ai.sleds.engine";
19091
19128
  var ENGINE_INSTALL_USAGE = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
@@ -19093,7 +19130,7 @@ ${ENGINE_USAGE}`;
19093
19130
  function stableArgv(argv, realpath = realpathSync, home) {
19094
19131
  const link = sledsLink(home);
19095
19132
  return argv.map((a) => {
19096
- if (!isAbsolute3(a) || a === link)
19133
+ if (!isAbsolute4(a) || a === link)
19097
19134
  return a;
19098
19135
  try {
19099
19136
  return realpath(a);
@@ -19279,7 +19316,7 @@ function engineInstall(o) {
19279
19316
  if (platform === "linux")
19280
19317
  return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
19281
19318
  const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
19282
- mkdirSync19(dirname15(plist), { recursive: true });
19319
+ mkdirSync19(dirname16(plist), { recursive: true });
19283
19320
  if (existsSync19(plist))
19284
19321
  exec("launchctl", ["unload", plist]);
19285
19322
  for (const other of installedEngines(o.home)) {
@@ -19321,7 +19358,7 @@ function installUnit(a) {
19321
19358
  const unit = engineUnitPath(o.home, label);
19322
19359
  const unitName = basename6(unit);
19323
19360
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
19324
- mkdirSync19(dirname15(unit), { recursive: true });
19361
+ mkdirSync19(dirname16(unit), { recursive: true });
19325
19362
  if (existsSync19(unit))
19326
19363
  systemctl("stop", unitName);
19327
19364
  for (const other of installedEngines(o.home)) {
@@ -19697,7 +19734,7 @@ async function runDoctor(o) {
19697
19734
  checks.push({ level: "ok", name: "path", detail: which[0] ?? "" });
19698
19735
  const link = sledsLink(home, o.platform);
19699
19736
  if (existsSync21(link)) {
19700
- const binDir = dirname16(link);
19737
+ const binDir = dirname17(link);
19701
19738
  const onPath = (env.PATH ?? "").split(delimiter3).includes(binDir);
19702
19739
  const first = which[0];
19703
19740
  const fix = `echo 'export PATH="${binDir}:$PATH"' >> ~/.zshrc`;
@@ -19935,6 +19972,8 @@ function engineChecks(o, env) {
19935
19972
  const org = engineOrg({ home: o.home, env }).slug;
19936
19973
  const engines = installedEngines(o.home).filter((e) => e.manager === manager && (e.org === null || !org || e.org === org));
19937
19974
  const need = engines.length > 0 ? "fail" : "warn";
19975
+ if (o.lentBy && !engines.some((e) => engineArg(e.programArgs, "lent-by")))
19976
+ checks.push(...lentChecks(o, o.lentBy, null));
19938
19977
  const runner = engines.map((e) => engineArg(e.programArgs, "runner")).find(Boolean) ?? "tmux";
19939
19978
  const gh = which("gh");
19940
19979
  if (!gh)
@@ -20034,9 +20073,30 @@ function engineChecks(o, env) {
20034
20073
  detail: `${e.label} names no --repos-dir — it works only if your engine profile sets Repos folders`,
20035
20074
  fix: "set Repos folders in Settings › Running work, or sleds engine install --repos-dir ~/code"
20036
20075
  });
20076
+ const lentBy = engineArg(e.programArgs, "lent-by");
20077
+ if (lentBy)
20078
+ checks.push(...lentChecks(o, lentBy, e));
20037
20079
  }
20038
20080
  return checks;
20039
20081
  }
20082
+ function lentChecks(o, lentBy, engine) {
20083
+ const checks = [];
20084
+ const problems = lentHomeProblems((o.accountHome ?? (() => userInfo2().homedir))(), lentBy, o.homeMode ?? homeMode);
20085
+ checks.push(problems.length === 0 ? { level: "ok", name: "lent homes", detail: `lent by ${lentBy} · both homes 700` } : {
20086
+ level: "fail",
20087
+ name: "lent homes",
20088
+ detail: problems.join("; "),
20089
+ fix: "sudo chmod 700 on each home named (the lender runs it for theirs)"
20090
+ });
20091
+ if (engine)
20092
+ checks.push(engine.programArgs.includes("--sandbox") ? { level: "ok", name: "lent sandbox", detail: `${engine.label} runs --sandbox` } : {
20093
+ level: "fail",
20094
+ name: "lent sandbox",
20095
+ detail: `${engine.label}: ${LENT_NEEDS_SANDBOX}`,
20096
+ fix: `sleds engine install --lent-by ${lentBy} --sandbox`
20097
+ });
20098
+ return checks;
20099
+ }
20040
20100
  function boxChecks(o, env, which, need) {
20041
20101
  const claude = which("claude");
20042
20102
  const file = boxEnvPath(o.home);
@@ -20151,10 +20211,11 @@ import {
20151
20211
  readFileSync as readFileSync20,
20152
20212
  existsSync as realExists2,
20153
20213
  rmSync as rmSync14,
20214
+ statSync as statSync6,
20154
20215
  writeFileSync as writeFileSync20
20155
20216
  } from "node:fs";
20156
- import { hostname as hostname4 } from "node:os";
20157
- import { join as join26, resolve as resolve10 } from "node:path";
20217
+ import { hostname as hostname4, userInfo as userInfo3 } from "node:os";
20218
+ import { dirname as dirname18, isAbsolute as isAbsolute5, join as join26, resolve as resolve10 } from "node:path";
20158
20219
  init_machine();
20159
20220
  init_state();
20160
20221
  init_meter();
@@ -20190,10 +20251,11 @@ var VALUE_FLAGS2 = [
20190
20251
  "hold-cpu",
20191
20252
  "hold-load",
20192
20253
  "hold-pressure",
20193
- "hold"
20254
+ "hold",
20255
+ "lent-by"
20194
20256
  ];
20195
20257
  var BOOL_FLAGS2 = ["once", "strict-mcp", "no-strict-mcp", "sandbox", "no-sandbox"];
20196
- var ENGINE_USAGE2 = "usage: sleds engine [--name <n>] [--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[:dir…]>] [--jira <EPIC>] [--mode <permission-mode>] [--model <id>] [--decider-model <id>] [--effort <e>] [--decider-effort <e>] [--drafter-model <id>] [--drafter-effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--events <file>] [--org <slug>] [--strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp] [--sandbox [--sandbox-allow <a,b>] | --no-sandbox] [--hold-cpu <%>] [--hold-load <per-core>] [--hold-pressure warn|critical] [--hold on|off] [--once]";
20258
+ var ENGINE_USAGE2 = "usage: sleds engine [--name <n>] [--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[:dir…]>] [--jira <EPIC>] [--mode <permission-mode>] [--model <id>] [--decider-model <id>] [--effort <e>] [--decider-effort <e>] [--drafter-model <id>] [--drafter-effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--events <file>] [--org <slug>] [--strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp] [--sandbox [--sandbox-allow <a,b>] | --no-sandbox] [--lent-by <lender> --sandbox] [--hold-cpu <%>] [--hold-load <per-core>] [--hold-pressure warn|critical] [--hold on|off] [--once]";
20197
20259
  function templateProblem2(flag, template, jira = false) {
20198
20260
  const known = flag === "branch" ? BRANCH_FIELDS : PR_TITLE_FIELDS;
20199
20261
  const used = templateFields(template);
@@ -20291,6 +20353,9 @@ function parseEngineArgs2(argv, host) {
20291
20353
  return {
20292
20354
  error: `--sandbox-allow takes hosts like pypi.org or *.example.com — no scheme, port or path (got ${badHost})`
20293
20355
  };
20356
+ const lentBy = s("lent-by")?.trim();
20357
+ if (lentBy && !raw.sandbox)
20358
+ return { error: LENT_NEEDS_SANDBOX2 };
20294
20359
  const opt = (k, v) => v ? { [k]: v } : {};
20295
20360
  return {
20296
20361
  flags: {
@@ -20321,6 +20386,7 @@ function parseEngineArgs2(argv, host) {
20321
20386
  ...mcpAllow.length ? { mcpAllow } : {},
20322
20387
  ...raw.sandbox ? { sandbox: true } : raw["no-sandbox"] ? { sandbox: false } : {},
20323
20388
  ...sandboxAllow.length ? { sandboxAllow: [...new Set(sandboxAllow)] } : {},
20389
+ ...opt("lentBy", lentBy),
20324
20390
  ...holdCpu !== undefined ? { holdCpu } : {},
20325
20391
  ...holdLoad !== undefined ? { holdLoad } : {},
20326
20392
  ...holdPressure ? { holdPressure } : {},
@@ -20555,7 +20621,7 @@ function engineClient2(o, org) {
20555
20621
  return fetchClient({
20556
20622
  ...src,
20557
20623
  timeoutMs: ENGINE_TIMEOUT_MS2,
20558
- version: "0.1.48"
20624
+ version: "0.1.49"
20559
20625
  });
20560
20626
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
20561
20627
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -20786,6 +20852,8 @@ function gitCommonDirOf2(exec, cwd) {
20786
20852
  return resolve10(cwd, dir);
20787
20853
  }
20788
20854
  function sandboxRefusal(flags) {
20855
+ if (flags.lentBy && !flags.sandbox)
20856
+ return LENT_NEEDS_SANDBOX2;
20789
20857
  if (!flags.sandbox)
20790
20858
  return null;
20791
20859
  if (flags.runner === "conductor")
@@ -20796,6 +20864,36 @@ function sandboxRefusal(flags) {
20796
20864
  return "sandboxed sessions: Codex's sandbox walls writes, not reads — the seat token stays readable; use Claude Code, or --no-sandbox";
20797
20865
  return null;
20798
20866
  }
20867
+ var LENT_NEEDS_SANDBOX2 = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
20868
+ function lenderHome2(accountHome, lentBy) {
20869
+ return isAbsolute5(lentBy) ? resolve10(lentBy) : join26(dirname18(accountHome), lentBy);
20870
+ }
20871
+ function lentHomeProblems2(accountHome, lentBy, modeOf) {
20872
+ const lender = lenderHome2(accountHome, lentBy);
20873
+ if (lender === resolve10(accountHome))
20874
+ return [
20875
+ `--lent-by ${lentBy} is this account — a lent engine runs as the borrower's own OS user, not the lender's`
20876
+ ];
20877
+ const problems = [];
20878
+ for (const [who, home] of [
20879
+ ["the borrower's", resolve10(accountHome)],
20880
+ ["the lender's", lender]
20881
+ ]) {
20882
+ const mode = modeOf(home);
20883
+ if (mode === null)
20884
+ problems.push(`${who} home ${home} cannot be read — is ${lentBy} right?`);
20885
+ else if ((mode & 63) !== 0)
20886
+ problems.push(`${who} home ${home} is ${(mode & 511).toString(8)}, open to other users — run: sudo chmod 700 ${home}`);
20887
+ }
20888
+ return problems;
20889
+ }
20890
+ var homeMode2 = (path) => {
20891
+ try {
20892
+ return statSync6(path).mode & 4095;
20893
+ } catch {
20894
+ return null;
20895
+ }
20896
+ };
20799
20897
  function pinOrg3(configJson, org) {
20800
20898
  if (!org)
20801
20899
  return configJson;
@@ -20836,7 +20934,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
20836
20934
  runner: deps.runner.name,
20837
20935
  atOnce: flags.atOnce,
20838
20936
  interval: flags.interval,
20839
- cliVersion: "0.1.48"
20937
+ cliVersion: "0.1.49"
20840
20938
  };
20841
20939
  const beat = (checkedAt) => {
20842
20940
  const m = deps.machine;
@@ -20914,6 +21012,7 @@ async function drive2(o, flags, deps, until, beat) {
20914
21012
  deps.log = (line) => event(noteEvent(line));
20915
21013
  let running = 0;
20916
21014
  let down = 0;
21015
+ await until.beforeFirstTick?.();
20917
21016
  for (;; ) {
20918
21017
  let report = null;
20919
21018
  try {
@@ -21095,6 +21194,14 @@ var engineCmd = async (o) => {
21095
21194
  error(refusal);
21096
21195
  return 2;
21097
21196
  }
21197
+ if (flags.lentBy) {
21198
+ const open = lentHomeProblems2((o.accountHome ?? (() => userInfo3().homedir))(), flags.lentBy, o.homeMode ?? homeMode2);
21199
+ if (open.length) {
21200
+ for (const line of open)
21201
+ error(`lent engine: ${line}`);
21202
+ return 2;
21203
+ }
21204
+ }
21098
21205
  if (flags.sandbox && flags.runner === "managed")
21099
21206
  notes.push("sandboxed sessions: the managed runner's container is its own sandbox — the engine's settings do not reach it");
21100
21207
  let source;
@@ -21186,6 +21293,7 @@ var engineCmd = async (o) => {
21186
21293
  watch.beat();
21187
21294
  return update();
21188
21295
  };
21296
+ let beforeFirstTick;
21189
21297
  if (client) {
21190
21298
  const send = beater(client, o.now ?? Date.now);
21191
21299
  let noted;
@@ -21204,20 +21312,33 @@ var engineCmd = async (o) => {
21204
21312
  refused = r.refused;
21205
21313
  error(r.below ? `${r.refused}. This engine is not shown as up until it beats on a version SLEDS serves.` : `${r.refused}. This engine is not shown as up until it beats under a name of its own.`);
21206
21314
  };
21315
+ const body = () => {
21316
+ const m = deps.machine;
21317
+ return beatBody(deps.readState(), { name: flags.name, runner: deps.runner.name }, {}, {
21318
+ atOnce: deps.atOnce,
21319
+ ...m?.last ? { machine: m.last } : {},
21320
+ ...m?.hold && !m.off ? { hold: m.hold } : {}
21321
+ });
21322
+ };
21323
+ const heard = (r) => {
21324
+ sayRefused(r);
21325
+ if (r.ok) {
21326
+ updater.sawBeat(r.update);
21327
+ noteBeat(r.update);
21328
+ }
21329
+ };
21330
+ beforeFirstTick = async () => {
21331
+ try {
21332
+ heard(await postBeat(client, body()));
21333
+ } catch {}
21334
+ };
21207
21335
  afterTick = async () => {
21208
21336
  try {
21209
- const m = deps.machine;
21210
- send(beatBody(deps.readState(), { name: flags.name, runner: deps.runner.name }, {}, {
21211
- atOnce: deps.atOnce,
21212
- ...m?.last ? { machine: m.last } : {},
21213
- ...m?.hold && !m.off ? { hold: m.hold } : {}
21214
- }))?.then((r) => {
21215
- sayRefused(r);
21216
- if (r?.ok) {
21217
- updater.sawBeat(r.update);
21218
- noteBeat(r.update);
21219
- }
21220
- if (r && (r.ok || ("unreachable" in r)))
21337
+ send(body())?.then((r) => {
21338
+ if (!r)
21339
+ return;
21340
+ heard(r);
21341
+ if (r.ok || "unreachable" in r)
21221
21342
  watch.beat();
21222
21343
  }, () => {});
21223
21344
  } catch {}
@@ -21225,7 +21346,7 @@ var engineCmd = async (o) => {
21225
21346
  };
21226
21347
  }
21227
21348
  try {
21228
- return await driveEngine2(o, flags, deps, { afterTick });
21349
+ return await driveEngine2(o, flags, deps, { afterTick, beforeFirstTick });
21229
21350
  } finally {
21230
21351
  watch.stop();
21231
21352
  }
@@ -21435,7 +21556,7 @@ import {
21435
21556
  writeFileSync as writeFileSync21
21436
21557
  } from "node:fs";
21437
21558
  import { hostname as hostname5 } from "node:os";
21438
- import { basename as basename7, dirname as dirname17, isAbsolute as isAbsolute4, join as join27 } from "node:path";
21559
+ import { basename as basename7, dirname as dirname19, isAbsolute as isAbsolute6, join as join27 } from "node:path";
21439
21560
  init_state();
21440
21561
  var ENGINE_LABEL2 = "ai.sleds.engine";
21441
21562
  var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
@@ -21443,7 +21564,7 @@ ${ENGINE_USAGE}`;
21443
21564
  function stableArgv2(argv, realpath = realpathSync2, home) {
21444
21565
  const link = sledsLink(home);
21445
21566
  return argv.map((a) => {
21446
- if (!isAbsolute4(a) || a === link)
21567
+ if (!isAbsolute6(a) || a === link)
21447
21568
  return a;
21448
21569
  try {
21449
21570
  return realpath(a);
@@ -21673,7 +21794,7 @@ function engineInstall2(o) {
21673
21794
  if (platform === "linux")
21674
21795
  return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
21675
21796
  const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
21676
- mkdirSync22(dirname17(plist), { recursive: true });
21797
+ mkdirSync22(dirname19(plist), { recursive: true });
21677
21798
  if (existsSync22(plist))
21678
21799
  exec("launchctl", ["unload", plist]);
21679
21800
  for (const other of installedEngines2(o.home)) {
@@ -21715,7 +21836,7 @@ function installUnit2(a) {
21715
21836
  const unit = engineUnitPath(o.home, label);
21716
21837
  const unitName = basename7(unit);
21717
21838
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
21718
- mkdirSync22(dirname17(unit), { recursive: true });
21839
+ mkdirSync22(dirname19(unit), { recursive: true });
21719
21840
  if (existsSync22(unit))
21720
21841
  systemctl("stop", unitName);
21721
21842
  for (const other of installedEngines2(o.home)) {
@@ -22740,7 +22861,7 @@ init_meter();
22740
22861
  // src/owed.ts
22741
22862
  init_config();
22742
22863
  import { existsSync as existsSync26, mkdirSync as mkdirSync23, readFileSync as readFileSync25, renameSync as renameSync11, writeFileSync as writeFileSync22 } from "node:fs";
22743
- import { dirname as dirname18, join as join28 } from "node:path";
22864
+ import { dirname as dirname20, join as join28 } from "node:path";
22744
22865
  var owedPath = (home) => join28(configDir(home), "owed.json");
22745
22866
  var sessionPath = (home) => join28(configDir(home), "session.json");
22746
22867
  var OWED_TTL_MS = 3 * 24 * 60 * 60000;
@@ -22766,7 +22887,7 @@ var readJson = (p) => {
22766
22887
  }
22767
22888
  };
22768
22889
  var writeJson = (p, v) => {
22769
- mkdirSync23(dirname18(p), { recursive: true, mode: 448 });
22890
+ mkdirSync23(dirname20(p), { recursive: true, mode: 448 });
22770
22891
  const tmp = `${p}.${process.pid}.tmp`;
22771
22892
  writeFileSync22(tmp, `${JSON.stringify(v, null, 2)}
22772
22893
  `, { mode: 384 });
@@ -22900,7 +23021,7 @@ function contextFor(deps) {
22900
23021
  ...src,
22901
23022
  fetchFn: deps.fetchFn,
22902
23023
  timeoutMs: deps.timeoutMs,
22903
- version: "0.1.48"
23024
+ version: "0.1.49"
22904
23025
  }), deps.home, now) : null;
22905
23026
  return {
22906
23027
  channel: "code",
@@ -24029,7 +24150,7 @@ init_meter();
24029
24150
  import { spawnSync as spawnSync7 } from "node:child_process";
24030
24151
  import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync27, rmSync as rmSync17, writeFileSync as writeFileSync24 } from "node:fs";
24031
24152
  import { hostname as hostname11 } from "node:os";
24032
- import { dirname as dirname19 } from "node:path";
24153
+ import { dirname as dirname21 } from "node:path";
24033
24154
  var defaultWhich3 = (cmd) => {
24034
24155
  const path = process.env.PATH ?? "";
24035
24156
  for (const dir of path.split(":").filter(Boolean)) {
@@ -24067,7 +24188,7 @@ var musherInstall = (o) => {
24067
24188
  const plist = musherPlistPath(o.home, env);
24068
24189
  const label = musherLabel(env);
24069
24190
  const dir = configDir(o.home, env);
24070
- mkdirSync25(dirname19(plist), { recursive: true });
24191
+ mkdirSync25(dirname21(plist), { recursive: true });
24071
24192
  mkdirSync25(dir, { recursive: true, mode: 448 });
24072
24193
  const execPath = o.sledsArgv[0] ?? process.execPath;
24073
24194
  const agentEnv = {
@@ -24505,7 +24626,7 @@ var packCmd = async (o) => {
24505
24626
 
24506
24627
  // src/commands/permissionEvent.ts
24507
24628
  import { chmodSync as chmodSync7, mkdirSync as mkdirSync26, readFileSync as readFileSync28, renameSync as renameSync12, rmSync as rmSync19, writeFileSync as writeFileSync26 } from "node:fs";
24508
- import { dirname as dirname20 } from "node:path";
24629
+ import { dirname as dirname22 } from "node:path";
24509
24630
  var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24510
24631
  var messageOf = (text) => {
24511
24632
  try {
@@ -24529,7 +24650,7 @@ var permissionEvent = async (o) => {
24529
24650
  if (!isToolPermission2(message))
24530
24651
  return 0;
24531
24652
  const mark = { message, at: (o.now ?? Date.now)() };
24532
- mkdirSync26(dirname20(o.mark), { recursive: true, mode: 448 });
24653
+ mkdirSync26(dirname22(o.mark), { recursive: true, mode: 448 });
24533
24654
  const tmp = `${o.mark}.${process.pid}.tmp`;
24534
24655
  writeFileSync26(tmp, `${JSON.stringify(mark)}
24535
24656
  `, { mode: 384 });
@@ -25569,11 +25690,81 @@ var seatEvent = async (o) => {
25569
25690
  return 0;
25570
25691
  };
25571
25692
 
25693
+ // src/commands/seatRevoke.ts
25694
+ var SEAT_REVOKE_USAGE = "usage: sleds seat revoke [--box <name>] [--yes] [--json]";
25695
+ var LAG = "a proxy may honour it for up to 60 s more";
25696
+ function parseSeatRevokeArgs(argv) {
25697
+ let box = null;
25698
+ let yes = false;
25699
+ let json = false;
25700
+ for (let i = 0;i < argv.length; i++) {
25701
+ const a = argv[i];
25702
+ if (a === "--json")
25703
+ json = true;
25704
+ else if (a === "--yes" || a === "-y")
25705
+ yes = true;
25706
+ else if (a === "--box") {
25707
+ const value = argv[i + 1]?.trim();
25708
+ if (!value || value.startsWith("--"))
25709
+ return { error: "--box needs the box's name" };
25710
+ box = value.replace(/^box:/, "");
25711
+ i++;
25712
+ } else
25713
+ return { error: `unknown argument ${a}` };
25714
+ }
25715
+ return { box, yes, json };
25716
+ }
25717
+ function why(j, status) {
25718
+ if (typeof j?.reason === "string")
25719
+ return j.reason;
25720
+ if (typeof j?.error === "string")
25721
+ return j.error;
25722
+ const e = j?.error;
25723
+ return typeof e?.message === "string" ? e.message : `HTTP ${status}`;
25724
+ }
25725
+ async function seatRevokeCmd(o) {
25726
+ const log = o.log ?? console.log;
25727
+ const error = o.error ?? console.error;
25728
+ const a = parseSeatRevokeArgs(o.argv);
25729
+ if ("error" in a) {
25730
+ error(a.error);
25731
+ error(SEAT_REVOKE_USAGE);
25732
+ return 2;
25733
+ }
25734
+ if (!a.box && !a.yes) {
25735
+ error("this revokes this machine's own seat — SLEDS stops here until `sleds init` again.");
25736
+ error("Add --yes to go ahead, or --box <name> to revoke one of your boxes instead.");
25737
+ return 2;
25738
+ }
25739
+ const client = engineClient({ home: o.home, ...o.client ? { client: o.client } : {} });
25740
+ if ("error" in client) {
25741
+ error(client.error);
25742
+ return 1;
25743
+ }
25744
+ const r = await client.postFull("/proxy/seat/revoke", a.box ? { box: a.box } : {});
25745
+ if (!r) {
25746
+ error("SLEDS unreachable — nothing revoked");
25747
+ return 1;
25748
+ }
25749
+ const ok = r.status < 300 && r.json?.ok !== false;
25750
+ if (a.json) {
25751
+ log(JSON.stringify({ v: 1, ...r.json ?? { ok: false, reason: `HTTP ${r.status}` } }));
25752
+ return ok ? 0 : 1;
25753
+ }
25754
+ if (!ok) {
25755
+ error(clean(`nothing revoked — ${why(r.json, r.status)}`));
25756
+ return 1;
25757
+ }
25758
+ const label = typeof r.json?.label === "string" ? r.json.label : a.box ? `box:${a.box}` : "";
25759
+ log(clean(a.box ? `${label} revoked — its token is dead; ${LAG}` : `this machine's seat${label ? ` (${label})` : ""} revoked — its token is dead; ${LAG}. Run \`sleds init\` with a new one to use SLEDS here again`));
25760
+ return 0;
25761
+ }
25762
+
25572
25763
  // src/commands/seatSync.ts
25573
25764
  import { execFileSync as execFileSync5 } from "node:child_process";
25574
25765
  import { existsSync as existsSync30, mkdirSync as mkdirSync27, readFileSync as readFileSync30, readdirSync as readdirSync7, rmSync as rmSync21, writeFileSync as writeFileSync27 } from "node:fs";
25575
25766
  import { platform as platform2 } from "node:os";
25576
- import { dirname as dirname21, join as join31 } from "node:path";
25767
+ import { dirname as dirname23, join as join31 } from "node:path";
25577
25768
 
25578
25769
  // src/cookieDb.ts
25579
25770
  import { copyFileSync as copyFileSync3, rmSync as rmSync20 } from "node:fs";
@@ -26007,7 +26198,7 @@ function install(home, argv, source, log, launchctl) {
26007
26198
  const tokenPath = seatTokenPath(home, source);
26008
26199
  rmSync21(tokenPath, { force: true });
26009
26200
  if (source.token) {
26010
- mkdirSync27(dirname21(tokenPath), { recursive: true, mode: 448 });
26201
+ mkdirSync27(dirname23(tokenPath), { recursive: true, mode: 448 });
26011
26202
  writeFileSync27(tokenPath, `${source.token}
26012
26203
  `, { mode: 384 });
26013
26204
  }
@@ -26184,8 +26375,8 @@ var sessionEvent = async (o) => {
26184
26375
 
26185
26376
  // src/commands/specEvent.ts
26186
26377
  init_meter();
26187
- import { readFileSync as readFileSync31, statSync as statSync5 } from "node:fs";
26188
- import { basename as basename8, isAbsolute as isAbsolute5, relative as relative2 } from "node:path";
26378
+ import { readFileSync as readFileSync31, statSync as statSync7 } from "node:fs";
26379
+ import { basename as basename8, isAbsolute as isAbsolute7, relative as relative2 } from "node:path";
26189
26380
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
26190
26381
  var ESTIMATE_TIMEOUT_MS = 4000;
26191
26382
  var MIN_SPEC_CHARS = 200;
@@ -26229,7 +26420,7 @@ function parseHookInput(raw) {
26229
26420
  return { toolName, filePath, cwd };
26230
26421
  }
26231
26422
  function projectKey(filePath, cwd) {
26232
- if (cwd && isAbsolute5(filePath) && isAbsolute5(cwd)) {
26423
+ if (cwd && isAbsolute7(filePath) && isAbsolute7(cwd)) {
26233
26424
  const rel = relative2(cwd, filePath);
26234
26425
  if (rel && !rel.startsWith(".."))
26235
26426
  return rel;
@@ -26238,7 +26429,7 @@ function projectKey(filePath, cwd) {
26238
26429
  }
26239
26430
  function readFileOrNull(path) {
26240
26431
  try {
26241
- if (statSync5(path).size > 2000000)
26432
+ if (statSync7(path).size > 2000000)
26242
26433
  return null;
26243
26434
  return readFileSync31(path, "utf8");
26244
26435
  } catch {
@@ -26850,7 +27041,7 @@ var hasEitherKey = (settings) => {
26850
27041
  // src/commands/update.ts
26851
27042
  init_server();
26852
27043
  import { existsSync as existsSync34 } from "node:fs";
26853
- import { homedir as homedir7, userInfo } from "node:os";
27044
+ import { homedir as homedir7, userInfo as userInfo4 } from "node:os";
26854
27045
  import { basename as basename9 } from "node:path";
26855
27046
  init_config();
26856
27047
  init_state();
@@ -26916,7 +27107,7 @@ var realPidAlive4 = (pid) => {
26916
27107
  };
26917
27108
  function machineClient(home, env) {
26918
27109
  const src = meterSource(home, undefined, { env });
26919
- return src ? fetchClient({ ...src, version: "0.1.48" }) : null;
27110
+ return src ? fetchClient({ ...src, version: "0.1.49" }) : null;
26920
27111
  }
26921
27112
  async function noteAnswer2(home, a, now) {
26922
27113
  try {
@@ -26963,7 +27154,7 @@ async function updateCmd(o) {
26963
27154
  const r = await rollBack({
26964
27155
  home,
26965
27156
  restoreState: false,
26966
- why: `asked by ${o.user ?? userInfo().username}`,
27157
+ why: `asked by ${o.user ?? userInfo4().username}`,
26967
27158
  now: now(),
26968
27159
  platform
26969
27160
  });
@@ -27439,7 +27630,7 @@ var defaultExec5 = (cmd, args) => {
27439
27630
  };
27440
27631
 
27441
27632
  // src/settings.ts
27442
- import { dirname as dirname22, join as join34, resolve as resolve12 } from "node:path";
27633
+ import { dirname as dirname24, join as join34, resolve as resolve12 } from "node:path";
27443
27634
  var withSeatEnv2 = (command, home, env = process.env, org) => {
27444
27635
  const parts = [];
27445
27636
  const sh = env.SLEDS_HOME?.trim();
@@ -27625,10 +27816,16 @@ commands:
27625
27816
  needs (gh signed in, git, tmux, its launchd agent, a repos dir). --json: {v, checks}.
27626
27817
  --sandbox: also run one short sandboxed Claude Code session (haiku) and prove it
27627
27818
  cannot read the SLEDS config dir or reach a host off the allowlist.
27819
+ --lent-by <lender>: check this account as a lent engine's before the install \u2014
27820
+ both homes 700 (an installed engine with --lent-by is checked without it).
27628
27821
  meter Show the pipeline: built today, queued with its band, delivered overnight.
27629
27822
  mcp Run the MCP server on stdio: tools usage, estimate, overnight, jobs.
27630
27823
  Register with \`claude mcp add sleds -- sleds mcp\` (Cowork sees the same server).
27631
- seat-sync Read the seat's subscription usage (weekly/5h limits, per-model, credits) from a
27824
+ seat revoke [--box <name>] [--yes] [--json] Kill a seat's token in one step. --box revokes
27825
+ your own box:<name> seat, from any terminal; without it this machine's own seat
27826
+ is revoked (--yes, since SLEDS stops here until \`sleds init\` again). A proxy may
27827
+ honour the token for up to 60 s more. --json: {v, ok, seatId, label} or {v, ok, reason}
27828
+ seat-sync Read the seat's subscription usage (weekly/5h limits, per-model, credits) from a
27632
27829
  claude.ai session and post it to the proxy. macOS.
27633
27830
  --browser <claude|chrome|brave|edge|chromium|\u2026> which cookie store (default claude)
27634
27831
  --profile <name> browser profile dir ("Profile 1") or its display name/email
@@ -27681,6 +27878,7 @@ var parseArgs = (argv) => {
27681
27878
  }
27682
27879
  return { cmd, flags };
27683
27880
  };
27881
+ var lentByOf = (v) => typeof v === "string" && v.trim() ? { lentBy: v.trim() } : {};
27684
27882
  var HELP = new Set(["help", "--help", "-h"]);
27685
27883
  var wantsHelp = (argv) => {
27686
27884
  if (HELP.has(argv[0] ?? "--help"))
@@ -27754,7 +27952,7 @@ var main = async (argv) => {
27754
27952
  now: Date.now(),
27755
27953
  stderr: (line) => console.error(line),
27756
27954
  isTTY: !!process.stderr.isTTY,
27757
- version: "0.1.48"
27955
+ version: "0.1.49"
27758
27956
  });
27759
27957
  switch (cmd) {
27760
27958
  case "update":
@@ -27775,6 +27973,11 @@ var main = async (argv) => {
27775
27973
  return sledCmd({ home, argv: argv.slice(1) });
27776
27974
  case "reopen":
27777
27975
  return reopenCmd({ home, argv: argv.slice(1) });
27976
+ case "seat":
27977
+ if (argv[1] === "revoke")
27978
+ return seatRevokeCmd({ home, argv: argv.slice(2) });
27979
+ console.error(SEAT_REVOKE_USAGE);
27980
+ return 2;
27778
27981
  case "engine":
27779
27982
  case "route": {
27780
27983
  if (cmd === "engine" && argv[1] === "status")
@@ -27957,7 +28160,8 @@ var main = async (argv) => {
27957
28160
  return doctor({
27958
28161
  home,
27959
28162
  json: argv.slice(1).includes("--json"),
27960
- sandbox: argv.slice(1).includes("--sandbox")
28163
+ sandbox: argv.slice(1).includes("--sandbox"),
28164
+ ...lentByOf(parseArgs(argv).flags["lent-by"])
27961
28165
  });
27962
28166
  case "status":
27963
28167
  return status3({ home });
@@ -27981,7 +28185,7 @@ ${renderHelpText(argv[1])}`);
27981
28185
  case "version":
27982
28186
  case "--version":
27983
28187
  case "-v":
27984
- console.log("sleds 0.1.48");
28188
+ console.log("sleds 0.1.49");
27985
28189
  return 0;
27986
28190
  default:
27987
28191
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
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",