@easybits.cloud/cli 0.6.1 → 0.8.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 (3) hide show
  1. package/README.md +30 -9
  2. package/dist/index.js +308 -115
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -36,7 +36,7 @@ another server (default `https://www.easybits.cloud`; `baseUrl` in `~/.easybitsr
36
36
  # Sandboxes (alias: sb)
37
37
  easybits sandboxes ls
38
38
  easybits sandboxes create --template node --name scratch --dotenv .env
39
- easybits sandboxes create --size m --timeout 1800 --no-wait # size s|m|l|xl (plan-gated)
39
+ easybits sandboxes create --size m --ttl 1800 --no-wait # size s|m|l|xl (plan-gated)
40
40
  easybits sandboxes get sb_abc123
41
41
  easybits sandboxes exec sb_abc123 -- npm test
42
42
  easybits sandboxes logs sb_abc123 --unit myapp --lines 100 --since "10 min ago" --grep ERROR
@@ -50,7 +50,7 @@ easybits sandboxes destroy sb_abc123 --yes
50
50
 
51
51
  # Hosting: permanent machines (alias: deploy)
52
52
  easybits machines ls
53
- easybits machines deploy sb_abc123 -m "v1.2"
53
+ easybits deploy sb_abc123 -m "v1.2" # = machines deploy
54
54
  easybits machines releases sb_abc123 --limit 5
55
55
  easybits machines logs sb_abc123 --lines 100 --grep ERROR
56
56
  easybits machines rollback sb_abc123 rel_789
@@ -75,31 +75,37 @@ easybits db rm leads --yes
75
75
  # Agents
76
76
  easybits agents ls
77
77
  easybits agents create --template goose --name helper --dotenv .env
78
- easybits agents message ag_123 "hello"
79
- easybits agents message ag_123 "and now?" --session ses_456 # continue a conversation
80
- easybits agents destroy ag_123 --yes
78
+ easybits agents get helper # by name or id
79
+ easybits agents message helper "hello"
80
+ easybits agents message helper "and now?" --session ses_456 # continue a conversation
81
+ easybits agents destroy helper --yes
81
82
 
82
83
  # Files, websites, account
83
84
  easybits files ls
84
85
  easybits files upload ./report.pdf
85
- easybits files delete FILE_ID --yes
86
+ easybits files rm FILE_ID --yes
86
87
  easybits websites ls
87
88
  easybits providers # storage provider (Tigris by default)
88
89
  easybits usage
89
90
 
90
91
  # MCP, SSH, docs
91
- easybits config # MCP config (streamable HTTP)
92
- easybits mcp # MCP config (stdio)
92
+ easybits whoami # account and where the credential comes from
93
+ easybits mcp config # MCP config (streamable HTTP)
94
+ easybits mcp config --stdio # MCP config (stdio)
93
95
  easybits ssh-key # public key for sandbox SSH
94
96
  easybits docs cli # a docs section as markdown (--en for English)
95
97
  easybits docs --open # open the docs in your browser
96
98
  ```
97
99
 
100
+ **Names or ids.** Wherever a command takes an agent, a sandbox/machine or a database, you can
101
+ pass its name instead of the id (`easybits sb exec scratch -- ls`, `easybits domains ls shop`).
102
+ The match is exact and case-insensitive; if two share the name the CLI exits 2 and lists the ids.
103
+
98
104
  Every command has help with examples: `easybits <command> <subcommand> --help`
99
105
  (or `easybits help <command> <subcommand>`). A typo gets a "Did you mean" suggestion.
100
106
 
101
107
  **Destructive commands** (`db rm`, `agents destroy`, `sandboxes destroy`, `domains rm`,
102
- `files delete`) ask before acting in a terminal: `[y/N]`, or typing the name/id for
108
+ `files rm`) ask before acting in a terminal: `[y/N]`, or typing the name/id for
103
109
  irreversible ones (a database, an agent, a permanent sandbox). Without a terminal they
104
110
  never prompt — and neither do they with `--json`: pass `--yes` / `-y`, or they exit `2`.
105
111
 
@@ -129,6 +135,21 @@ Host *.ghosty
129
135
 
130
136
  Full reference: https://www.easybits.cloud/docs#cli · https://www.easybits.cloud/en/docs/cli.md
131
137
 
138
+ ## Changes in 0.8.0
139
+
140
+ - One naming rule (like `gh`): `easybits <noun> <verb>`. Old spellings still work and print the new
141
+ one on stderr (never with `--json`):
142
+ `easybits config` → `easybits mcp config`; `easybits mcp` → `easybits mcp config --stdio`;
143
+ `easybits deploy <machine>` is now the verb (= `machines deploy`), so `deploy ls` → `machines ls`;
144
+ `machines release` → `machines deploy` (it clashed with `releases`);
145
+ `sandboxes create --timeout` → `--ttl` (`--timeout` stays in `exec`, where it caps the command).
146
+ - `files rm` is the name (`delete` still works), like `db rm` and `domains rm`.
147
+ - New `easybits whoami`: account email and where the credential comes from.
148
+
149
+ ## Changes in 0.7.0
150
+
151
+ - Agents, sandboxes/machines and databases by **name** anywhere an id goes; `easybits agents get`.
152
+
132
153
  ## Changes in 0.6.1
133
154
 
134
155
  - Help: `--yes` is no longer listed as a global flag; it lives on each destructive command
