@tonbo/cli 0.17.1 → 0.17.2

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/bin/tonbo.js +36 -3
  2. package/package.json +1 -1
package/dist/bin/tonbo.js CHANGED
@@ -5688,7 +5688,10 @@ function awakeFields(machine) {
5688
5688
  const held = awake.held_by.length > 0;
5689
5689
  const sleeps = awake.sleeps_at_earliest ? `${awake.sleeps_at_earliest} (${awake.sleeps_by === "idle_early" ? `idle_early: quiet for ${duration(earlyReclaimSeconds)}` : `sleep_after: idle for ${duration(machine.sleep_after_seconds)}`})` : held ? "not while anything holds it" : "not known yet";
5690
5690
  return [
5691
- ["Held by", held ? awake.held_by.map(holderText).join(", ") : "nothing"],
5691
+ [
5692
+ "Held by",
5693
+ held ? awake.held_by.map(holderText).join(", ") : `nothing${awake.sleeps_at_earliest ? `, so it sleeps at ${awake.sleeps_at_earliest}` : ""}; a detached process (tonbo machine exec ${machine.name} --detach -- <command>) or a hold (tonbo machine hold set ${machine.name} <hold> --for 15m) keeps it awake`
5694
+ ],
5692
5695
  ...awake.idle_since ? [["Idle since", awake.idle_since]] : [],
5693
5696
  ["Quiet", awake.quiet.passes ? "yes" : `no: ${awake.quiet.reason ?? "measuring"}`],
5694
5697
  ["Sleeps at", sleeps],
@@ -6825,7 +6828,7 @@ var COMMAND_HELP = {
6825
6828
  },
6826
6829
  "tonbo machine exec": {
6827
6830
  about: [
6828
- "Runs one command in the Machine's running activation through its region's cell origin, waking the Machine first when it is asleep, and streams its stdout and stderr here as they come; a call that woke the Machine prints its hourly ceiling on stderr. The command runs without a shell, as the image's User, and lives exactly as long as the call: there is no terminal and no streamed stdin, and `--stdin` sends at most 1 MiB read before the call.",
6831
+ "Runs one command in the Machine's running activation through its region's cell origin, waking the Machine first when it is asleep, and streams its stdout and stderr here as they come; a call that woke the Machine prints its hourly ceiling on stderr. The command runs without a shell, as the image's User, and lives exactly as long as the call: what it starts in the background, with `nohup`, `setsid` or `&`, is ended when it returns, and the CLI then says how many it ended. There is no terminal and no streamed stdin, and `--stdin` sends at most 1 MiB read before the call.",
6829
6832
  "The CLI exits with the command's status: 128 plus the signal's number when a signal ended it, 124 at `--timeout`, 125 when it was refused, killed or passed 64 MiB of output, 126 or 127 when it could not run, and 255 when the connection ended first and the outcome is unknown. `--json` prints the answer's document instead, with the first and last 512 KiB of each stream.",
6830
6833
  "`--detach` starts the command in the background instead, prints its process id and returns at once: it runs whatever happens to this call, keeps the Machine awake until it ends, and is ended at `--timeout` (default 3600, at most 21600). Read it with `tonbo machine output`, wait for it with `tonbo machine wait`, list them with `tonbo machine ps` and end one with `tonbo machine kill`. A budget or funding stop, or a sleep you ask for, still ends it."
6831
6834
  ],
@@ -7215,9 +7218,30 @@ function parseEnv2(pairs) {
7215
7218
  }
7216
7219
  return env;
7217
7220
  }
7221
+ var BACKGROUNDERS = /* @__PURE__ */ new Set(["nohup", "setsid"]);
7222
+ var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ash", "ksh", "mksh"]);
7223
+ function backgroundsWork(command) {
7224
+ const base = (arg) => arg.slice(arg.lastIndexOf("/") + 1);
7225
+ if (command.some((arg) => BACKGROUNDERS.has(base(arg)))) return true;
7226
+ const shell = command.findIndex((arg) => SHELLS.has(base(arg)));
7227
+ if (shell < 0) return false;
7228
+ const flag = command.findIndex((arg, at) => at > shell && /^-[a-zA-Z]*c[a-zA-Z]*$/.test(arg));
7229
+ const script = flag < 0 ? void 0 : command[flag + 1]?.trimEnd();
7230
+ if (script === void 0) return false;
7231
+ return script.endsWith("&") && !script.endsWith("&&") || /\b(nohup|disown|setsid)\b/.test(script);
7232
+ }
7233
+ var detachWay = (name) => `To keep work running after the call, use: tonbo machine exec ${name} --detach -- <command>`;
7234
+ function backgroundEndedNotice(name, count) {
7235
+ const processes = count === 1 ? "1 background process" : `${count} background processes`;
7236
+ return `${processes} this command started ${count === 1 ? "was" : "were"} ended when it returned. ${detachWay(name)}`;
7237
+ }
7238
+ function backgroundsWorkNotice(name, detach) {
7239
+ return detach ? `--detach already keeps the command running after the call, and what it puts in the background with nohup or & is ended when the command itself returns. Run the work in the foreground: tonbo machine exec ${name} --detach -- <command>` : `This command puts work in the background, which is ended when the command returns. ${detachWay(name)}`;
7240
+ }
7218
7241
  async function machineExecCommand(deps, name, command, options = {}) {
7219
7242
  if (command.length === 0)
7220
7243
  throw new Error("Name the command after --, such as `tonbo machine exec web -- ls /`.");
7244
+ if (backgroundsWork(command)) deps.notice(backgroundsWorkNotice(name, false));
7221
7245
  const key = await deps.auth.apiKey();
7222
7246
  const region = await regionOf(deps, key, name);
7223
7247
  const body = { command };
@@ -7244,6 +7268,7 @@ async function machineExecCommand(deps, name, command, options = {}) {
7244
7268
  if (options.json) {
7245
7269
  const answer = await response.json();
7246
7270
  print(deps, true, answer.data, "");
7271
+ noticeEnded(deps, name, answer.data);
7247
7272
  exitWith(deps, answer.data.reason ? execExitCode(answer.data) : 0);
7248
7273
  return;
7249
7274
  }
@@ -7276,10 +7301,17 @@ async function machineExecCommand(deps, name, command, options = {}) {
7276
7301
  }
7277
7302
  line(buffered);
7278
7303
  const ended = end ?? { reason: "lost", exit_code: null, signal: null };
7279
- if (ended.reason !== "exited" && ended.reason !== "signaled")
7304
+ if (ended.reason === "timeout") deps.notice(`The command ended: timeout. ${detachWay(name)}`);
7305
+ else if (ended.reason !== "exited" && ended.reason !== "signaled")
7280
7306
  deps.notice(`The command ended: ${ended.reason}.`);
7307
+ noticeEnded(deps, name, ended);
7281
7308
  exitWith(deps, execExitCode(ended));
7282
7309
  }
7310
+ function noticeEnded(deps, name, end) {
7311
+ const count = end.killed_processes ?? 0;
7312
+ if (count > 0 && (end.reason === "exited" || end.reason === "signaled"))
7313
+ deps.notice(backgroundEndedNotice(name, count));
7314
+ }
7283
7315
  function processEndText(process2) {
7284
7316
  const end = process2.end;
7285
7317
  if (!end) return "-";
@@ -7299,6 +7331,7 @@ async function machineProcessStartCommand(deps, name, command, options = {}) {
7299
7331
  throw new Error(
7300
7332
  "Name the command after --, such as `tonbo machine exec web --detach -- npm run build`."
7301
7333
  );
7334
+ if (backgroundsWork(command)) deps.notice(backgroundsWorkNotice(name, true));
7302
7335
  const body = { command };
7303
7336
  if (options.cwd) body.cwd = options.cwd;
7304
7337
  if (options.env?.length) body.env = parseEnv2(options.env);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonbo/cli",
3
- "version": "0.17.1",
3
+ "version": "0.17.2",
4
4
  "description": "The Tonbo CLI. Compute, disks, databases and streams for AI agents: your agent picks what it needs, sets it up, and knows what it costs, from the command line.",
5
5
  "homepage": "https://tonbo.dev",
6
6
  "bugs": {