airgate 0.1.7 → 0.2.0

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/airgate.mjs +537 -210
  2. package/package.json +1 -1
package/dist/airgate.mjs CHANGED
@@ -1,4 +1,133 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/pack.ts
13
+ var pack_exports = {};
14
+ __export(pack_exports, {
15
+ CLAUDE_CONFIG: () => CLAUDE_CONFIG,
16
+ TooLargeError: () => TooLargeError,
17
+ UPLOAD_LIMIT: () => UPLOAD_LIMIT,
18
+ git: () => git,
19
+ gitFiles: () => gitFiles,
20
+ gitTopLevel: () => gitTopLevel,
21
+ isEnvFile: () => isEnvFile,
22
+ isGitRepo: () => isGitRepo,
23
+ packClaudeConfig: () => packClaudeConfig,
24
+ packProject: () => packProject,
25
+ runTar: () => runTar
26
+ });
27
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
28
+ import { existsSync, lstatSync, readdirSync } from "node:fs";
29
+ import { join as join2 } from "node:path";
30
+ function isEnvFile(path) {
31
+ const name = path.split("/").pop() ?? "";
32
+ return /^\.env($|\.)/.test(name) && !/\.(example|sample|template|dist|defaults?|schema)$/.test(name);
33
+ }
34
+ function git(cwd, args, input) {
35
+ const r = spawnSync2("git", args, { cwd, input, maxBuffer: 256 * 1024 * 1024 });
36
+ return { status: r.status ?? (r.error ? 127 : 1), stdout: r.stdout?.toString("utf8") ?? "", stderr: r.stderr?.toString("utf8") ?? "", error: r.error };
37
+ }
38
+ function isGitRepo(dir) {
39
+ return git(dir, ["rev-parse", "--is-inside-work-tree"]).stdout.trim() === "true";
40
+ }
41
+ function gitTopLevel(dir) {
42
+ const r = git(dir, ["rev-parse", "--show-toplevel"]);
43
+ return r.status === 0 ? r.stdout.trim() : null;
44
+ }
45
+ function gitFiles(dir) {
46
+ const r = git(dir, ["ls-files", "-co", "--exclude-standard", "-z"]);
47
+ if (r.status !== 0) throw new Error(`git ls-files: ${r.stderr.trim()}`);
48
+ const seen = /* @__PURE__ */ new Set();
49
+ const out = [];
50
+ for (const f of r.stdout.split("\0")) {
51
+ if (!f || seen.has(f)) continue;
52
+ seen.add(f);
53
+ try {
54
+ lstatSync(join2(dir, f));
55
+ out.push(f);
56
+ } catch {
57
+ }
58
+ }
59
+ return out;
60
+ }
61
+ function runTar(cwd, args, input, limit = UPLOAD_LIMIT) {
62
+ return new Promise((resolve10, reject) => {
63
+ const child = spawn("tar", args, { cwd, env: { ...process.env, COPYFILE_DISABLE: "1" }, stdio: ["pipe", "pipe", "pipe"] });
64
+ const chunks = [];
65
+ let size2 = 0;
66
+ let err = "";
67
+ let over2 = false;
68
+ child.stdout.on("data", (d) => {
69
+ size2 += d.length;
70
+ if (size2 > limit) {
71
+ over2 = true;
72
+ child.kill();
73
+ return;
74
+ }
75
+ chunks.push(d);
76
+ });
77
+ child.stderr.on("data", (d) => err += d.toString());
78
+ child.on("error", (e) => reject(new Error(`tar: ${e.message}`)));
79
+ child.on("close", (code3) => {
80
+ if (over2) return reject(new TooLargeError(limit));
81
+ if (code3 !== 0) return reject(new Error(`tar failed: ${err.trim().split("\n").slice(-3).join(" ")}`));
82
+ resolve10(Buffer.concat(chunks));
83
+ });
84
+ child.stdin.on("error", () => void 0);
85
+ child.stdin.end(input ?? "");
86
+ });
87
+ }
88
+ async function packProject(dir, limit = UPLOAD_LIMIT) {
89
+ if (isGitRepo(dir)) {
90
+ const tracked = new Set(git(dir, ["ls-files", "-c", "-z"]).stdout.split("\0"));
91
+ const all = gitFiles(dir).filter((f) => !f.startsWith(".airgate/"));
92
+ const skipped2 = all.filter((f) => (isEnvFile(f) || f.split("/").pop() === ".envrc") && !tracked.has(f));
93
+ const files2 = all.filter((f) => !skipped2.includes(f));
94
+ if (!files2.length) throw new Error("no files to send (git sees nothing tracked or untracked here)");
95
+ const archive2 = await runTar(dir, ["--null", "-T", "-", "-czf", "-"], files2.join("\0") + "\0", limit);
96
+ return { archive: archive2, files: files2.length, git: true, skipped: skipped2 };
97
+ }
98
+ const skipped = readdirSync(dir).filter((f) => isEnvFile(f) || f === ".envrc");
99
+ const archive = await runTar(dir, ["--exclude=node_modules", "--exclude=.git", "--exclude=.airgate", ...ENV_EXCLUDES, "-czf", "-", "."], null, limit);
100
+ return { archive, files: -1, git: false, skipped };
101
+ }
102
+ async function packClaudeConfig(home, limit = UPLOAD_LIMIT) {
103
+ const root = join2(home, ".claude");
104
+ if (!existsSync(root)) return null;
105
+ const parts = CLAUDE_CONFIG.filter((p) => existsSync(join2(root, p)));
106
+ if (!parts.length) return null;
107
+ const archive = await runTar(
108
+ root,
109
+ ["--exclude=*credentials*", "--exclude=*.credentials*", "--exclude=.env*", "--exclude=node_modules", "-czf", "-", ...parts],
110
+ null,
111
+ limit
112
+ );
113
+ return { archive, parts };
114
+ }
115
+ var UPLOAD_LIMIT, TooLargeError, ENV_EXCLUDES, CLAUDE_CONFIG;
116
+ var init_pack = __esm({
117
+ "src/pack.ts"() {
118
+ "use strict";
119
+ UPLOAD_LIMIT = 32 * 6 * 1024 * 1024;
120
+ TooLargeError = class extends Error {
121
+ limit;
122
+ constructor(limit) {
123
+ super(`over ${Math.round(limit / 1024 / 1024)} MiB packed`);
124
+ this.limit = limit;
125
+ }
126
+ };
127
+ ENV_EXCLUDES = [".env", ".env.local", ".env.*.local", ".env.development", ".env.production", ".env.test", ".env.staging", ".envrc"].map((p) => `--exclude=${p}`);
128
+ CLAUDE_CONFIG = ["settings.json", "CLAUDE.md", "skills", "agents", "commands"];
129
+ }
130
+ });
2
131
 
3
132
  // ../sandbox/src/types.ts