package/dist/index.js CHANGED
@@ -495,7 +495,8 @@ var providers = {
495
495
  synopsis: "providers",
496
496
  leaf: {
497
497
  summary: "Show storage providers",
498
- usage: "easybits providers [list]",
498
+ usage: "easybits providers",
499
+ examples: ["easybits providers", "easybits providers --json"],
499
500
  async run(ctx) {
500
501
  const data = {
501
502
  defaultProvider: "tigris",
@@ -508,60 +509,57 @@ var providers = {
508
509
  }
509
510
  }
510
511
  };
511
- var config = {
512
- name: "config",
512
+ var mcp = {
513
+ name: "mcp",
513
514
  group: "MCP",
514
- summary: "Print MCP config JSON (streamable HTTP)",
515
- synopsis: "config",
516
- leaf: {
517
- summary: "Print MCP config JSON (streamable HTTP) with your key",
518
- usage: "easybits config",
519
- examples: ["easybits config > .mcp.json"],
520
- async run(ctx) {
521
- const apiKey = resolveApiKey(ctx);
522
- const baseUrl = await resolveBaseUrl3();
523
- console.log(
524
- JSON.stringify(
525
- {
515
+ summary: "Print the MCP config JSON for your agent or editor",
516
+ synopsis: "mcp",
517
+ subs: {
518
+ config: {
519
+ summary: "Print MCP config JSON: streamable HTTP with your key, or stdio (npx @easybits.cloud/mcp)",
520
+ usage: "easybits mcp config [--stdio]",
521
+ options: { stdio: { type: "boolean", description: "stdio config (npx @easybits.cloud/mcp) instead of streamable HTTP" } },
522
+ examples: ["easybits mcp config > .mcp.json", "easybits mcp config --stdio"],
523
+ async run(ctx) {
524
+ if (bool(ctx, "stdio")) {
525
+ const config2 = {
526
526
  mcpServers: {
527
- easybits: {
528
- type: "streamable-http",
529
- url: `${baseUrl}/api/mcp`,
530
- headers: { Authorization: `Bearer ${apiKey || "eb_sk_live_YOUR_KEY"}` }
531
- }
527
+ easybits: { command: "npx", args: ["-y", "@easybits.cloud/mcp"], env: { EASYBITS_API_KEY: "eb_sk_live_YOUR_KEY" } }
532
528
  }
533
- },
534
- null,
535
- 2
536
- )
537
- );
529
+ };
530
+ console.log(JSON.stringify(config2, null, 2));
531
+ return;
532
+ }
533
+ const apiKey = resolveApiKey(ctx);
534
+ const baseUrl = await resolveBaseUrl3();
535
+ const config = {
536
+ mcpServers: {
537
+ easybits: {
538
+ type: "streamable-http",
539
+ url: `${baseUrl}/api/mcp`,
540
+ headers: { Authorization: `Bearer ${apiKey || "eb_sk_live_YOUR_KEY"}` }
541
+ }
542
+ }
543
+ };
544
+ console.log(JSON.stringify(config, null, 2));
545
+ }
538
546
  }
539
547
  }
540
548
  };
541
- var mcp = {
542
- name: "mcp",
543
- group: "MCP",
544
- summary: "Print MCP stdio config JSON",
545
- synopsis: "mcp",
549
+ var whoami = {
550
+ name: "whoami",
551
+ group: "Account",
552
+ summary: "Show which account and credential the CLI is using",
553
+ synopsis: "whoami",
546
554
  leaf: {
547
- summary: "Print MCP stdio config JSON (npx @easybits.cloud/mcp)",
548
- usage: "easybits mcp",
549
- async run() {
550
- console.log(
551
- JSON.stringify(
552
- {
553
- mcpServers: {
554
- easybits: {
555
- command: "npx",
556
- args: ["-y", "@easybits.cloud/mcp"],
557
- env: { EASYBITS_API_KEY: "eb_sk_live_YOUR_KEY" }
558
- }
559
- }
560
- },
561
- null,
562
- 2
563
- )
564
- );
555
+ summary: "Show the account email and where the credential comes from",
556
+ usage: "easybits whoami",
557
+ examples: ["easybits whoami", "easybits whoami --json"],
558
+ async run(ctx) {
559
+ const source = process.env.EASYBITS_API_KEY ? "env EASYBITS_API_KEY" : ctx.token ? "--token" : readRc().oauth?.accessToken ? "browser session (~/.easybitsrc)" : readRc().apiKey ? "API key (~/.easybitsrc)" : void 0;
560
+ if (!source) throw notLoggedIn();
561
+ const email = await fetchEmail(await resolveCredential(ctx));
562
+ emit(ctx, { email, source, baseUrl: await resolveBaseUrl3() }, () => console.log(`${email ?? "(unknown email)"} via ${source}`));
565
563
  }
566
564
  }
567
565
  };
@@ -674,12 +672,12 @@ var files = {
674
672
  emit(ctx, data.file, () => console.log(`Uploaded: ${data.file.id}`));
675
673
  }
676
674
  },
677
- delete: {
678
- aliases: ["rm"],
675
+ rm: {
676
+ aliases: ["delete", "remove"],
679
677
  summary: "Delete a file (soft delete, 7-day trash)",
680
- usage: "easybits files delete <file-id> [--yes]",
678
+ usage: "easybits files rm <file-id> [--yes]",
681
679
  options: { ...YES_OPTION },
682
- examples: ["easybits files delete 6650f0c2a1b2c3d4e5f60718", "easybits files rm 6650f0c2a1b2c3d4e5f60718 --yes"],
680
+ examples: ["easybits files rm 6650f0c2a1b2c3d4e5f60718", "easybits files rm 6650f0c2a1b2c3d4e5f60718 --yes"],
683
681
  async run(ctx) {
684
682
  const id = need(ctx, 0, "file-id", this.usage);
685
683
  requireYesIfHeadless(ctx);
@@ -992,11 +990,11 @@ function printSandbox(s) {
992
990
  if (s.tier) console.log(`Tier: ${s.tier}${s.monthlyMxn != null ? ` ($${s.monthlyMxn} MXN/month)` : ""}`);
993
991
  }
994
992
  async function sandbox(ctx, usage2) {
995
- const id = need(ctx, 0, "sandbox-id", usage2);
993
+ const id = need(ctx, 0, "sandbox", usage2);
996
994
  const eb = await getClient(ctx);
997
995
  return eb.sandboxes.get(id);
998
996
  }
999
- var FILES_USAGE = "easybits sandboxes files <ls|read|write> <sandbox-id> <path> [local-file]";
997
+ var FILES_USAGE = "easybits sandboxes files <ls|read|write> <sandbox> <path> [local-file]";
1000
998
  var sandboxes = {
1001
999
  name: "sandboxes",
1002
1000
  aliases: ["sandbox", "sb"],
@@ -1026,11 +1024,11 @@ var sandboxes = {
1026
1024
  create: {
1027
1025
  aliases: ["new"],
1028
1026
  summary: "Create a sandbox and wait until it is running",
1029
- usage: "easybits sandboxes create [--template ubuntu] [--name <name>] [--timeout <s>] [--size s|m|l|xl] [--dotenv <path>] [--env K=V]...",
1027
+ usage: "easybits sandboxes create [--template ubuntu] [--name <name>] [--ttl <s>] [--size s|m|l|xl] [--dotenv <path>] [--env K=V]...",
1030
1028
  options: {
1031
1029
  template: { type: "string", value: "name", description: "Base template (default ubuntu): ubuntu, python, node, code-interpreter\u2026" },
1032
1030
  name: { type: "string", value: "name", description: "Human label (lets you `ssh <name>.ghosty`)" },
1033
- timeout: { type: "string", value: "seconds", description: "Time to live before auto-destroy" },
1031
+ ttl: { type: "string", value: "seconds", description: "Time to live before auto-destroy (was --timeout)" },
1034
1032
  size: { type: "string", value: "s|m|l|xl", description: "VM size class (gated by plan)" },
1035
1033
  env: { type: "string", multiple: true, value: "K=V", description: "Environment variable, non-secret (repeatable)" },
1036
1034
  ...ENV_FILE_OPTION,
@@ -1049,7 +1047,7 @@ var sandboxes = {
1049
1047
  const sb = await eb.sandboxes.create({
1050
1048
  template: str(ctx, "template") ?? "ubuntu",
1051
1049
  name: str(ctx, "name"),
1052
- timeoutSeconds: int(ctx, "timeout", this.usage),
1050
+ timeoutSeconds: int(ctx, "ttl", this.usage),
1053
1051
  size,
1054
1052
  env: Object.keys(env).length ? env : void 0,
1055
1053
  waitForReady: !bool(ctx, "no-wait")
@@ -1061,7 +1059,7 @@ var sandboxes = {
1061
1059
  get: {
1062
1060
  aliases: ["show", "status"],
1063
1061
  summary: "Show one sandbox",
1064
- usage: "easybits sandboxes get <sandbox-id>",
1062
+ usage: "easybits sandboxes get <sandbox>",
1065
1063
  examples: ["easybits sandboxes get sb_abc123"],
1066
1064
  async run(ctx) {
1067
1065
  const rec = sandboxRecord(await sandbox(ctx, this.usage));
@@ -1070,7 +1068,7 @@ var sandboxes = {
1070
1068
  },
1071
1069
  exec: {
1072
1070
  summary: "Run a shell command inside a sandbox",
1073
- usage: "easybits sandboxes exec <sandbox-id> [--cwd <dir>] [--timeout <s>] [--dotenv <path>] -- <command...>",
1071
+ usage: "easybits sandboxes exec <sandbox> [--cwd <dir>] [--timeout <s>] [--dotenv <path>] -- <command...>",
1074
1072
  options: {
1075
1073
  cwd: { type: "string", value: "dir", description: "Working directory" },
1076
1074
  timeout: { type: "string", value: "seconds", description: "Kill the command after this long" },
@@ -1084,7 +1082,7 @@ var sandboxes = {
1084
1082
  "easybits sb exec sb_abc123 --json -- 'ls -la /' | jq .exitCode"
1085
1083
  ],
1086
1084
  async run(ctx) {
1087
- need(ctx, 0, "sandbox-id", this.usage);
1085
+ need(ctx, 0, "sandbox", this.usage);
1088
1086
  const command = ctx.args.slice(1).join(" ");
1089
1087
  if (!command) throw usageError("Missing <command>.", this.usage);
1090
1088
  const env = await envFrom(ctx, this.usage);
@@ -1107,7 +1105,7 @@ var sandboxes = {
1107
1105
  },
1108
1106
  logs: {
1109
1107
  summary: "Read the sandbox journal (optionally one systemd unit)",
1110
- usage: "easybits sandboxes logs <sandbox-id> [--unit <unit>] [--lines <n>] [--since <spec>] [--grep <text>]",
1108
+ usage: "easybits sandboxes logs <sandbox> [--unit <unit>] [--lines <n>] [--since <spec>] [--grep <text>]",
1111
1109
  options: {
1112
1110
  unit: { type: "string", value: "unit", description: "systemd unit to filter" },
1113
1111
  lines: { type: "string", value: "n", description: "Last N lines (default 200)" },
@@ -1142,7 +1140,7 @@ var sandboxes = {
1142
1140
  ],
1143
1141
  async run(ctx) {
1144
1142
  const action = need(ctx, 0, "ls|read|write", FILES_USAGE);
1145
- const id = need(ctx, 1, "sandbox-id", FILES_USAGE);
1143
+ const id = need(ctx, 1, "sandbox", FILES_USAGE);
1146
1144
  const path = need(ctx, 2, "path", FILES_USAGE);
1147
1145
  const eb = await getClient(ctx);
1148
1146
  const sb = await eb.sandboxes.get(id);
@@ -1186,7 +1184,7 @@ var sandboxes = {
1186
1184
  },
1187
1185
  suspend: {
1188
1186
  summary: "Suspend to disk (frees CPU; TTL paused)",
1189
- usage: "easybits sandboxes suspend <sandbox-id>",
1187
+ usage: "easybits sandboxes suspend <sandbox>",
1190
1188
  examples: ["easybits sandboxes suspend sb_abc123"],
1191
1189
  async run(ctx) {
1192
1190
  const r = await (await sandbox(ctx, this.usage)).suspend();
@@ -1195,7 +1193,7 @@ var sandboxes = {
1195
1193
  },
1196
1194
  resume: {
1197
1195
  summary: "Wake a suspended sandbox",
1198
- usage: "easybits sandboxes resume <sandbox-id> [--dotenv <path>] [--env K=V]...",
1196
+ usage: "easybits sandboxes resume <sandbox> [--dotenv <path>] [--env K=V]...",
1199
1197
  options: {
1200
1198
  env: { type: "string", multiple: true, value: "K=V", description: "Rewrite env on wake, non-secret (repeatable)" },
1201
1199
  ...ENV_FILE_OPTION
@@ -1210,11 +1208,11 @@ var sandboxes = {
1210
1208
  destroy: {
1211
1209
  aliases: ["rm", "delete"],
1212
1210
  summary: "Destroy a sandbox (irreversible)",
1213
- usage: "easybits sandboxes destroy <sandbox-id> [--yes]",
1211
+ usage: "easybits sandboxes destroy <sandbox> [--yes]",
1214
1212
  options: { ...YES_OPTION },
1215
1213
  examples: ["easybits sandboxes destroy sb_abc123", "easybits sb rm sb_abc123 --yes"],
1216
1214
  async run(ctx) {
1217
- need(ctx, 0, "sandbox-id", this.usage);
1215
+ need(ctx, 0, "sandbox", this.usage);
1218
1216
  requireYesIfHeadless(ctx);
1219
1217
  const sb = await sandbox(ctx, this.usage);
1220
1218
  await confirm(
@@ -1228,7 +1226,7 @@ var sandboxes = {
1228
1226
  },
1229
1227
  snapshot: {
1230
1228
  summary: "Capture a copy-on-write snapshot without stopping the box",
1231
- usage: "easybits sandboxes snapshot <sandbox-id> [--name <name>]",
1229
+ usage: "easybits sandboxes snapshot <sandbox> [--name <name>]",
1232
1230
  options: { name: { type: "string", value: "name", description: "Snapshot label" } },
1233
1231
  examples: ["easybits sandboxes snapshot sb_abc123 --name before-upgrade"],
1234
1232
  async run(ctx) {
@@ -1240,19 +1238,19 @@ var sandboxes = {
1240
1238
  };
1241
1239
 
1242
1240
  // src/commands/machines.ts
1243
- var SECRETS_USAGE = "easybits machines secrets <ls|set|unset> <machine-id> [--dotenv <path> | NAME]";
1241
+ var SECRETS_USAGE = "easybits machines secrets <ls|set|unset> <machine> [--dotenv <path> | NAME]";
1244
1242
  var machines = {
1245
1243
  name: "machines",
1246
- aliases: ["deploy", "machine"],
1244
+ aliases: ["machine"],
1247
1245
  group: "Hosting",
1248
- summary: "Permanent machines and their releases (alias: deploy)",
1246
+ summary: "Permanent machines and their releases (easybits deploy <machine> publishes)",
1249
1247
  synopsis: "machines",
1250
1248
  subs: {
1251
1249
  ls: {
1252
1250
  aliases: ["list"],
1253
1251
  summary: "List your permanent machines",
1254
1252
  usage: "easybits machines ls",
1255
- examples: ["easybits machines ls", "easybits deploy ls --json"],
1253
+ examples: ["easybits machines ls", "easybits machines ls --json"],
1256
1254
  async run(ctx) {
1257
1255
  const eb = await getClient(ctx);
1258
1256
  const rows = (await eb.machines.list()).map(sandboxRecord);
@@ -1268,13 +1266,12 @@ var machines = {
1268
1266
  }
1269
1267
  },
1270
1268
  deploy: {
1271
- aliases: ["release"],
1272
1269
  summary: "Publish the machine's current app code as a new release",
1273
- usage: "easybits machines deploy <machine-id> [--message <text>]",
1270
+ usage: "easybits machines deploy <machine> [--message <text>]",
1274
1271
  options: { message: { type: "string", short: "m", value: "text", description: "Release note" } },
1275
- examples: ['easybits machines deploy sb_abc123 -m "v1.2: fix checkout"'],
1272
+ examples: ['easybits machines deploy shop -m "v1.2: fix checkout"', 'easybits deploy shop -m "v1.2" # same thing, shorter'],
1276
1273
  async run(ctx) {
1277
- const id = need(ctx, 0, "machine-id", this.usage);
1274
+ const id = need(ctx, 0, "machine", this.usage);
1278
1275
  const eb = await getClient(ctx);
1279
1276
  const r = await eb.machines.deploy(id, { message: str(ctx, "message") });
1280
1277
  emit(ctx, r, () => console.log(`Release v${r.version} ${r.releaseId} (${r.status}, ${fmtBytes(r.sizeBytes)})`));
@@ -1282,11 +1279,11 @@ var machines = {
1282
1279
  },
1283
1280
  releases: {
1284
1281
  summary: "List releases, newest first",
1285
- usage: "easybits machines releases <machine-id> [--limit <n>]",
1282
+ usage: "easybits machines releases <machine> [--limit <n>]",
1286
1283
  options: { limit: { type: "string", value: "n", description: "How many" } },
1287
1284
  examples: ["easybits machines releases sb_abc123 --limit 5"],
1288
1285
  async run(ctx) {
1289
- const id = need(ctx, 0, "machine-id", this.usage);
1286
+ const id = need(ctx, 0, "machine", this.usage);
1290
1287
  const eb = await getClient(ctx);
1291
1288
  const r = await eb.machines.releases(id, { limit: int(ctx, "limit", this.usage) });
1292
1289
  emit(
@@ -1295,21 +1292,21 @@ var machines = {
1295
1292
  () => table(
1296
1293
  r.items.map((x) => ({ ...x, size: fmtBytes(x.sizeBytes), createdAt: fmtDate(x.createdAt) })),
1297
1294
  [["version", "VERSION"], ["releaseId", "ID"], ["status", "STATUS"], ["size", "SIZE"], ["createdAt", "CREATED"], ["message", "MESSAGE"]],
1298
- "No releases yet. Publish one: easybits machines deploy <machine-id>"
1295
+ "No releases yet. Publish one: easybits machines deploy <machine>"
1299
1296
  )
1300
1297
  );
1301
1298
  }
1302
1299
  },
1303
1300
  logs: {
1304
1301
  summary: "Tail the app's own log",
1305
- usage: "easybits machines logs <machine-id> [--lines <n>] [--grep <text>]",
1302
+ usage: "easybits machines logs <machine> [--lines <n>] [--grep <text>]",
1306
1303
  options: {
1307
1304
  lines: { type: "string", value: "n", description: "Last N lines" },
1308
1305
  grep: { type: "string", value: "text", description: "Only lines matching" }
1309
1306
  },
1310
1307
  examples: ["easybits machines logs sb_abc123 --lines 100", "easybits machines logs sb_abc123 --grep ERROR"],
1311
1308
  async run(ctx) {
1312
- const id = need(ctx, 0, "machine-id", this.usage);
1309
+ const id = need(ctx, 0, "machine", this.usage);
1313
1310
  const eb = await getClient(ctx);
1314
1311
  const r = await eb.machines.logs(id, { lines: int(ctx, "lines", this.usage), grep: str(ctx, "grep") });
1315
1312
  emit(ctx, r, () => process.stdout.write(r.output.endsWith("\n") || !r.output ? r.output : r.output + "\n"));
@@ -1317,10 +1314,10 @@ var machines = {
1317
1314
  },
1318
1315
  rollback: {
1319
1316
  summary: "Put a previous release back on the same machine (data untouched)",
1320
- usage: "easybits machines rollback <machine-id> <release-id>",
1317
+ usage: "easybits machines rollback <machine> <release-id>",
1321
1318
  examples: ["easybits machines rollback sb_abc123 rel_789"],
1322
1319
  async run(ctx) {
1323
- const id = need(ctx, 0, "machine-id", this.usage);
1320
+ const id = need(ctx, 0, "machine", this.usage);
1324
1321
  const rel = need(ctx, 1, "release-id", this.usage);
1325
1322
  const eb = await getClient(ctx);
1326
1323
  const r = await eb.machines.rollback(id, rel);
@@ -1340,7 +1337,7 @@ var machines = {
1340
1337
  ],
1341
1338
  async run(ctx) {
1342
1339
  const action = need(ctx, 0, "ls|set|unset", SECRETS_USAGE);
1343
- const id = need(ctx, 1, "machine-id", SECRETS_USAGE);
1340
+ const id = need(ctx, 1, "machine", SECRETS_USAGE);
1344
1341
  const eb = await getClient(ctx);
1345
1342
  if (action === "ls" || action === "list") {
1346
1343
  const r = await eb.machines.secrets(id);
@@ -1368,7 +1365,7 @@ var machines = {
1368
1365
 
1369
1366
  // src/commands/domains.ts
1370
1367
  async function sandbox2(ctx, usage2) {
1371
- const id = need(ctx, 0, "sandbox-id", usage2);
1368
+ const id = need(ctx, 0, "sandbox", usage2);
1372
1369
  return (await getClient(ctx)).sandboxes.get(id);
1373
1370
  }
1374
1371
  var domains = {
@@ -1381,7 +1378,7 @@ var domains = {
1381
1378
  ls: {
1382
1379
  aliases: ["list"],
1383
1380
  summary: "List domains attached to a sandbox",
1384
- usage: "easybits domains ls <sandbox-id>",
1381
+ usage: "easybits domains ls <sandbox>",
1385
1382
  examples: ["easybits domains ls sb_abc123"],
1386
1383
  async run(ctx) {
1387
1384
  const items = await (await sandbox2(ctx, this.usage)).listDomains();
@@ -1390,7 +1387,7 @@ var domains = {
1390
1387
  },
1391
1388
  add: {
1392
1389
  summary: "Attach a domain to a port; prints the DNS record to create",
1393
- usage: "easybits domains add <sandbox-id> <domain> --port <port>",
1390
+ usage: "easybits domains add <sandbox> <domain> --port <port>",
1394
1391
  options: { port: { type: "string", value: "port", description: "Port inside the sandbox (required)" } },
1395
1392
  examples: ["easybits domains add sb_abc123 shop.example.com --port 3000"],
1396
1393
  async run(ctx) {
@@ -1407,7 +1404,7 @@ var domains = {
1407
1404
  },
1408
1405
  verify: {
1409
1406
  summary: "Check DNS + HTTPS for a domain",
1410
- usage: "easybits domains verify <sandbox-id> <domain>",
1407
+ usage: "easybits domains verify <sandbox> <domain>",
1411
1408
  examples: ["easybits domains verify sb_abc123 shop.example.com"],
1412
1409
  async run(ctx) {
1413
1410
  const domain = need(ctx, 1, "domain", this.usage);
@@ -1423,11 +1420,11 @@ var domains = {
1423
1420
  rm: {
1424
1421
  aliases: ["remove", "delete"],
1425
1422
  summary: "Detach a domain",
1426
- usage: "easybits domains rm <sandbox-id> <domain> [--yes]",
1423
+ usage: "easybits domains rm <sandbox> <domain> [--yes]",
1427
1424
  options: { ...YES_OPTION },
1428
1425
  examples: ["easybits domains rm sb_abc123 shop.example.com", "easybits domains rm sb_abc123 shop.example.com --yes"],
1429
1426
  async run(ctx) {
1430
- need(ctx, 0, "sandbox-id", this.usage);
1427
+ need(ctx, 0, "sandbox", this.usage);
1431
1428
  const domain = need(ctx, 1, "domain", this.usage);
1432
1429
  requireYesIfHeadless(ctx);
1433
1430
  await confirm(ctx, `Detach ${domain} from ${ctx.args[0]}? It stops serving right away.`);
@@ -1438,16 +1435,104 @@ var domains = {
1438
1435
  }
1439
1436
  };
1440
1437
 
1438
+ // src/refs.ts
1439
+ var ID_FORMAT = {
1440
+ agent: /^[0-9a-f]{24}$/i,
1441
+ db: /^[0-9a-f]{24}$/i,
1442
+ sandbox: /^sb_[0-9a-z-]+$/i
1443
+ };
1444
+ var looksLikeId = (kind, ref) => ID_FORMAT[kind].test(ref);
1445
+ var REF_ARGS = {
1446
+ "agents get": { index: 0, kind: "agent" },
1447
+ "agents message": { index: 0, kind: "agent" },
1448
+ "agents destroy": { index: 0, kind: "agent" },
1449
+ "sandboxes get": { index: 0, kind: "sandbox" },
1450
+ "sandboxes exec": { index: 0, kind: "sandbox" },
1451
+ "sandboxes logs": { index: 0, kind: "sandbox" },
1452
+ "sandboxes files": { index: 1, kind: "sandbox" },
1453
+ "sandboxes suspend": { index: 0, kind: "sandbox" },
1454
+ "sandboxes resume": { index: 0, kind: "sandbox" },
1455
+ "sandboxes destroy": { index: 0, kind: "sandbox" },
1456
+ "sandboxes snapshot": { index: 0, kind: "sandbox" },
1457
+ "machines deploy": { index: 0, kind: "sandbox" },
1458
+ "machines releases": { index: 0, kind: "sandbox" },
1459
+ "machines logs": { index: 0, kind: "sandbox" },
1460
+ "machines rollback": { index: 0, kind: "sandbox" },
1461
+ "machines secrets": { index: 1, kind: "sandbox" },
1462
+ "domains ls": { index: 0, kind: "sandbox" },
1463
+ "domains add": { index: 0, kind: "sandbox" },
1464
+ "domains verify": { index: 0, kind: "sandbox" },
1465
+ "domains rm": { index: 0, kind: "sandbox" },
1466
+ "db rm": { index: 0, kind: "db" },
1467
+ "db tables": { index: 0, kind: "db" },
1468
+ "db query": { index: 0, kind: "db" }
1469
+ };
1470
+ function matchRef(kind, ref, items) {
1471
+ if (looksLikeId(kind, ref)) return { ok: true, id: ref };
1472
+ const list2 = items ?? [];
1473
+ if (list2.some((x) => x.id === ref)) return { ok: true, id: ref };
1474
+ const want = ref.trim().toLowerCase();
1475
+ const matches = list2.filter((x) => (x.name ?? "").trim().toLowerCase() === want);
1476
+ if (matches.length === 1) return { ok: true, id: matches[0].id };
1477
+ if (matches.length === 0) return { ok: false, reason: "not_found" };
1478
+ return { ok: false, reason: "ambiguous", matches };
1479
+ }
1480
+
1481
+ // src/resolve.ts
1482
+ var LS_HINT = {
1483
+ agent: "easybits agents ls",
1484
+ sandbox: "easybits sandboxes ls",
1485
+ db: "easybits db ls"
1486
+ };
1487
+ var NOUN = { agent: "agent", sandbox: "sandbox or machine", db: "database" };
1488
+ var cache = /* @__PURE__ */ new Map();
1489
+ function listRefs(ctx, kind) {
1490
+ let p = cache.get(kind);
1491
+ if (!p) {
1492
+ p = (async () => {
1493
+ const eb = await getClient(ctx);
1494
+ if (kind === "agent") return (await eb.listAgents()).map((a) => ({ id: a.agentId, name: a.name, status: a.status }));
1495
+ if (kind === "db") return (await eb.listDatabases()).items.map((d) => ({ id: d.id, name: d.name }));
1496
+ return (await eb.sandboxes.list()).map((s) => ({ id: s.sandboxId, name: s.name, status: s.status }));
1497
+ })();
1498
+ p.catch(() => cache.delete(kind));
1499
+ cache.set(kind, p);
1500
+ }
1501
+ return p;
1502
+ }
1503
+ async function resolveRef(ctx, kind, ref) {
1504
+ if (looksLikeId(kind, ref)) return ref;
1505
+ const r = matchRef(kind, ref, await listRefs(ctx, kind));
1506
+ if (r.ok) return r.id;
1507
+ if (r.reason === "not_found") {
1508
+ throw new CliError(`No ${NOUN[kind]} with id or name "${ref}".`, EXIT.API, `List them with: ${LS_HINT[kind]}`, "not_found", 404);
1509
+ }
1510
+ const shown = r.matches.slice(0, 10).map((m) => ` ${m.id}${m.status ? ` (${m.status})` : ""}`);
1511
+ if (r.matches.length > 10) shown.push(` \u2026and ${r.matches.length - 10} more`);
1512
+ throw new CliError(
1513
+ `"${ref}" matches ${r.matches.length} ${kind === "db" ? "databases" : `${kind}s`}. Use the id:`,
1514
+ EXIT.USAGE,
1515
+ `${shown.join("\n")}
1516
+ List them with: ${LS_HINT[kind]}`,
1517
+ "ambiguous"
1518
+ );
1519
+ }
1520
+ async function resolveRefs(ctx, key) {
1521
+ const spec = REF_ARGS[key];
1522
+ const ref = spec ? ctx.args[spec.index] : void 0;
1523
+ if (!spec || !ref) return;
1524
+ ctx.args[spec.index] = await resolveRef(ctx, spec.kind, ref);
1525
+ }
1526
+
1441
1527
  // src/commands/db.ts
1442
- async function findDb(eb, ref) {
1443
- const { items } = await eb.listDatabases();
1444
- const hit = items.find((d) => d.id === ref) ?? items.find((d) => d.name === ref);
1445
- if (!hit) throw new CliError(`No database "${ref}".`, 1, "List them with: easybits db ls", "not_found", 404);
1446
- return hit;
1528
+ async function findDb(ctx, id) {
1529
+ const hit = (await listRefs(ctx, "db")).find((d) => d.id === id);
1530
+ if (!hit) throw new CliError(`No database "${id}".`, 1, "List them with: easybits db ls", "not_found", 404);
1531
+ return { id, name: hit.name ?? id };
1447
1532
  }
1448
1533
  var db = {
1449
1534
  name: "db",
1450
- aliases: ["databases"],
1535
+ aliases: ["databases", "database"],
1451
1536
  group: "Data",
1452
1537
  summary: "SQL databases (libSQL)",
1453
1538
  synopsis: "db",
@@ -1493,7 +1578,7 @@ var db = {
1493
1578
  const ref = need(ctx, 0, "db-id|name", this.usage);
1494
1579
  requireYesIfHeadless(ctx);
1495
1580
  const eb = await getClient(ctx);
1496
- const hit = await findDb(eb, ref);
1581
+ const hit = await findDb(ctx, ref);
1497
1582
  await confirm(ctx, `Delete database ${hit.name} (${hit.id}) and all its data?`, { typeName: hit.name });
1498
1583
  const r = await eb.deleteDatabase(hit.id);
1499
1584
  emit(ctx, { ...r, id: hit.id, name: hit.name }, () => console.log(`Deleted ${hit.name} (${hit.id})`));
@@ -1508,7 +1593,7 @@ var db = {
1508
1593
  async run(ctx) {
1509
1594
  const ref = need(ctx, 0, "db-id|name", this.usage);
1510
1595
  const eb = await getClient(ctx);
1511
- const hit = await findDb(eb, ref);
1596
+ const hit = await findDb(ctx, ref);
1512
1597
  const q = (sql, args) => eb.queryDatabase(hit.id, sql, args);
1513
1598
  const names = (await q("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_litestream_%' ORDER BY name")).rows.map((r) => String(r[0]));
1514
1599
  const ident = (n) => `"${n.replace(/"/g, '""')}"`;
@@ -1549,7 +1634,7 @@ var db = {
1549
1634
  const ref = need(ctx, 0, "db-id|name", this.usage);
1550
1635
  const sql = need(ctx, 1, "sql", this.usage);
1551
1636
  const eb = await getClient(ctx);
1552
- const hit = await findDb(eb, ref);
1637
+ const hit = await findDb(ctx, ref);
1553
1638
  const args = list(ctx, "arg");
1554
1639
  const r = await eb.queryDatabase(hit.id, sql, args.length ? args : void 0);
1555
1640
  emit(ctx, r, () => {
@@ -1595,6 +1680,27 @@ var agents = {
1595
1680
  );
1596
1681
  }
1597
1682
  },
1683
+ get: {
1684
+ aliases: ["show"],
1685
+ summary: "Show one agent (by id or name)",
1686
+ usage: "easybits agents get <agent>",
1687
+ examples: ["easybits agents get helper", "easybits agents get 6650f0c2a1b2c3d4e5f60718 --json"],
1688
+ async run(ctx) {
1689
+ const id = need(ctx, 0, "agent", this.usage);
1690
+ const eb = await getClient(ctx);
1691
+ const a = await eb.getAgent(id);
1692
+ emit(ctx, a, () => {
1693
+ console.log(`ID: ${a.agentId}`);
1694
+ if (a.name) console.log(`Name: ${a.name}`);
1695
+ console.log(`Template: ${a.template}`);
1696
+ console.log(`Status: ${a.status}`);
1697
+ console.log(`Sandbox: ${a.sandboxId}`);
1698
+ console.log(`URL: ${a.agentUrl}`);
1699
+ console.log(`Created: ${fmtDate(a.createdAt)}`);
1700
+ console.log(`Expires: ${a.expiresAt ? fmtDate(a.expiresAt) : "never"}`);
1701
+ });
1702
+ }
1703
+ },
1598
1704
  create: {
1599
1705
  summary: "Create an agent from a template",
1600
1706
  usage: "easybits agents create --template <template> [--name <name>] [--dotenv <path>] [--env K=V]... [--timeout <s>]",
@@ -1631,12 +1737,12 @@ var agents = {
1631
1737
  },
1632
1738
  message: {
1633
1739
  aliases: ["msg", "send"],
1634
- summary: "Send a message; streams the reply",
1635
- usage: "easybits agents message <agent-id> <text...> [--session <id>]",
1740
+ summary: "Send a message (agent by id or name); streams the reply",
1741
+ usage: "easybits agents message <agent> <text...> [--session <id>]",
1636
1742
  options: { session: { type: "string", value: "id", description: "Continue a conversation" } },
1637
- examples: ['easybits agents message ag_123 "summarize /data/work/README.md"', "easybits agents message ag_123 hi --json"],
1743
+ examples: ['easybits agents message helper "summarize /data/work/README.md"', "easybits agents message 6650f0c2a1b2c3d4e5f60718 hi --json"],
1638
1744
  async run(ctx) {
1639
- const id = need(ctx, 0, "agent-id", this.usage);
1745
+ const id = need(ctx, 0, "agent", this.usage);
1640
1746
  const content = ctx.args.slice(1).join(" ");
1641
1747
  if (!content) throw usageError("Missing <text>.", this.usage);
1642
1748
  const eb = await getClient(ctx);
@@ -1653,11 +1759,11 @@ var agents = {
1653
1759
  destroy: {
1654
1760
  aliases: ["rm", "delete"],
1655
1761
  summary: "Destroy an agent and its sandbox",
1656
- usage: "easybits agents destroy <agent-id> [--yes]",
1762
+ usage: "easybits agents destroy <agent> [--yes]",
1657
1763
  options: { ...YES_OPTION },
1658
- examples: ["easybits agents destroy ag_123 # asks you to type the id", "easybits agents destroy ag_123 --yes"],
1764
+ examples: ["easybits agents destroy helper # asks you to type the id", "easybits agents destroy helper --yes"],
1659
1765
  async run(ctx) {
1660
- const id = need(ctx, 0, "agent-id", this.usage);
1766
+ const id = need(ctx, 0, "agent", this.usage);
1661
1767
  requireYesIfHeadless(ctx);
1662
1768
  await confirm(ctx, `Destroy agent ${id} and its sandbox (including /data)?`, { typeName: id });
1663
1769
  const eb = await getClient(ctx);
@@ -1718,6 +1824,7 @@ var docs = {
1718
1824
  var COMMANDS = [
1719
1825
  login,
1720
1826
  logout,
1827
+ whoami,
1721
1828
  usage,
1722
1829
  sandboxes,
1723
1830
  agents,
@@ -1728,7 +1835,6 @@ var COMMANDS = [
1728
1835
  files,
1729
1836
  websites,
1730
1837
  providers,
1731
- config,
1732
1838
  mcp,
1733
1839
  sshKey,
1734
1840
  sshProxy,
@@ -1770,7 +1876,13 @@ var GLOBAL_FLAGS = `Global flags:
1770
1876
  -h, --help Help (also: easybits <command> --help)
1771
1877
  -v, --version Print the CLI version
1772
1878
 
1773
- Deletes (sandboxes destroy, agents destroy, db rm, domains rm, files delete) ask first;
1879
+ Naming (like gh): noun then verb \u2014 easybits <noun> <verb>. Old spellings still work
1880
+ and print the new one on stderr (never with --json).
1881
+
1882
+ Names or ids: agents, sandboxes/machines and databases take either
1883
+ (exact, case-insensitive; two with the same name \u2192 exit 2 with the ids).
1884
+
1885
+ Deletes (sandboxes destroy, agents destroy, db rm, domains rm, files rm) ask first;
1774
1886
  without a terminal or with --json pass -y/--yes, or they exit 2.
1775
1887
 
1776
1888
  Env: EASYBITS_API_KEY (API key), EASYBITS_URL (API server, default https://www.easybits.cloud)
@@ -1830,7 +1942,71 @@ var BANNER = " \x1B[38;2;244;245;251m\u2860\u2812\u2812\u28E4\x1B[38;
1830
1942
 
1831
1943
  // src/index.ts
1832
1944
  import { EasybitsError as EasybitsError2 } from "@easybits.cloud/sdk";
1833
- var VERSION = true ? "0.6.1" : "dev";
1945
+
1946
+ // src/aliases.ts
1947
+ function positionals(argv2) {
1948
+ const out = [];
1949
+ for (let i = 0; i < argv2.length; i++) {
1950
+ const a = argv2[i];
1951
+ if (a === "--") break;
1952
+ if (a === "--token") {
1953
+ i++;
1954
+ continue;
1955
+ }
1956
+ if (a.startsWith("-") && a !== "-") continue;
1957
+ out.push(i);
1958
+ }
1959
+ return out;
1960
+ }
1961
+ var MACHINE_SUBS = ["ls", "list", "deploy", "release", "releases", "logs", "rollback", "secrets"];
1962
+ var SANDBOX_NAMES = ["sandboxes", "sandbox", "sb"];
1963
+ function normalizeArgs(input) {
1964
+ const argv2 = [...input];
1965
+ const notes = [];
1966
+ const end = argv2.indexOf("--");
1967
+ const head = end === -1 ? argv2 : argv2.slice(0, end);
1968
+ const help = head.includes("--help") || head.includes("-h");
1969
+ let pos = positionals(argv2);
1970
+ const at = (n) => pos[n] != null ? argv2[pos[n]] : void 0;
1971
+ if (at(0) === "config") {
1972
+ argv2.splice(pos[0], 1, "mcp", "config");
1973
+ notes.push("`easybits config` is now `easybits mcp config`.");
1974
+ } else if (at(0) === "mcp" && at(1) == null && !help) {
1975
+ argv2.splice(pos[0] + 1, 0, "config");
1976
+ argv2.push("--stdio");
1977
+ notes.push("`easybits mcp` is now `easybits mcp config --stdio`.");
1978
+ }
1979
+ if (at(0) === "deploy") {
1980
+ const sub = at(1);
1981
+ if (sub && MACHINE_SUBS.includes(sub) && sub !== "deploy") {
1982
+ argv2[pos[0]] = "machines";
1983
+ notes.push(`\`easybits deploy ${sub}\` is now \`easybits machines ${sub}\`.`);
1984
+ } else if (sub === "deploy") {
1985
+ argv2[pos[0]] = "machines";
1986
+ notes.push("`easybits deploy deploy` is now `easybits deploy <machine>`.");
1987
+ } else {
1988
+ argv2.splice(pos[0], 1, "machines", "deploy");
1989
+ }
1990
+ pos = positionals(argv2);
1991
+ }
1992
+ if ((at(0) === "machines" || at(0) === "machine") && at(1) === "release") {
1993
+ argv2[pos[1]] = "deploy";
1994
+ notes.push("`machines release` is now `machines deploy`.");
1995
+ }
1996
+ if (SANDBOX_NAMES.includes(at(0) ?? "") && (at(1) === "create" || at(1) === "new")) {
1997
+ const stop = argv2.indexOf("--");
1998
+ for (let i = 0; i < (stop === -1 ? argv2.length : stop); i++) {
1999
+ if (argv2[i] === "--timeout" || argv2[i].startsWith("--timeout=")) {
2000
+ argv2[i] = argv2[i].replace("--timeout", "--ttl");
2001
+ notes.push("`sandboxes create --timeout` is now `--ttl` (`--timeout` stays for `exec`).");
2002
+ }
2003
+ }
2004
+ }
2005
+ return { argv: argv2, notes };
2006
+ }
2007
+
2008
+ // src/index.ts
2009
+ var VERSION = true ? "0.8.0" : "dev";
1834
2010
  var GLOBAL_OPTIONS = {
1835
2011
  json: { type: "boolean" },
1836
2012
  token: { type: "string" },
@@ -1862,13 +2038,13 @@ function positionalIndexes(argv2) {
1862
2038
  function parse(argv2, skip, leaf) {
1863
2039
  const rest = argv2.filter((_, i) => !skip.includes(i));
1864
2040
  try {
1865
- const { values, positionals } = parseArgs({
2041
+ const { values, positionals: positionals2 } = parseArgs({
1866
2042
  args: rest,
1867
2043
  options: { ...GLOBAL_OPTIONS, ...leaf?.options ?? {} },
1868
2044
  allowPositionals: true,
1869
2045
  strict: true
1870
2046
  });
1871
- return { values, positionals };
2047
+ return { values, positionals: positionals2 };
1872
2048
  } catch (e) {
1873
2049
  throw usageError(e.message.split("\n")[0].split(". To specify")[0].replace(/\.?$/, "."), leaf?.usage);
1874
2050
  }
@@ -1892,7 +2068,15 @@ function unknownSub(cmd, name) {
1892
2068
  ${e.hint}`;
1893
2069
  return e;
1894
2070
  }
1895
- async function main(argv2) {
2071
+ function aliasNote(note) {
2072
+ const dim = process.stderr.isTTY && !process.env.NO_COLOR;
2073
+ process.stderr.write(dim ? `\x1B[2m${note}\x1B[0m
2074
+ ` : `${note}
2075
+ `);
2076
+ }
2077
+ async function main(input) {
2078
+ const { argv: argv2, notes } = normalizeArgs(input);
2079
+ if (!wantsJson(argv2)) notes.forEach(aliasNote);
1896
2080
  const pos = positionalIndexes(argv2);
1897
2081
  const cmdName = pos.length ? argv2[pos[0]] : void 0;
1898
2082
  if (!cmdName || cmdName === "help") {
@@ -1922,22 +2106,25 @@ async function main(argv2) {
1922
2106
  const cmd = findCommand(cmdName);
1923
2107
  if (!cmd) throw unknownCommand(cmdName);
1924
2108
  let leaf = cmd.leaf;
2109
+ let subKey;
1925
2110
  const skip = [pos[0]];
1926
2111
  if (cmd.subs) {
1927
2112
  const subName = pos[1] != null ? argv2[pos[1]] : void 0;
1928
2113
  const entry = subName ? findSub(cmd, subName) : void 0;
1929
2114
  if (entry) {
1930
2115
  leaf = entry[1];
2116
+ subKey = entry[0];
1931
2117
  skip.push(pos[1]);
1932
2118
  } else if (subName) {
1933
2119
  throw unknownSub(cmd, subName);
1934
2120
  } else if (cmd.defaultSub && !argv2.includes("--help") && !argv2.includes("-h")) {
1935
2121
  leaf = cmd.subs[cmd.defaultSub];
2122
+ subKey = cmd.defaultSub;
1936
2123
  } else if (!argv2.includes("--help") && !argv2.includes("-h")) {
1937
2124
  throw usageError(`Missing subcommand for "${cmd.name}".`, `easybits ${cmd.name} <${Object.keys(cmd.subs).join("|")}>`);
1938
2125
  }
1939
2126
  }
1940
- const { values, positionals } = parse(argv2, skip, leaf);
2127
+ const { values, positionals: positionals2 } = parse(argv2, skip, leaf);
1941
2128
  if (values.version) {
1942
2129
  console.log(VERSION);
1943
2130
  return;
@@ -1949,14 +2136,20 @@ async function main(argv2) {
1949
2136
  const ctx = {
1950
2137
  json: values.json === true,
1951
2138
  token: typeof values.token === "string" ? values.token : void 0,
1952
- args: positionals,
2139
+ args: positionals2,
1953
2140
  opts: values
1954
2141
  };
2142
+ const refKey = `${cmd.name} ${subKey ?? ""}`.trim();
2143
+ const target = leaf;
2144
+ const run = async () => {
2145
+ await resolveRefs(ctx, refKey);
2146
+ await target.run.call(target, ctx);
2147
+ };
1955
2148
  try {
1956
- await leaf.run.call(leaf, ctx);
2149
+ await run();
1957
2150
  } catch (e) {
1958
2151
  if (e instanceof EasybitsError2 && e.status === 401 && usedSession() && await forceRefresh()) {
1959
- await leaf.run.call(leaf, ctx);
2152
+ await run();
1960
2153
  return;
1961
2154
  }
1962
2155
  throw e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Official CLI for EasyBits, the cloud for AI agents — sandboxes, hosting, domains, databases, agents and files from your terminal",
5
5
  "type": "module",
6
6
  "bin": {