4
133
  var SIZES = {
@@ -268,9 +397,9 @@ function parseDotenv(input) {
268
397
  const unparsed = [];
269
398
  for (let i = 0; i < lines.length; i++) {
270
399
  const lineNo = i + 1;
271
- const line2 = lines[i].trim();
272
- if (!line2 || line2.startsWith("#")) continue;
273
- const m = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(line2);
400
+ const line3 = lines[i].trim();
401
+ if (!line3 || line3.startsWith("#")) continue;
402
+ const m = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(line3);
274
403
  if (!m) {
275
404
  unparsed.push(lineNo);
276
405
  continue;
@@ -387,6 +516,14 @@ function signChatgpt(t, headers) {
387
516
  headers.originator ??= "codex_cli_rs";
388
517
  }
389
518
 
519
+ // ../sandbox/src/servers.ts
520
+ var SERVER_SIZES = {
521
+ tiny: { id: "tiny", name: "tiny", cpuKind: "shared", cpus: 1, memoryMb: 512, diskGb: 1, monthlyUsd: 6, costUsd: 3.84, blurb: "a bot, a webhook, a cron" },
522
+ small: { id: "small", name: "small", cpuKind: "shared", cpus: 1, memoryMb: 1024, diskGb: 3, monthlyUsd: 10, costUsd: 7.15, blurb: "a small api or an assistant" },
523
+ medium: { id: "medium", name: "medium", cpuKind: "shared", cpus: 2, memoryMb: 2048, diskGb: 10, monthlyUsd: 20, costUsd: 14.89, blurb: "a busier service with a database" }
524
+ };
525
+ var SERVER_SIZE_IDS = Object.keys(SERVER_SIZES);
526
+
390
527
  // src/api.ts
391
528
  var UPLOAD_PART = 6 * 1024 * 1024;
392
529
  var ApiError = class extends Error {
@@ -794,9 +931,49 @@ var Api = class {
794
931
  removeTrigger(id) {
795
932
  return this.request("DELETE", `/v1/github/triggers/${enc(id)}`);
796
933
  }
934
+ /* always-on servers (docs/SERVERS.md) */
935
+ servers() {
936
+ return this.request("GET", "/v1/servers").then((r) => r.servers);
937
+ }
938
+ serverSizes() {
939
+ return this.request("GET", "/v1/servers/sizes");
940
+ }
941
+ createServer(name, size2) {
942
+ return this.request("POST", "/v1/servers", { body: { name, size: size2 } }).then((r) => r.server);
943
+ }
944
+ server(ref) {
945
+ return this.request("GET", `/v1/servers/${enc(ref)}`).then((r) => r.server);
946
+ }
947
+ serverCheckout(ref) {
948
+ return this.request("POST", `/v1/servers/${enc(ref)}/checkout`, { body: {} });
949
+ }
950
+ deployServer(ref, archive, cmd) {
951
+ return this.request("POST", `/v1/servers/${enc(ref)}/deploy${cmd ? `?cmd=${encodeURIComponent(cmd)}` : ""}`, { raw: archive, contentType: "application/gzip" }).then((r) => r.server);
952
+ }
953
+ deployServerFromPod(ref, podId, dir, command) {
954
+ return this.request("POST", `/v1/servers/${enc(ref)}/deploy`, { body: { podId, dir, ...command ? { command } : {} } }).then((r) => r.server);
955
+ }
956
+ restartServer(ref) {
957
+ return this.request("POST", `/v1/servers/${enc(ref)}/restart`, { body: {} });
958
+ }
959
+ serverEnv(ref, body) {
960
+ return this.request("PUT", `/v1/servers/${enc(ref)}/env`, { body }).then((r) => r.server);
961
+ }
962
+ podAlwaysOn(podId, body) {
963
+ return this.request("POST", `/v1/pods/${enc(podId)}/always-on`, { body }).then((r) => r.server);
964
+ }
965
+ serverLogs(ref) {
966
+ return this.request("GET", `/v1/servers/${enc(ref)}/logs`).then((r) => r.lines);
967
+ }
968
+ deleteServer(ref) {
969
+ return this.request("DELETE", `/v1/servers/${enc(ref)}`);
970
+ }
797
971
  sendVerification() {
798
972
  return this.request("POST", "/v1/auth/verify/send", { body: {} });
799
973
  }
974
+ reportBug(text2, expected, context) {
975
+ return this.request("POST", "/v1/bugs", { body: { text: text2, ...expected ? { expected } : {}, ...context ? { context } : {} } });
976
+ }
800
977
  terminalUrl(id, cols, rows2) {
801
978
  const q = new URLSearchParams({ token: this.tokenForUrl(), cols: String(cols), rows: String(rows2), client: this.client });
802
979
  return `${this.gateway.replace(/^http/, "ws")}/v1/pods/${enc(id)}/terminal?${q}`;
@@ -835,10 +1012,10 @@ async function readSse(body, onEvent) {
835
1012
  buf = buf.slice(i + 2);
836
1013
  let event = "message";
837
1014
  const data = [];
838
- for (const line2 of block.split("\n")) {
839
- if (line2.startsWith(":")) continue;
840
- if (line2.startsWith("event:")) event = line2.slice(6).trim();
841
- else if (line2.startsWith("data:")) data.push(line2.slice(5).replace(/^ /, ""));
1015
+ for (const line3 of block.split("\n")) {
1016
+ if (line3.startsWith(":")) continue;
1017
+ if (line3.startsWith("event:")) event = line3.slice(6).trim();
1018
+ else if (line3.startsWith("data:")) data.push(line3.slice(5).replace(/^ /, ""));
842
1019
  }
843
1020
  if (data.length) onEvent(event, data.join("\n"));
844
1021
  }
@@ -1396,9 +1573,9 @@ function banner(text2, options = {}) {
1396
1573
  const ink = new Uint8Array(cols * rows2);
1397
1574
  let x = x0;
1398
1575
  for (const g of glyphs) {
1399
- g.forEach((line2, y) => {
1400
- for (let i = 0; i < line2.length; i++)
1401
- if (line2[i] === "#") {
1576
+ g.forEach((line3, y) => {
1577
+ for (let i = 0; i < line3.length; i++)
1578
+ if (line3[i] === "#") {
1402
1579
  for (let k = 0; k < s2; k++)
1403
1580
  if (x + i * s2 + k < cols && y0 + y < rows2)
1404
1581
  ink[(y0 + y) * cols + x + i * s2 + k] = 1;
@@ -1481,26 +1658,26 @@ function banner(text2, options = {}) {
1481
1658
  const [core, edge] = [...shine[g]];
1482
1659
  const lines = [];
1483
1660
  for (let r = 0; r < rows2; r++) {
1484
- let line2 = "";
1661
+ let line3 = "";
1485
1662
  for (let c = 0; c < cols; c++) {
1486
1663
  const k = r * cols + c;
1487
1664
  const cell = grid2[k];
1488
1665
  if (c >= upTo) {
1489
- line2 += " ";
1666
+ line3 += " ";
1490
1667
  continue;
1491
1668
  }
1492
1669
  if (cell !== null) {
1493
- line2 += cell;
1670
+ line3 += cell;
1494
1671
  if (color && palette && cell !== " ")
1495
1672
  color[k] = (paper ? 0 : half) + 0;
1496
1673
  continue;
1497
1674
  }
1498
1675
  const d = Math.abs(c + slant * r - p);
1499
- line2 += d < width / 2 ? core : d < width ? edge ?? core : fill[g];
1676
+ line3 += d < width / 2 ? core : d < width ? edge ?? core : fill[g];
1500
1677
  if (color && palette)
1501
1678
  color[k] = (paper ? 0 : half) + 1 + bucket(c);
1502
1679
  }
1503
- lines.push(line2);
1680
+ lines.push(line3);
1504
1681
  }
1505
1682
  return lines.join("\n");
1506
1683
  };
@@ -1720,8 +1897,8 @@ var isPiece = (v) => v !== null && typeof v === "object" && "meta" in v && typeo
1720
1897
  var isClip = (v) => v !== null && typeof v === "object" && !(v instanceof Surface) && "src" in v && !("meta" in v);
1721
1898
  var unwrap = (v) => v !== null && typeof v === "object" && !("meta" in v) && !("src" in v) && isPiece(v.default) ? v.default : v;
1722
1899
  var textName = (text2) => {
1723
- const line2 = text2.split("\n").find((l) => l.trim())?.trim() ?? "";
1724
- return line2 ? `"${line2.slice(0, 24)}"` : "text";
1900
+ const line3 = text2.split("\n").find((l) => l.trim())?.trim() ?? "";
1901
+ return line3 ? `"${line3.slice(0, 24)}"` : "text";
1725
1902
  };
1726
1903
  function prepare(fn, v, keys = CLIP) {
1727
1904
  v = unwrap(v);
@@ -2292,10 +2469,10 @@ function freeze(src, at3) {
2292
2469
  }
2293
2470
  function named(src, name, o = {}) {
2294
2471
  const opts2 = object(o, "named()", ["note", "category"]);
2295
- const line2 = (v) => typeof v === "string" && !!v.trim() && !/[\u0000-\u001f\u007f-\u009f]/.test(v);
2296
- if (!line2(name))
2472
+ const line3 = (v) => typeof v === "string" && !!v.trim() && !/[\u0000-\u001f\u007f-\u009f]/.test(v);
2473
+ if (!line3(name))
2297
2474
  fail2(`named() takes a name, one line such as "hello", not ${shown(name)}`);
2298
- if (opts2.note !== void 0 && !line2(opts2.note))
2475
+ if (opts2.note !== void 0 && !line3(opts2.note))
2299
2476
  fail2(`named()'s note takes one line saying what you see, not ${shown(opts2.note)}`);
2300
2477
  const p = prepare("named", src);
2301
2478
  const asItWas = p.speed === 1 && !p.offset && !p.tint && !Object.keys(p.options).length;
@@ -2866,13 +3043,13 @@ function scan(src, options) {
2866
3043
  const pos = lineAt(t);
2867
3044
  if (pos === null)
2868
3045
  return copy(s2, g);
2869
- const line2 = forward ? pos : along - 1 - pos;
3046
+ const line3 = forward ? pos : along - 1 - pos;
2870
3047
  for (let y = 0, i = 0; y < g.rows; y++)
2871
3048
  for (let x = 0; x < g.cols; x++, i++) {
2872
3049
  const q = across2 ? y : x;
2873
- if (q === line2)
3050
+ if (q === line3)
2874
3051
  s2.put(i, char, ink >= 0 ? ink : g.chars[i] === EMPTY ? NONE : g.colors[i]);
2875
- else if (!reveal || (forward ? q < line2 : q > line2)) {
3052
+ else if (!reveal || (forward ? q < line3 : q > line3)) {
2876
3053
  s2.chars[i] = g.chars[i];
2877
3054
  s2.colors[i] = g.colors[i];
2878
3055
  }
@@ -3086,7 +3263,7 @@ function outline(src, options) {
3086
3263
  setup: () => {
3087
3264
  const solid = new Uint8Array(cols * rows2), out = new Uint8Array(cols * rows2), queue = new Int32Array(cols * rows2);
3088
3265
  const is = (a, x, y) => x >= 0 && x < cols && y >= 0 && y < rows2 && a[y * cols + x] === 1;
3089
- const line2 = (x, y) => is(out, x, y) && (is(solid, x - 1, y - 1) || is(solid, x, y - 1) || is(solid, x + 1, y - 1) || is(solid, x - 1, y) || is(solid, x + 1, y) || is(solid, x - 1, y + 1) || is(solid, x, y + 1) || is(solid, x + 1, y + 1));
3266
+ const line3 = (x, y) => is(out, x, y) && (is(solid, x - 1, y - 1) || is(solid, x, y - 1) || is(solid, x + 1, y - 1) || is(solid, x - 1, y) || is(solid, x + 1, y) || is(solid, x - 1, y + 1) || is(solid, x, y + 1) || is(solid, x + 1, y + 1));
3090
3267
  const over2 = { mask: null };
3091
3268
  return (t, s2, g) => {
3092
3269
  solid.fill(0);
@@ -3106,12 +3283,12 @@ function outline(src, options) {
3106
3283
  flood(solid, out, queue, cols, rows2);
3107
3284
  for (let y = 0; y < rows2; y++)
3108
3285
  for (let x = 0; x < cols; x++) {
3109
- if (!line2(x, y))
3286
+ if (!line3(x, y))
3110
3287
  continue;
3111
- const up2 = line2(x, y - 1) && (is(solid, x - 1, y) || is(solid, x - 1, y - 1) || is(solid, x + 1, y) || is(solid, x + 1, y - 1));
3112
- const down4 = line2(x, y + 1) && (is(solid, x - 1, y) || is(solid, x - 1, y + 1) || is(solid, x + 1, y) || is(solid, x + 1, y + 1));
3113
- const left = line2(x - 1, y) && (is(solid, x, y - 1) || is(solid, x - 1, y - 1) || is(solid, x, y + 1) || is(solid, x - 1, y + 1));
3114
- const right = line2(x + 1, y) && (is(solid, x, y - 1) || is(solid, x + 1, y - 1) || is(solid, x, y + 1) || is(solid, x + 1, y + 1));
3288
+ const up2 = line3(x, y - 1) && (is(solid, x - 1, y) || is(solid, x - 1, y - 1) || is(solid, x + 1, y) || is(solid, x + 1, y - 1));
3289
+ const down4 = line3(x, y + 1) && (is(solid, x - 1, y) || is(solid, x - 1, y + 1) || is(solid, x + 1, y) || is(solid, x + 1, y + 1));
3290
+ const left = line3(x - 1, y) && (is(solid, x, y - 1) || is(solid, x - 1, y - 1) || is(solid, x, y + 1) || is(solid, x - 1, y + 1));
3291
+ const right = line3(x + 1, y) && (is(solid, x, y - 1) || is(solid, x + 1, y - 1) || is(solid, x, y + 1) || is(solid, x + 1, y + 1));
3115
3292
  const bits = (up2 ? 1 : 0) | (down4 ? 2 : 0) | (left ? 4 : 0) | (right ? 8 : 0);
3116
3293
  if (!bits)
3117
3294
  continue;
@@ -3753,12 +3930,12 @@ var Surface = class _Surface {
3753
3930
  for (let i = 0; i < n2; i++)
3754
3931
  color[i] = colors[i] === NONE ? ink : colors[i];
3755
3932
  }
3756
- const line2 = this.#line;
3933
+ const line3 = this.#line;
3757
3934
  let out = "";
3758
3935
  for (let r = 0; r < rows2; r++) {
3759
3936
  for (let c = 0, i = r * cols; c < cols; c++, i++)
3760
- line2[c] = chars3[i] || 32;
3761
- out += (r ? "\n" : "") + String.fromCharCode.apply(null, line2);
3937
+ line3[c] = chars3[i] || 32;
3938
+ out += (r ? "\n" : "") + String.fromCharCode.apply(null, line3);
3762
3939
  }
3763
3940
  return out;
3764
3941
  }
@@ -4653,12 +4830,12 @@ function words(str2, what) {
4653
4830
  }
4654
4831
  function writeLines(s2, lines, lefts, top, k, transparent) {
4655
4832
  let left = Infinity, right = -Infinity;
4656
- lines.forEach((line2, r) => {
4657
- const lx = lefts[r], len = line2.length;
4833
+ lines.forEach((line3, r) => {
4834
+ const lx = lefts[r], len = line3.length;
4658
4835
  if (len)
4659
4836
  left = Math.min(left, lx), right = Math.max(right, lx + len);
4660
4837
  for (let i = 0; i < len; i++) {
4661
- const c = line2.charCodeAt(i);
4838
+ const c = line3.charCodeAt(i);
4662
4839
  if (transparent && c === 32)
4663
4840
  continue;
4664
4841
  const j = s2.index(lx + i, top + r);
@@ -4675,25 +4852,25 @@ function layout(str2, width, wrap2) {
4675
4852
  out.push(para.slice(0, width));
4676
4853
  continue;
4677
4854
  }
4678
- let line2 = "";
4855
+ let line3 = "";
4679
4856
  for (let word of para.split(" ").filter(Boolean)) {
4680
4857
  while (word.length > width) {
4681
- if (line2)
4682
- out.push(line2);
4683
- line2 = "";
4858
+ if (line3)
4859
+ out.push(line3);
4860
+ line3 = "";
4684
4861
  out.push(word.slice(0, width));
4685
4862
  word = word.slice(width);
4686
4863
  }
4687
- if (!line2)
4688
- line2 = word;
4689
- else if (line2.length + 1 + word.length <= width)
4690
- line2 += " " + word;
4864
+ if (!line3)
4865
+ line3 = word;
4866
+ else if (line3.length + 1 + word.length <= width)
4867
+ line3 += " " + word;
4691
4868
  else {
4692
- out.push(line2);
4693
- line2 = word;
4869
+ out.push(line3);
4870
+ line3 = word;
4694
4871
  }
4695
4872
  }
4696
- out.push(line2);
4873
+ out.push(line3);
4697
4874
  }
4698
4875
  return out;
4699
4876
  }
@@ -5403,7 +5580,7 @@ function cat() {
5403
5580
  const put = (r, c, s2) => [...s2].forEach((ch, i) => ch !== " " && c + i < cols && (g[r][c + i] = ch));
5404
5581
  const p = (u + 0.4) % BREATH / BREATH;
5405
5582
  const rise = p < 0.35 ? Math.sin(Math.PI / 2 * (p / 0.35)) ** 2 : p < 0.8 ? Math.cos(Math.PI / 2 * ((p - 0.35) / 0.45)) ** 2 : 0;
5406
- BACK[rise < 0.3 ? 0 : rise < 0.75 ? 1 : 2].forEach((line2, i) => put(3 + i, 0, line2));
5583
+ BACK[rise < 0.3 ? 0 : rise < 0.75 ? 1 : 2].forEach((line3, i) => put(3 + i, 0, line3));
5407
5584
  const fl = FLICK.find(([a, b2]) => u >= a && u < b2);
5408
5585
  const up2 = fl ? Math.sin(Math.PI * (u - fl[0]) / (fl[1] - fl[0])) : 0;
5409
5586
  TIP[up2 > 0.7 ? 2 : up2 > 0.25 ? 1 : 0].forEach(([r, c, s2]) => put(r, c, s2));
@@ -5511,9 +5688,9 @@ function spinners2({ names = meta2.options.names, labels = meta2.options.labels,
5511
5688
  const x0 = n2 % per * slot2, y0 = 1 + Math.floor(n2 / per) * (block + 1);
5512
5689
  const art = draw2(Math.floor(t * meta2.fps * speed2 / frames + 1e-3));
5513
5690
  const top = y0 + Math.floor((3 - art.length) / 2);
5514
- art.forEach((line2, j) => {
5515
- const left = x0 + Math.floor((slot2 - [...line2].length) / 2);
5516
- [...line2].forEach((g, i) => grid2[top + j][left + i] = g);
5691
+ art.forEach((line3, j) => {
5692
+ const left = x0 + Math.floor((slot2 - [...line3].length) / 2);
5693
+ [...line3].forEach((g, i) => grid2[top + j][left + i] = g);
5517
5694
  });
5518
5695
  if (labels)
5519
5696
  [...name].forEach((g, i) => grid2[y0 + 4][x0 + Math.floor((slot2 - name.length) / 2) + i] = g);
@@ -5552,8 +5729,8 @@ function inkBox(frame, seconds, step) {
5552
5729
  let bottom = -1;
5553
5730
  let right = -1;
5554
5731
  for (let t = 0; t < seconds; t += step) {
5555
- frame(t).split("\n").forEach((line2, y) => {
5556
- [...line2].forEach((ch, x) => {
5732
+ frame(t).split("\n").forEach((line3, y) => {
5733
+ [...line3].forEach((ch, x) => {
5557
5734
  if (ch === " ") return;
5558
5735
  top = Math.min(top, y);
5559
5736
  bottom = Math.max(bottom, y);
@@ -5905,9 +6082,9 @@ function formatTable(rows2, head, indent = 0) {
5905
6082
  );
5906
6083
  }
5907
6084
  function paintArt(on, a) {
5908
- return a.lines.map((line2, y) => {
6085
+ return a.lines.map((line3, y) => {
5909
6086
  const tones2 = a.tones?.[y];
5910
- if (!on || !tones2) return line2.trimEnd();
6087
+ if (!on || !tones2) return line3.trimEnd();
5911
6088
  let out = "";
5912
6089
  let run2 = "";
5913
6090
  let tone2;
@@ -5915,7 +6092,7 @@ function paintArt(on, a) {
5915
6092
  if (run2) out += tone2 ? paint2(true, tone2, run2) : run2;
5916
6093
  run2 = "";
5917
6094
  };
5918
- [...line2].forEach((ch, x) => {
6095
+ [...line3].forEach((ch, x) => {
5919
6096
  const t = ch === " " ? tone2 : tones2[x];
5920
6097
  if (t !== tone2) {
5921
6098
  flush();
@@ -5932,8 +6109,8 @@ function createUi(io) {
5932
6109
  const errColor = colorOn(io.stderr, io.env);
5933
6110
  const ui2 = {
5934
6111
  tty: Boolean(io.stdout.isTTY),
5935
- out: (line2 = "") => void io.stdout.write(line2 + "\n"),
5936
- err: (line2 = "") => void io.stderr.write(line2 + "\n"),
6112
+ out: (line3 = "") => void io.stdout.write(line3 + "\n"),
6113
+ err: (line3 = "") => void io.stderr.write(line3 + "\n"),
5937
6114
  c: (tone2, s2) => paint2(outColor, tone2, s2),
5938
6115
  ce: (tone2, s2) => paint2(errColor, tone2, s2),
5939
6116
  json: (v) => void io.stdout.write(JSON.stringify(v, null, 2) + "\n"),
@@ -6021,9 +6198,9 @@ function billingOff(e) {
6021
6198
  function hoursMeter(ui2, used, cap2) {
6022
6199
  if (!ui2.tty) return null;
6023
6200
  const scale2 = cap2 && cap2 > 0 ? cap2 : PLANS.pro.includedHours;
6024
- const [line2] = ui2.art(bar(used / scale2, 24));
6201
+ const [line3] = ui2.art(bar(used / scale2, 24));
6025
6202
  const of = cap2 && cap2 > 0 ? `of ${cap2}h` : `of ${scale2}h (what pro includes)`;
6026
- return `${line2} ${hours(used)} ${ui2.c("dim", of)}`;
6203
+ return `${line3} ${hours(used)} ${ui2.c("dim", of)}`;
6027
6204
  }
6028
6205
  var usage = {
6029
6206
  name: "usage",
@@ -7380,6 +7557,11 @@ var secrets = {
7380
7557
  import { spawnSync } from "node:child_process";
7381
7558
  import { rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
7382
7559
  import { resolve as resolve4 } from "node:path";
7560
+
7561
+ // src/version.ts
7562
+ var VERSION = "0.2.0";
7563
+
7564
+ // src/commands/team.ts
7383
7565
  async function checkSimilar(ctx, task2) {
7384
7566
  const similar = await ctx.api.similar(task2).catch(() => []);
7385
7567
  if (!similar.length || ctx.json) return { go: true, similar };
@@ -7796,6 +7978,35 @@ var verify = {
7796
7978
  ctx.ui.out(r.sent ? `sent to ${me.user.email}. open the link in it.` : "this gateway can't send email; ask whoever runs it");
7797
7979
  }
7798
7980
  };
7981
+ var bug = {
7982
+ name: "bug",
7983
+ aliases: ["report"],
7984
+ group: "account",
7985
+ usage: '"<what happened>" [--expected "<what you expected>"]',
7986
+ summary: "report a bug: a person reads every report, and you get an email when it changes",
7987
+ example: 'airgate bug "airgate down said the patch was empty after a crew"',
7988
+ notes: [
7989
+ "it sends your words plus the cli version, your OS and node version, and the folder name (never its contents). --no-context sends only your words.",
7990
+ "or pipe a longer report in: airgate bug < report.txt"
7991
+ ],
7992
+ flags: {
7993
+ expected: { type: "string", value: "text", help: "what you expected instead" },
7994
+ "no-context": { type: "boolean", help: "don't send the cli version, OS and folder name" }
7995
+ },
7996
+ async run(ctx) {
7997
+ let text2 = ctx.args.positionals.join(" ").trim();
7998
+ if (!text2 && !ctx.io.stdin.isTTY) {
7999
+ const chunks = [];
8000
+ for await (const c of ctx.io.stdin) chunks.push(Buffer.from(c));
8001
+ text2 = Buffer.concat(chunks).toString("utf8").trim();
8002
+ }
8003
+ if (text2.length < 5) throw new UsageError('airgate bug "<what happened>"', 'a sentence is enough; add --expected "<what you expected>"');
8004
+ const context = bool(ctx.args, "no-context") ? void 0 : { cli: VERSION, os: `${process.platform} ${process.arch}`, node: process.version, folder: ctx.io.cwd.split("/").pop() ?? "" };
8005
+ const r = await ctx.api.reportBug(text2.slice(0, 8e3), str(ctx.args, "expected"), context);
8006
+ if (ctx.json) return ctx.ui.json(r);
8007
+ ctx.ui.out(`${ctx.ui.c("ok", "reported")} ${r.bug.id}. a person reads it; you get an email when it changes.`);
8008
+ }
8009
+ };
7799
8010
 
7800
8011
  // src/commands/pods.ts
7801
8012
  import { execFileSync } from "node:child_process";
@@ -7924,7 +8135,7 @@ var launch = {
7924
8135
  network: { type: "string", value: "policy", choices: NETWORK_NAMES, default: "allowlist", help: "allowlist | open | off" },
7925
8136
  allow: { type: "list", value: "host", help: "extra hosts for the allowlist (repeatable, or comma-separated)" },
7926
8137
  "mask-term": { type: "list", value: "word", help: "always mask this word: a client, a codename (repeatable)" },
7927
- "auto-stop": { type: "string", value: "minutes", help: "sleep after this many idle minutes (default 10), or keep-awake (pro)" }
8138
+ "auto-stop": { type: "string", value: "minutes", help: 'sleep after this many idle minutes (default 10). for 24/7: airgate always-on <pod> --cmd "..."' }
7928
8139
  },
7929
8140
  async run(ctx) {
7930
8141
  if (ctx.args.positionals.length) throw new UsageError(`unexpected "${ctx.args.positionals[0]}"`, "name the pod with --name");
@@ -7989,10 +8200,11 @@ function specFromFlags(ctx, defaults = {}) {
7989
8200
  const network2 = str(a, "network") ?? "allowlist";
7990
8201
  const autoStop = str(a, "auto-stop");
7991
8202
  let autoStopMinutes = null;
7992
- if (autoStop === "keep-awake" || autoStop === "never") autoStopMinutes = 0;
7993
- else if (autoStop !== void 0) {
8203
+ if (autoStop === "keep-awake" || autoStop === "never" || autoStop === "0") {
8204
+ throw new UsageError("keep awake is gone: a pod sleeps when idle", 'for 24/7, make it always on once it is up: airgate always-on <pod> --cmd "node bot.js" (from $6 a month per server, on any plan, never counts against your hours)');
8205
+ } else if (autoStop !== void 0) {
7994
8206
  autoStopMinutes = Number(autoStop);
7995
- if (!Number.isInteger(autoStopMinutes) || autoStopMinutes < 1) throw new UsageError("--auto-stop: whole idle minutes \u2265 1, or keep-awake");
8207
+ if (!Number.isInteger(autoStopMinutes) || autoStopMinutes < 1) throw new UsageError("--auto-stop: whole idle minutes \u2265 1");
7996
8208
  }
7997
8209
  const repo = repoArg(ctx, str(a, "repo"));
7998
8210
  const template = str(a, "template") ?? "code";
@@ -8061,7 +8273,7 @@ var info = {
8061
8273
  ["network", s2.network + (s2.network === "allowlist" && s2.allow?.length ? ` \xB7 ${s2.allow.join(", ")}` : "")],
8062
8274
  ["mask", s2.masking ? `on${s2.maskTerms?.length ? ` \xB7 ${s2.maskTerms.length} terms` : ""}` : ctx.ui.c("warn", "off")],
8063
8275
  ["undo", s2.autoCheckpoint ? "auto checkpoints" : "manual only"],
8064
- ["auto-stop", s2.autoStopMinutes === 0 ? "keep awake (billed while awake)" : `sleeps after ${s2.autoStopMinutes ?? 10} min idle`]
8276
+ ["auto-stop", s2.autoStopMinutes === 0 ? "keep awake (never sleeps: every hour counts, ~720 a month)" : `sleeps after ${s2.autoStopMinutes ?? 10} min idle`]
8065
8277
  ];
8066
8278
  if (s2.repo) rows2.push(["repo", s2.repo]);
8067
8279
  for (const [i, j] of (s2.jobs ?? []).entries()) {
@@ -8169,7 +8381,7 @@ var templates = {
8169
8381
  const a = info2 ? info2.templates[id] : id === "code" ? { available: true } : { available: false, reason: "not on this gateway" };
8170
8382
  const avail = a?.available ? ui2.c("ok", "yes") : ui2.c("dim", a?.reason ?? "no");
8171
8383
  const rate = t.proOnly ? `pro ${price(PLANS.pro.hourlyUsd)}` : `${price(PLANS.payg.hourlyUsd)} \xB7 pro ${price(PLANS.pro.hourlyUsd)}`;
8172
- return [id, t.name, t.tagline + (t.alwaysOn ? ui2.c("dim", " \xB7 always on, idle is free") : ""), avail, rate];
8384
+ return [id, t.name, t.tagline + (t.alwaysOn ? ui2.c("dim", " \xB7 always reachable, asleep is free") : ""), avail, rate];
8173
8385
  });
8174
8386
  ui2.table(rows2, { head: ["template", "name", "", "available", "per active hour"] });
8175
8387
  ui2.out();
@@ -8352,118 +8564,15 @@ var log = {
8352
8564
  // src/commands/mcp.ts
8353
8565
  import { createInterface } from "node:readline";
8354
8566
 
8355
- // src/version.ts
8356
- var VERSION = "0.1.7";
8357
-
8358
8567
  // src/commands/updown.ts
8359
8568
  import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
8360
8569
  import { homedir as homedir2 } from "node:os";
8361
8570
  import { basename, dirname as dirname2, isAbsolute, join as join4, relative as relative2, resolve as resolve5 } from "node:path";
8362
-
8363
- // src/pack.ts
8364
- import { spawn, spawnSync as spawnSync2 } from "node:child_process";
8365
- import { existsSync, lstatSync, readdirSync } from "node:fs";
8366
- import { join as join2 } from "node:path";
8367
- var UPLOAD_LIMIT = 32 * 6 * 1024 * 1024;
8368
- var TooLargeError = class extends Error {
8369
- limit;
8370
- constructor(limit) {
8371
- super(`over ${Math.round(limit / 1024 / 1024)} MiB packed`);
8372
- this.limit = limit;
8373
- }
8374
- };
8375
- function isEnvFile(path) {
8376
- const name = path.split("/").pop() ?? "";
8377
- return /^\.env($|\.)/.test(name) && !/\.(example|sample|template|dist|defaults?|schema)$/.test(name);
8378
- }
8379
- var ENV_EXCLUDES = [".env", ".env.local", ".env.*.local", ".env.development", ".env.production", ".env.test", ".env.staging", ".envrc"].map((p) => `--exclude=${p}`);
8380
- function git(cwd, args, input) {
8381
- const r = spawnSync2("git", args, { cwd, input, maxBuffer: 256 * 1024 * 1024 });
8382
- return { status: r.status ?? (r.error ? 127 : 1), stdout: r.stdout?.toString("utf8") ?? "", stderr: r.stderr?.toString("utf8") ?? "", error: r.error };
8383
- }
8384
- function isGitRepo(dir) {
8385
- return git(dir, ["rev-parse", "--is-inside-work-tree"]).stdout.trim() === "true";
8386
- }
8387
- function gitTopLevel(dir) {
8388
- const r = git(dir, ["rev-parse", "--show-toplevel"]);
8389
- return r.status === 0 ? r.stdout.trim() : null;
8390
- }
8391
- function gitFiles(dir) {
8392
- const r = git(dir, ["ls-files", "-co", "--exclude-standard", "-z"]);
8393
- if (r.status !== 0) throw new Error(`git ls-files: ${r.stderr.trim()}`);
8394
- const seen = /* @__PURE__ */ new Set();
8395
- const out = [];
8396
- for (const f of r.stdout.split("\0")) {
8397
- if (!f || seen.has(f)) continue;
8398
- seen.add(f);
8399
- try {
8400
- lstatSync(join2(dir, f));
8401
- out.push(f);
8402
- } catch {
8403
- }
8404
- }
8405
- return out;
8406
- }
8407
- function runTar(cwd, args, input, limit = UPLOAD_LIMIT) {
8408
- return new Promise((resolve9, reject) => {
8409
- const child = spawn("tar", args, { cwd, env: { ...process.env, COPYFILE_DISABLE: "1" }, stdio: ["pipe", "pipe", "pipe"] });
8410
- const chunks = [];
8411
- let size2 = 0;
8412
- let err = "";
8413
- let over2 = false;
8414
- child.stdout.on("data", (d) => {
8415
- size2 += d.length;
8416
- if (size2 > limit) {
8417
- over2 = true;
8418
- child.kill();
8419
- return;
8420
- }
8421
- chunks.push(d);
8422
- });
8423
- child.stderr.on("data", (d) => err += d.toString());
8424
- child.on("error", (e) => reject(new Error(`tar: ${e.message}`)));
8425
- child.on("close", (code3) => {
8426
- if (over2) return reject(new TooLargeError(limit));
8427
- if (code3 !== 0) return reject(new Error(`tar failed: ${err.trim().split("\n").slice(-3).join(" ")}`));
8428
- resolve9(Buffer.concat(chunks));
8429
- });
8430
- child.stdin.on("error", () => void 0);
8431
- child.stdin.end(input ?? "");
8432
- });
8433
- }
8434
- async function packProject(dir, limit = UPLOAD_LIMIT) {
8435
- if (isGitRepo(dir)) {
8436
- const tracked = new Set(git(dir, ["ls-files", "-c", "-z"]).stdout.split("\0"));
8437
- const all = gitFiles(dir).filter((f) => !f.startsWith(".airgate/"));
8438
- const skipped2 = all.filter((f) => (isEnvFile(f) || f.split("/").pop() === ".envrc") && !tracked.has(f));
8439
- const files2 = all.filter((f) => !skipped2.includes(f));
8440
- if (!files2.length) throw new Error("no files to send (git sees nothing tracked or untracked here)");
8441
- const archive2 = await runTar(dir, ["--null", "-T", "-", "-czf", "-"], files2.join("\0") + "\0", limit);
8442
- return { archive: archive2, files: files2.length, git: true, skipped: skipped2 };
8443
- }
8444
- const skipped = readdirSync(dir).filter((f) => isEnvFile(f) || f === ".envrc");
8445
- const archive = await runTar(dir, ["--exclude=node_modules", "--exclude=.git", "--exclude=.airgate", ...ENV_EXCLUDES, "-czf", "-", "."], null, limit);
8446
- return { archive, files: -1, git: false, skipped };
8447
- }
8448
- var CLAUDE_CONFIG = ["settings.json", "CLAUDE.md", "skills", "agents", "commands"];
8449
- async function packClaudeConfig(home, limit = UPLOAD_LIMIT) {
8450
- const root = join2(home, ".claude");
8451
- if (!existsSync(root)) return null;
8452
- const parts = CLAUDE_CONFIG.filter((p) => existsSync(join2(root, p)));
8453
- if (!parts.length) return null;
8454
- const archive = await runTar(
8455
- root,
8456
- ["--exclude=*credentials*", "--exclude=*.credentials*", "--exclude=.env*", "--exclude=node_modules", "-czf", "-", ...parts],
8457
- null,
8458
- limit
8459
- );
8460
- return { archive, parts };
8461
- }
8462
-
8463
- // src/commands/updown.ts
8571
+ init_pack();
8464
8572
  import { gunzipSync } from "node:zlib";
8465
8573
 
8466
8574
  // src/sessions.ts
8575
+ init_pack();
8467
8576
  import { createHash, randomUUID } from "node:crypto";
8468
8577
  import { closeSync, existsSync as existsSync2, mkdirSync as mkdirSync2, mkdtempSync, openSync, readdirSync as readdirSync2, readFileSync as readFileSync4, readSync, realpathSync, renameSync as renameSync2, rmSync as rmSync3, statSync, writeFileSync as writeFileSync3 } from "node:fs";
8469
8578
  import { tmpdir } from "node:os";
@@ -9039,14 +9148,14 @@ function printSessions(ctx, list2) {
9039
9148
  }
9040
9149
  }
9041
9150
  function printFiles(ctx, blocks, toErr) {
9042
- const line2 = toErr ? ctx.ui.err : ctx.ui.out;
9151
+ const line3 = toErr ? ctx.ui.err : ctx.ui.out;
9043
9152
  const c = toErr ? ctx.ui.ce : ctx.ui.c;
9044
9153
  const files2 = orderFiles(blocks.map((b2) => b2.file));
9045
9154
  const width = Math.max(...files2.map((f) => f.path.length + (f.from ? f.from.length + 4 : 0)));
9046
9155
  for (const f of files2) {
9047
9156
  const name = f.from ? `${f.from} \u2192 ${f.path}` : f.path;
9048
9157
  const mark = f.risky ? c("warn", "!") : " ";
9049
- line2(` ${mark} ${STATUS_MARK[f.status]} ${f.risky ? name.padEnd(width) + " " + c("warn", f.reason ?? "") : name}`.trimEnd());
9158
+ line3(` ${mark} ${STATUS_MARK[f.status]} ${f.risky ? name.padEnd(width) + " " + c("warn", f.reason ?? "") : name}`.trimEnd());
9050
9159
  }
9051
9160
  }
9052
9161
  var down2 = {
@@ -9213,7 +9322,7 @@ var LineReader = class {
9213
9322
  }
9214
9323
  };
9215
9324
  function ttyPrompt(stdin, out) {
9216
- return (question, opts2 = {}) => new Promise((resolve9, reject) => {
9325
+ return (question, opts2 = {}) => new Promise((resolve10, reject) => {
9217
9326
  out.write(question);
9218
9327
  const decoder = new StringDecoder2("utf8");
9219
9328
  let answer = "";
@@ -9226,7 +9335,7 @@ function ttyPrompt(stdin, out) {
9226
9335
  stdin.pause();
9227
9336
  out.write("\n");
9228
9337
  if (err) reject(err);
9229
- else resolve9(answer);
9338
+ else resolve10(answer);
9230
9339
  };
9231
9340
  const onData = (d) => {
9232
9341
  for (const ch of decoder.write(d)) {
@@ -9253,16 +9362,16 @@ function ttyPrompt(stdin, out) {
9253
9362
  }
9254
9363
  function openInBrowser(url) {
9255
9364
  const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", '""', url]] : ["xdg-open", [url]];
9256
- return new Promise((resolve9) => {
9365
+ return new Promise((resolve10) => {
9257
9366
  try {
9258
9367
  const child = spawn2(cmd, args, { stdio: "ignore", detached: true });
9259
- child.on("error", () => resolve9(false));
9368
+ child.on("error", () => resolve10(false));
9260
9369
  child.on("spawn", () => {
9261
9370
  child.unref();
9262
- resolve9(true);
9371
+ resolve10(true);
9263
9372
  });
9264
9373
  } catch {
9265
- resolve9(false);
9374
+ resolve10(false);
9266
9375
  }
9267
9376
  });
9268
9377
  }
@@ -9359,6 +9468,7 @@ async function runCommand(ctx, cmd, argv, cwd) {
9359
9468
  import { existsSync as existsSync5, readFileSync as readFileSync8, rmSync as rmSync5, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
9360
9469
  import { basename as basename2, resolve as resolve6 } from "node:path";
9361
9470
  import { randomBytes } from "node:crypto";
9471
+ init_pack();
9362
9472
 
9363
9473
  // src/relay.ts
9364
9474
  import { chmodSync as chmodSync2, existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
@@ -9863,6 +9973,7 @@ When you are done, leave your changes in the files there (no need to commit or p
9863
9973
  // src/commands/crews.ts
9864
9974
  import { existsSync as existsSync6, readFileSync as readFileSync9, realpathSync as realpathSync3, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync7 } from "node:fs";
9865
9975
  import { relative as relative3, resolve as resolve7 } from "node:path";
9976
+ init_pack();
9866
9977
  var KINDS = ["race", "contest", "council", "squad", "split"];
9867
9978
  var ACTIONS = ["show", "say", "stop", "down", "rm", "list"];
9868
9979
  var AGENT_ALIAS = { claude: "claude-code", "claude-code": "claude-code", codex: "codex", opencode: "opencode", pi: "pi" };
@@ -10508,6 +10619,67 @@ var TOOLS = [
10508
10619
  return ctx.api.note(need(a, "text"));
10509
10620
  }
10510
10621
  },
10622
+ {
10623
+ name: "pod_always_on",
10624
+ description: "Keep something from one of the user's pods running 24/7 (a bot, a webhook, a small api): it runs the pod's folder with a start command on a small server of its own that never sleeps, with its own https address. A flat monthly price per server on any plan (from $6), never counted against hours: only when the user asked for it. Running it again redeploys. When billing is on the result has a checkoutUrl the user must open to pay.",
10625
+ inputSchema: obj(
10626
+ {
10627
+ pod: POD,
10628
+ command: s('the start command, e.g. "node bot.js"'),
10629
+ dir: s("a folder in the pod's workspace (default: all of it)"),
10630
+ size: s("tiny ($6/month, default), small ($10) or medium ($20)")
10631
+ },
10632
+ ["pod", "command"]
10633
+ ),
10634
+ async call(ctx, a) {
10635
+ const p = await resolvePod(ctx, need(a, "pod"));
10636
+ const out = await ctx.api.podAlwaysOn(p.id, { command: need(a, "command"), dir: typeof a.dir === "string" ? a.dir : "", size: typeof a.size === "string" ? a.size : "tiny" });
10637
+ return { server: out.name, state: out.state, url: out.url, command: out.command, ...out.checkoutUrl ? { checkoutUrl: out.checkoutUrl } : {}, ...out.error ? { error: out.error } : {} };
10638
+ }
10639
+ },
10640
+ {
10641
+ name: "server_list",
10642
+ description: "List the user's always-on servers (small machines that never sleep, for a bot, a webhook or a small api): name, size, state, https address, what they run.",
10643
+ inputSchema: obj({}, []),
10644
+ async call(ctx) {
10645
+ return (await ctx.api.servers()).map((x) => ({ name: x.name, size: x.size, state: x.state, url: x.url, command: x.command, pod: x.podId ?? null }));
10646
+ }
10647
+ },
10648
+ {
10649
+ name: "server_deploy",
10650
+ description: "Deploy code to one of the user's always-on servers: a folder on this machine (packed like airgate up, .env files left out) or a folder in one of their pods, plus the command that starts it. The code gets PORT=8080 (served at the server's https address) and DATA_DIR=/data. It replaces what the server runs: only when the user asked for it.",
10651
+ inputSchema: obj(
10652
+ {
10653
+ server: s("the server name"),
10654
+ command: s('the start command, e.g. "node bot.js" (kept from the last deploy if left out)'),
10655
+ dir: s("a folder on this machine (default: the current one); with pod: a folder in the pod's workspace"),
10656
+ pod: s("deploy from this pod instead (name or id)")
10657
+ },
10658
+ ["server"]
10659
+ ),
10660
+ async call(ctx, a) {
10661
+ const name = need(a, "server");
10662
+ const command = typeof a.command === "string" ? a.command : void 0;
10663
+ if (typeof a.pod === "string" && a.pod) {
10664
+ const p = await resolvePod(ctx, a.pod);
10665
+ const out2 = await ctx.api.deployServerFromPod(name, p.id, typeof a.dir === "string" ? a.dir : "", command);
10666
+ return { server: out2.name, url: out2.url, command: out2.command };
10667
+ }
10668
+ const { packProject: packProject2 } = await Promise.resolve().then(() => (init_pack(), pack_exports));
10669
+ const { resolve: resolve10 } = await import("node:path");
10670
+ const packed = await packProject2(resolve10(ctx.io.cwd, typeof a.dir === "string" && a.dir ? a.dir : "."), 50 * 1024 * 1024);
10671
+ const out = await ctx.api.deployServer(name, packed.archive, command);
10672
+ return { server: out.name, url: out.url, command: out.command, leftOut: packed.skipped };
10673
+ }
10674
+ },
10675
+ {
10676
+ name: "server_logs",
10677
+ description: "Recent log lines of one of the user's always-on servers (what its command printed).",
10678
+ inputSchema: obj({ server: s("the server name") }, ["server"]),
10679
+ async call(ctx, a) {
10680
+ return (await ctx.api.serverLogs(need(a, "server"))).slice(-100);
10681
+ }
10682
+ },
10511
10683
  {
10512
10684
  name: "pod_share",
10513
10685
  description: "Share one of the user's pods with their team workspace (teammates can open its terminal, files and checkpoints and fork it; never delete it), or stop sharing. Only when the user asks for it.",
@@ -10651,11 +10823,11 @@ var mcp = {
10651
10823
  `));
10652
10824
  if (relayer) ctx.io.stderr.write(`airgate mcp: signing pods' ${relayer.vendors.join(" / ")} calls from this laptop while this runs
10653
10825
  `);
10654
- for await (const line2 of rl) {
10655
- if (!line2.trim()) continue;
10826
+ for await (const line3 of rl) {
10827
+ if (!line3.trim()) continue;
10656
10828
  let msg;
10657
10829
  try {
10658
- msg = JSON.parse(line2);
10830
+ msg = JSON.parse(line3);
10659
10831
  } catch {
10660
10832
  send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
10661
10833
  continue;
@@ -11100,21 +11272,21 @@ function pad2(s2, w) {
11100
11272
  function wrap(s2, w) {
11101
11273
  const out = [];
11102
11274
  for (const para of s2.split("\n")) {
11103
- let line2 = "";
11275
+ let line3 = "";
11104
11276
  for (const word of para.split(/(\s+)/)) {
11105
- if ([...line2 + word].length <= w) {
11106
- line2 += word;
11277
+ if ([...line3 + word].length <= w) {
11278
+ line3 += word;
11107
11279
  continue;
11108
11280
  }
11109
- if (line2.trim()) out.push(line2.trimEnd());
11281
+ if (line3.trim()) out.push(line3.trimEnd());
11110
11282
  let rest = word.trimStart();
11111
11283
  while ([...rest].length > w) {
11112
11284
  out.push([...rest].slice(0, w).join(""));
11113
11285
  rest = [...rest].slice(w).join("");
11114
11286
  }
11115
- line2 = rest;
11287
+ line3 = rest;
11116
11288
  }
11117
- out.push(line2.trimEnd());
11289
+ out.push(line3.trimEnd());
11118
11290
  }
11119
11291
  return out;
11120
11292
  }
@@ -11661,8 +11833,8 @@ function header(c, s2) {
11661
11833
  if (x + right.length + 1 < c.cols) c.text(c.cols - right.length - 1, 0, right, { fg: s2.connected ? "ok" : "warn" });
11662
11834
  }
11663
11835
  function drawArt(c, x, y, a, w = Infinity, h = Infinity) {
11664
- a.lines.slice(0, Math.max(0, h)).forEach((line2, r) => {
11665
- const chars3 = [...line2].slice(0, Math.max(0, w));
11836
+ a.lines.slice(0, Math.max(0, h)).forEach((line3, r) => {
11837
+ const chars3 = [...line3].slice(0, Math.max(0, w));
11666
11838
  const tones2 = a.tones?.[r];
11667
11839
  let k = 0;
11668
11840
  while (k < chars3.length) {
@@ -11691,8 +11863,8 @@ function splash(c, s2) {
11691
11863
  y += 2;
11692
11864
  }
11693
11865
  const mark = "\u22A0 ";
11694
- const line2 = mark + TAGLINE;
11695
- const lx = Math.max(0, Math.floor((c.cols - line2.length) / 2));
11866
+ const line3 = mark + TAGLINE;
11867
+ const lx = Math.max(0, Math.floor((c.cols - line3.length) / 2));
11696
11868
  c.text(lx, y, mark, { bold: true });
11697
11869
  c.text(lx + mark.length, y, TAGLINE, { fg: "dim" }, c.cols - lx - mark.length);
11698
11870
  const skip = "any key skips";
@@ -12035,8 +12207,8 @@ function modal(c, s2, m) {
12035
12207
  (m.checkpoints ?? []).slice(start, start + rowsAvail).forEach((cp, i) => {
12036
12208
  const isSel = start + i === m.sel;
12037
12209
  c.text(b2.x + 2, b2.y + 1 + i, isSel ? "> " : " ", { bold: isSel });
12038
- const line2 = `${pad2(`#${start + i + 1}`, 4)} ${pad2(ago2(cp.at, s2.now), 4)} ${pad2(cp.source, 6)} ${cp.label}`;
12039
- c.text(b2.x + 4, b2.y + 1 + i, pad2(line2, b2.w - 6), { inverse: isSel, fg: cp.source === "auto" && !isSel ? "dim" : void 0 });
12210
+ const line3 = `${pad2(`#${start + i + 1}`, 4)} ${pad2(ago2(cp.at, s2.now), 4)} ${pad2(cp.source, 6)} ${cp.label}`;
12211
+ c.text(b2.x + 4, b2.y + 1 + i, pad2(line3, b2.w - 6), { inverse: isSel, fg: cp.source === "auto" && !isSel ? "dim" : void 0 });
12040
12212
  });
12041
12213
  c.text(b2.x + 2, b2.y + b2.h - 2, "enter rewind \xB7 r race from here \xB7 esc close", { fg: "dim" }, b2.w - 4);
12042
12214
  return;
@@ -12103,7 +12275,7 @@ function formModal(c, s2, f) {
12103
12275
  v = text2(f.task, "what the agent should do each time");
12104
12276
  break;
12105
12277
  case "template":
12106
- v = `\u2039 ${t.name} \u203A ${t.proOnly ? "pro \xB7 " : ""}${t.alwaysOn ? "always on \xB7 " : ""}${t.tagline}`;
12278
+ v = `\u2039 ${t.name} \u203A ${t.proOnly ? "pro \xB7 " : ""}${t.alwaysOn ? "always reachable \xB7 " : ""}${t.tagline}`;
12107
12279
  break;
12108
12280
  case "agent": {
12109
12281
  const a = AGENT_LIST[f.agent];
@@ -12659,6 +12831,158 @@ var snapshot2 = {
12659
12831
  }
12660
12832
  };
12661
12833
 
12834
+ // src/commands/servers.ts
12835
+ import { resolve as resolve9 } from "node:path";
12836
+ init_pack();
12837
+ var memory = (mb) => mb >= 1024 ? `${mb / 1024} GB` : `${mb} MB`;
12838
+ function line2(ctx, s2) {
12839
+ const state = s2.state === "running" ? ctx.ui.c("ok", "running") : s2.state === "failed" ? ctx.ui.c("bad", "failed") : ctx.ui.c("warn", s2.state.replace("_", " "));
12840
+ return [s2.name, SERVER_SIZES[s2.size].name, state, s2.url ?? ctx.ui.c("dim", "(no address yet)"), s2.command ? ctx.ui.c("dim", s2.command) : ctx.ui.c("dim", "nothing deployed")];
12841
+ }
12842
+ var server = {
12843
+ name: "server",
12844
+ aliases: ["servers"],
12845
+ group: "pods",
12846
+ usage: "[ls|create|deploy|logs|env|restart|rm] \u2026",
12847
+ summary: "always-on servers: a small machine that never sleeps, for a bot, a webhook or a small api",
12848
+ example: 'airgate server deploy chat-bot --cmd "node bot.js"',
12849
+ notes: [
12850
+ "airgate server [ls] your servers",
12851
+ `airgate server create <name> [--size ${SERVER_SIZE_IDS.join("|")}] ${SERVER_SIZE_IDS.map((id) => `${id} $${SERVER_SIZES[id].monthlyUsd}`).join(", ")} a month; opens the checkout`,
12852
+ `airgate server deploy <name> [dir] --cmd "<start command>" this folder (no .env files) runs on it; --pod <pod> [--dir d] deploys a pod's folder`,
12853
+ "airgate server logs <name> what it printed",
12854
+ "airgate server env <name> --secrets A,B which of your account secrets it gets",
12855
+ "airgate server restart <name> \xB7 rm <name> rm deletes it now and ends its subscription",
12856
+ "what it runs gets PORT=8080 (served at its https address) and DATA_DIR=/data (kept). node and python are installed."
12857
+ ],
12858
+ flags: {
12859
+ size: { type: "string", value: "size", help: `create: ${SERVER_SIZE_IDS.join(", ")} (default tiny)` },
12860
+ cmd: { type: "string", value: "command", help: "deploy: the command that starts it" },
12861
+ pod: { type: "string", value: "pod", help: "deploy: from this pod instead of this folder" },
12862
+ dir: { type: "string", value: "dir", help: "deploy --pod: the folder in its workspace" },
12863
+ secrets: { type: "string", value: "names", help: 'env: account secrets it gets (comma separated; "" for none)' },
12864
+ yes: { type: "boolean", short: "y", help: "don't ask" }
12865
+ },
12866
+ async run(ctx) {
12867
+ const { ui: ui2 } = ctx;
12868
+ const [sub = "ls", name, extra] = ctx.args.positionals;
12869
+ if (sub === "ls" || sub === "list") {
12870
+ const list2 = await ctx.api.servers();
12871
+ if (ctx.json) return ui2.json(list2);
12872
+ if (!list2.length) {
12873
+ ui2.out(ui2.c("dim", "no servers yet."));
12874
+ return void ui2.out(ui2.c("dim", `make one: airgate server create chat-bot (${SERVER_SIZE_IDS.map((id) => `${id} $${SERVER_SIZES[id].monthlyUsd}`).join(", ")} a month)`));
12875
+ }
12876
+ return void ui2.table(list2.map((s2) => line2(ctx, s2)), { head: ["server", "size", "", "address", "runs"] });
12877
+ }
12878
+ if (!name) throw new UsageError(`airgate server ${sub} <name>`);
12879
+ if (sub === "create") {
12880
+ const size2 = str(ctx.args, "size") ?? "tiny";
12881
+ if (!SERVER_SIZE_IDS.includes(size2)) throw new UsageError(`--size: ${SERVER_SIZE_IDS.join(", ")}`);
12882
+ const z = SERVER_SIZES[size2];
12883
+ const s2 = await ui2.spin(`making ${name}`, ctx.api.createServer(name, size2));
12884
+ if (ctx.json) return ui2.json(s2);
12885
+ if (s2.checkoutUrl) {
12886
+ ui2.out(`${name}: ${z.cpus} cpu, ${memory(z.memoryMb)}, ${z.diskGb} GB disk, $${z.monthlyUsd} a month. pay for it and it starts:`);
12887
+ return void await openOrPrint(ctx, s2.checkoutUrl, `${name} checkout`);
12888
+ }
12889
+ return void ui2.out(`${ui2.c("ok", "up")} ${name} at ${s2.url}. put code on it: airgate server deploy ${name} --cmd "node bot.js"`);
12890
+ }
12891
+ if (sub === "deploy") {
12892
+ const cmd = str(ctx.args, "cmd");
12893
+ const podRef = str(ctx.args, "pod");
12894
+ let s2;
12895
+ if (podRef) {
12896
+ const pod = await resolvePod(ctx, podRef);
12897
+ s2 = await ui2.spin(`deploying ${pod.spec.name}:~/workspace/${str(ctx.args, "dir") ?? ""} to ${name}`, ctx.api.deployServerFromPod(name, pod.id, str(ctx.args, "dir") ?? "", cmd));
12898
+ } else {
12899
+ const dir = resolve9(ctx.io.cwd, extra ?? ".");
12900
+ let archive;
12901
+ try {
12902
+ const packed = await ui2.spin(`packing ${dir}`, packProject(dir, 50 * 1024 * 1024));
12903
+ archive = packed.archive;
12904
+ if (packed.skipped.length && !ctx.json) ui2.err(ui2.ce("dim", `left out: ${packed.skipped.join(", ")} (env files never travel; give it secrets: airgate server env ${name} --secrets \u2026)`));
12905
+ } catch (e) {
12906
+ if (e instanceof TooLargeError) throw new CliError("the folder is over 50 MB packed", "leave out build output and dependencies (they install on the server), or deploy from a pod");
12907
+ throw e;
12908
+ }
12909
+ s2 = await ui2.spin(`deploying to ${name}`, ctx.api.deployServer(name, archive, cmd));
12910
+ }
12911
+ if (ctx.json) return ui2.json(s2);
12912
+ return void ui2.out(`${ui2.c("ok", "deployed")} ${name}: ${s2.command}. it restarts into it now; ${s2.url ?? ""}`);
12913
+ }
12914
+ if (sub === "logs") {
12915
+ const lines = await ctx.api.serverLogs(name);
12916
+ if (ctx.json) return ui2.json(lines);
12917
+ if (!lines.length) return void ui2.out(ui2.c("dim", "nothing yet"));
12918
+ for (const l of lines.slice(-200)) ui2.out(`${ui2.c("dim", l.at.slice(11, 19))} ${l.message}`);
12919
+ return;
12920
+ }
12921
+ if (sub === "env") {
12922
+ const raw = str(ctx.args, "secrets");
12923
+ const cmd = str(ctx.args, "cmd");
12924
+ if (raw === void 0 && !cmd) {
12925
+ const s3 = await ctx.api.server(name);
12926
+ if (ctx.json) return ui2.json({ secrets: s3.secrets, command: s3.command });
12927
+ return void ui2.out(`${name} gets: ${s3.secrets.length ? s3.secrets.join(", ") : ui2.c("dim", "no secrets")}${s3.command ? ` \xB7 runs ${s3.command}` : ""}`);
12928
+ }
12929
+ const names = raw === void 0 ? void 0 : raw.split(",").map((x) => x.trim()).filter(Boolean);
12930
+ const s2 = await ctx.api.serverEnv(name, { ...names ? { secrets: names } : {}, ...cmd ? { command: cmd } : {} });
12931
+ if (ctx.json) return ui2.json(s2);
12932
+ return void ui2.out(`${name} gets: ${s2.secrets.length ? s2.secrets.join(", ") : "no secrets"}. it restarts with them.`);
12933
+ }
12934
+ if (sub === "restart") {
12935
+ await ctx.api.restartServer(name);
12936
+ return ctx.json ? ui2.json({ ok: true }) : void ui2.out(`${name} is restarting.`);
12937
+ }
12938
+ if (sub === "pay" || sub === "checkout") {
12939
+ const r = await ctx.api.serverCheckout(name);
12940
+ return ctx.json ? ui2.json(r) : void await openOrPrint(ctx, r.url, `${name} checkout`);
12941
+ }
12942
+ if (sub === "rm" || sub === "delete") {
12943
+ if (!await confirm(ctx, `delete ${name}? its machine, disk and address go now, and its subscription ends today.`)) throw new CliError("nothing deleted");
12944
+ await ctx.api.deleteServer(name);
12945
+ return ctx.json ? ui2.json({ ok: true }) : void ui2.out(`deleted ${name}.`);
12946
+ }
12947
+ throw new UsageError(`unknown "${sub}"`, "airgate server ls|create|deploy|logs|env|restart|rm");
12948
+ }
12949
+ };
12950
+ var alwaysOn = {
12951
+ name: "always-on",
12952
+ group: "pods",
12953
+ usage: '<pod> --cmd "<start command>" [--dir <folder>] [--size tiny|small|medium]',
12954
+ summary: "keep something from a pod running 24/7: from $6 a month per server, on any plan, never counts against your hours",
12955
+ example: 'airgate always-on web --cmd "node bot.js" --dir bot',
12956
+ notes: [
12957
+ "the pod still sleeps when idle: its folder runs on a small server of its own that never sleeps, with its own https address. run it again to redeploy.",
12958
+ `sizes: ${SERVER_SIZE_IDS.map((id) => `${id} $${SERVER_SIZES[id].monthlyUsd}`).join(", ")} a month. it gets PORT=8080 (served at its address) and DATA_DIR=/data (kept).`,
12959
+ "see and manage them: airgate server"
12960
+ ],
12961
+ flags: {
12962
+ cmd: { type: "string", value: "command", help: "the command that starts it" },
12963
+ dir: { type: "string", value: "folder", help: "a folder in the pod's workspace (default: all of it)" },
12964
+ size: { type: "string", value: "size", help: `${SERVER_SIZE_IDS.join(", ")} (default tiny)` }
12965
+ },
12966
+ async run(ctx) {
12967
+ const { ui: ui2 } = ctx;
12968
+ const [ref, extra] = ctx.args.positionals;
12969
+ if (!ref || extra) throw new UsageError('airgate always-on <pod> --cmd "<start command>"');
12970
+ const cmd = str(ctx.args, "cmd");
12971
+ if (!cmd) throw new UsageError('--cmd: what starts it, e.g. --cmd "node bot.js"');
12972
+ const size2 = str(ctx.args, "size") ?? "tiny";
12973
+ if (!SERVER_SIZE_IDS.includes(size2)) throw new UsageError(`--size: ${SERVER_SIZE_IDS.join(", ")}`);
12974
+ const pod = await resolvePod(ctx, ref);
12975
+ const s2 = await ui2.spin(`making ${pod.spec.name} always on`, ctx.api.podAlwaysOn(pod.id, { command: cmd, dir: str(ctx.args, "dir") ?? "", size: size2 }));
12976
+ if (ctx.json) return ui2.json(s2);
12977
+ if (s2.checkoutUrl) {
12978
+ ui2.out(`${pod.spec.name} always on: a ${size2} server, $${SERVER_SIZES[size2].monthlyUsd} a month. pay for it and it starts with ${cmd}:`);
12979
+ return void await openOrPrint(ctx, s2.checkoutUrl, `${s2.name} checkout`);
12980
+ }
12981
+ if (s2.error) ui2.err(ui2.ce("warn", s2.error));
12982
+ ui2.out(`${ui2.c("ok", "always on")} ${pod.spec.name}: ${s2.command ?? cmd} at ${s2.url}. manage it: airgate server ${s2.name}`);
12983
+ }
12984
+ };
12985
+
12662
12986
  // src/cli.ts
12663
12987
  var help = {
12664
12988
  name: "help",
@@ -12684,6 +13008,9 @@ var COMMANDS = [
12684
13008
  reset,
12685
13009
  deleteAccount,
12686
13010
  verify,
13011
+ bug,
13012
+ server,
13013
+ alwaysOn,
12687
13014
  team,
12688
13015
  github,
12689
13016
  pods,
@@ -12785,7 +13112,7 @@ function printCommandHelp(ui2, c) {
12785
13112
  ui2.out(`example: ${c.example}`);
12786
13113
  }
12787
13114
  function report(ui2, err, cmd, env) {
12788
- const line2 = (msg, hint) => {
13115
+ const line3 = (msg, hint) => {
12789
13116
  ui2.err(`${ui2.ce("bad", "error")}: ${msg}`);
12790
13117
  if (hint) ui2.err(ui2.ce("dim", hint));
12791
13118
  };
@@ -12797,35 +13124,35 @@ function report(ui2, err, cmd, env) {
12797
13124
  return 2;
12798
13125
  }
12799
13126
  if (err instanceof CliError) {
12800
- line2(err.message, err.hint);
13127
+ line3(err.message, err.hint);
12801
13128
  return err.exitCode;
12802
13129
  }
12803
13130
  if (err instanceof NetworkError) {
12804
- line2(err.message, "is it up? airgate status \xB7 another gateway: airgate login --gateway <url> or AIRGATE_GATEWAY");
13131
+ line3(err.message, "is it up? airgate status \xB7 another gateway: airgate login --gateway <url> or AIRGATE_GATEWAY");
12805
13132
  return 1;
12806
13133
  }
12807
13134
  if (err instanceof ApiError) {
12808
13135
  if (err.status === 401 && err.code !== "bad_credentials") {
12809
- if (env.AIRGATE_TOKEN) line2("AIRGATE_TOKEN was not accepted (expired, revoked, or for another gateway)", "unset it, or run: airgate login");
13136
+ if (env.AIRGATE_TOKEN) line3("AIRGATE_TOKEN was not accepted (expired, revoked, or for another gateway)", "unset it, or run: airgate login");
12810
13137
  else ui2.err(`not signed in. run: airgate login`);
12811
13138
  return 1;
12812
13139
  }
12813
13140
  if (err.code === "plan_required") {
12814
- line2(err.message.charAt(0).toLowerCase() + err.message.slice(1));
13141
+ line3(err.message.charAt(0).toLowerCase() + err.message.slice(1));
12815
13142
  const url = typeof err.extra.checkoutUrl === "string" ? err.extra.checkoutUrl : void 0;
12816
13143
  ui2.err(url ? `upgrade: ${url}` : `upgrade: airgate upgrade pro ($${PLANS.pro.monthlyUsd}/month, ${PLANS.pro.includedHours}h included)`);
12817
13144
  return 1;
12818
13145
  }
12819
13146
  if (err.code === "template_unavailable") {
12820
- line2(err.message.charAt(0).toLowerCase() + err.message.slice(1), "see what this gateway offers: airgate templates");
13147
+ line3(err.message.charAt(0).toLowerCase() + err.message.slice(1), "see what this gateway offers: airgate templates");
12821
13148
  return 1;
12822
13149
  }
12823
13150
  if (err.code === "team_plan_required" || err.code === "no_seat") {
12824
- line2(err.message.charAt(0).toLowerCase() + err.message.slice(1), err.code === "no_seat" ? "add one: airgate team seats <n>" : "the owner starts it: airgate team upgrade --seats <n>");
13151
+ line3(err.message.charAt(0).toLowerCase() + err.message.slice(1), err.code === "no_seat" ? "add one: airgate team seats <n>" : "the owner starts it: airgate team upgrade --seats <n>");
12825
13152
  return 1;
12826
13153
  }
12827
13154
  if (err.status === 402 || err.code === "billing_required") {
12828
- line2(`${err.message}`);
13155
+ line3(`${err.message}`);
12829
13156
  const p = PLANS;
12830
13157
  ui2.err(` payg no monthly fee, $${p.payg.hourlyUsd.toFixed(2)}/active hour`);
12831
13158
  ui2.err(` pro $${p.pro.monthlyUsd}/month, ${p.pro.includedHours}h included, then $${p.pro.hourlyUsd.toFixed(2)}/h`);
@@ -12834,30 +13161,30 @@ function report(ui2, err, cmd, env) {
12834
13161
  return 1;
12835
13162
  }
12836
13163
  if (err.code === "signups_closed") {
12837
- line2("sign-ups aren't open yet", "have an invite token? airgate login --token <token>");
13164
+ line3("sign-ups aren't open yet", "have an invite token? airgate login --token <token>");
12838
13165
  return 1;
12839
13166
  }
12840
13167
  if (err.code === "signup_code_required") {
12841
- line2("an early access code is needed to sign up", "airgate signup --code <code>");
13168
+ line3("an early access code is needed to sign up", "airgate signup --code <code>");
12842
13169
  return 1;
12843
13170
  }
12844
13171
  if (err.code === "email_taken") {
12845
- line2("an account with this email exists", "sign in: airgate login \xB7 forgot the password? airgate forgot");
13172
+ line3("an account with this email exists", "sign in: airgate login \xB7 forgot the password? airgate forgot");
12846
13173
  return 1;
12847
13174
  }
12848
13175
  if (err.status === 429) {
12849
- line2(err.message.toLowerCase(), "wait a few minutes and try again");
13176
+ line3(err.message.toLowerCase(), "wait a few minutes and try again");
12850
13177
  return 1;
12851
13178
  }
12852
13179
  if (err.code === "capacity") {
12853
- line2(err.message.toLowerCase(), "every machine is busy; try again in a minute");
13180
+ line3(err.message.toLowerCase(), "every machine is busy; try again in a minute");
12854
13181
  return 1;
12855
13182
  }
12856
13183
  if (err.code === "not_ready") {
12857
- line2(err.message.toLowerCase(), "wait for it: airgate info <pod>");
13184
+ line3(err.message.toLowerCase(), "wait for it: airgate info <pod>");
12858
13185
  return 1;
12859
13186
  }
12860
- line2(err.message.charAt(0).toLowerCase() + err.message.slice(1));
13187
+ line3(err.message.charAt(0).toLowerCase() + err.message.slice(1));
12861
13188
  return 1;
12862
13189
  }
12863
13190
  const e = err;
@@ -12865,7 +13192,7 @@ function report(ui2, err, cmd, env) {
12865
13192
  ui2.err("cancelled.");
12866
13193
  return 1;
12867
13194
  }
12868
- line2(e?.message ?? String(err));
13195
+ line3(e?.message ?? String(err));
12869
13196
  if (env.AIRGATE_DEBUG && e?.stack) ui2.err(e.stack);
12870
13197
  return 1;
12871
13198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airgate",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Give your AI its own computer: the airgate command line.",
5
5
  "type": "module",
6
6
  "bin": {