@easybits.cloud/cli 0.6.1 → 0.7.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 +8 -3
  2. package/dist/index.js +168 -47
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -75,9 +75,10 @@ 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
@@ -95,6 +96,10 @@ easybits docs cli # a docs section as markdown (--en for English)
95
96
  easybits docs --open # open the docs in your browser
96
97
  ```
97
98
 
99
+ **Names or ids.** Wherever a command takes an agent, a sandbox/machine or a database, you can
100
+ pass its name instead of the id (`easybits sb exec scratch -- ls`, `easybits domains ls shop`).
101
+ The match is exact and case-insensitive; if two share the name the CLI exits 2 and lists the ids.
102
+
98
103
  Every command has help with examples: `easybits <command> <subcommand> --help`
99
104
  (or `easybits help <command> <subcommand>`). A typo gets a "Did you mean" suggestion.
100
105
 
package/dist/index.js CHANGED
@@ -992,11 +992,11 @@ function printSandbox(s) {
992
992
  if (s.tier) console.log(`Tier: ${s.tier}${s.monthlyMxn != null ? ` ($${s.monthlyMxn} MXN/month)` : ""}`);
993
993
  }
994
994
  async function sandbox(ctx, usage2) {
995
- const id = need(ctx, 0, "sandbox-id", usage2);
995
+ const id = need(ctx, 0, "sandbox", usage2);
996
996
  const eb = await getClient(ctx);
997
997
  return eb.sandboxes.get(id);
998
998
  }
999
- var FILES_USAGE = "easybits sandboxes files <ls|read|write> <sandbox-id> <path> [local-file]";
999
+ var FILES_USAGE = "easybits sandboxes files <ls|read|write> <sandbox> <path> [local-file]";
1000
1000
  var sandboxes = {
1001
1001
  name: "sandboxes",
1002
1002
  aliases: ["sandbox", "sb"],
@@ -1061,7 +1061,7 @@ var sandboxes = {
1061
1061
  get: {
1062
1062
  aliases: ["show", "status"],
1063
1063
  summary: "Show one sandbox",
1064
- usage: "easybits sandboxes get <sandbox-id>",
1064
+ usage: "easybits sandboxes get <sandbox>",
1065
1065
  examples: ["easybits sandboxes get sb_abc123"],
1066
1066
  async run(ctx) {
1067
1067
  const rec = sandboxRecord(await sandbox(ctx, this.usage));
@@ -1070,7 +1070,7 @@ var sandboxes = {
1070
1070
  },
1071
1071
  exec: {
1072
1072
  summary: "Run a shell command inside a sandbox",
1073
- usage: "easybits sandboxes exec <sandbox-id> [--cwd <dir>] [--timeout <s>] [--dotenv <path>] -- <command...>",
1073
+ usage: "easybits sandboxes exec <sandbox> [--cwd <dir>] [--timeout <s>] [--dotenv <path>] -- <command...>",
1074
1074
  options: {
1075
1075
  cwd: { type: "string", value: "dir", description: "Working directory" },
1076
1076
  timeout: { type: "string", value: "seconds", description: "Kill the command after this long" },
@@ -1084,7 +1084,7 @@ var sandboxes = {
1084
1084
  "easybits sb exec sb_abc123 --json -- 'ls -la /' | jq .exitCode"
1085
1085
  ],
1086
1086
  async run(ctx) {
1087
- need(ctx, 0, "sandbox-id", this.usage);
1087
+ need(ctx, 0, "sandbox", this.usage);
1088
1088
  const command = ctx.args.slice(1).join(" ");
1089
1089
  if (!command) throw usageError("Missing <command>.", this.usage);
1090
1090
  const env = await envFrom(ctx, this.usage);
@@ -1107,7 +1107,7 @@ var sandboxes = {
1107
1107
  },
1108
1108
  logs: {
1109
1109
  summary: "Read the sandbox journal (optionally one systemd unit)",
1110
- usage: "easybits sandboxes logs <sandbox-id> [--unit <unit>] [--lines <n>] [--since <spec>] [--grep <text>]",
1110
+ usage: "easybits sandboxes logs <sandbox> [--unit <unit>] [--lines <n>] [--since <spec>] [--grep <text>]",
1111
1111
  options: {
1112
1112
  unit: { type: "string", value: "unit", description: "systemd unit to filter" },
1113
1113
  lines: { type: "string", value: "n", description: "Last N lines (default 200)" },
@@ -1142,7 +1142,7 @@ var sandboxes = {
1142
1142
  ],
1143
1143
  async run(ctx) {
1144
1144
  const action = need(ctx, 0, "ls|read|write", FILES_USAGE);
1145
- const id = need(ctx, 1, "sandbox-id", FILES_USAGE);
1145
+ const id = need(ctx, 1, "sandbox", FILES_USAGE);
1146
1146
  const path = need(ctx, 2, "path", FILES_USAGE);
1147
1147
  const eb = await getClient(ctx);
1148
1148
  const sb = await eb.sandboxes.get(id);
@@ -1186,7 +1186,7 @@ var sandboxes = {
1186
1186
  },
1187
1187
  suspend: {
1188
1188
  summary: "Suspend to disk (frees CPU; TTL paused)",
1189
- usage: "easybits sandboxes suspend <sandbox-id>",
1189
+ usage: "easybits sandboxes suspend <sandbox>",
1190
1190
  examples: ["easybits sandboxes suspend sb_abc123"],
1191
1191
  async run(ctx) {
1192
1192
  const r = await (await sandbox(ctx, this.usage)).suspend();
@@ -1195,7 +1195,7 @@ var sandboxes = {
1195
1195
  },
1196
1196
  resume: {
1197
1197
  summary: "Wake a suspended sandbox",
1198
- usage: "easybits sandboxes resume <sandbox-id> [--dotenv <path>] [--env K=V]...",
1198
+ usage: "easybits sandboxes resume <sandbox> [--dotenv <path>] [--env K=V]...",
1199
1199
  options: {
1200
1200
  env: { type: "string", multiple: true, value: "K=V", description: "Rewrite env on wake, non-secret (repeatable)" },
1201
1201
  ...ENV_FILE_OPTION
@@ -1210,11 +1210,11 @@ var sandboxes = {
1210
1210
  destroy: {
1211
1211
  aliases: ["rm", "delete"],
1212
1212
  summary: "Destroy a sandbox (irreversible)",
1213
- usage: "easybits sandboxes destroy <sandbox-id> [--yes]",
1213
+ usage: "easybits sandboxes destroy <sandbox> [--yes]",
1214
1214
  options: { ...YES_OPTION },
1215
1215
  examples: ["easybits sandboxes destroy sb_abc123", "easybits sb rm sb_abc123 --yes"],
1216
1216
  async run(ctx) {
1217
- need(ctx, 0, "sandbox-id", this.usage);
1217
+ need(ctx, 0, "sandbox", this.usage);
1218
1218
  requireYesIfHeadless(ctx);
1219
1219
  const sb = await sandbox(ctx, this.usage);
1220
1220
  await confirm(
@@ -1228,7 +1228,7 @@ var sandboxes = {
1228
1228
  },
1229
1229
  snapshot: {
1230
1230
  summary: "Capture a copy-on-write snapshot without stopping the box",
1231
- usage: "easybits sandboxes snapshot <sandbox-id> [--name <name>]",
1231
+ usage: "easybits sandboxes snapshot <sandbox> [--name <name>]",
1232
1232
  options: { name: { type: "string", value: "name", description: "Snapshot label" } },
1233
1233
  examples: ["easybits sandboxes snapshot sb_abc123 --name before-upgrade"],
1234
1234
  async run(ctx) {
@@ -1240,7 +1240,7 @@ var sandboxes = {
1240
1240
  };
1241
1241
 
1242
1242
  // src/commands/machines.ts
1243
- var SECRETS_USAGE = "easybits machines secrets <ls|set|unset> <machine-id> [--dotenv <path> | NAME]";
1243
+ var SECRETS_USAGE = "easybits machines secrets <ls|set|unset> <machine> [--dotenv <path> | NAME]";
1244
1244
  var machines = {
1245
1245
  name: "machines",
1246
1246
  aliases: ["deploy", "machine"],
@@ -1270,11 +1270,11 @@ var machines = {
1270
1270
  deploy: {
1271
1271
  aliases: ["release"],
1272
1272
  summary: "Publish the machine's current app code as a new release",
1273
- usage: "easybits machines deploy <machine-id> [--message <text>]",
1273
+ usage: "easybits machines deploy <machine> [--message <text>]",
1274
1274
  options: { message: { type: "string", short: "m", value: "text", description: "Release note" } },
1275
1275
  examples: ['easybits machines deploy sb_abc123 -m "v1.2: fix checkout"'],
1276
1276
  async run(ctx) {
1277
- const id = need(ctx, 0, "machine-id", this.usage);
1277
+ const id = need(ctx, 0, "machine", this.usage);
1278
1278
  const eb = await getClient(ctx);
1279
1279
  const r = await eb.machines.deploy(id, { message: str(ctx, "message") });
1280
1280
  emit(ctx, r, () => console.log(`Release v${r.version} ${r.releaseId} (${r.status}, ${fmtBytes(r.sizeBytes)})`));
@@ -1282,11 +1282,11 @@ var machines = {
1282
1282
  },
1283
1283
  releases: {
1284
1284
  summary: "List releases, newest first",
1285
- usage: "easybits machines releases <machine-id> [--limit <n>]",
1285
+ usage: "easybits machines releases <machine> [--limit <n>]",
1286
1286
  options: { limit: { type: "string", value: "n", description: "How many" } },
1287
1287
  examples: ["easybits machines releases sb_abc123 --limit 5"],
1288
1288
  async run(ctx) {
1289
- const id = need(ctx, 0, "machine-id", this.usage);
1289
+ const id = need(ctx, 0, "machine", this.usage);
1290
1290
  const eb = await getClient(ctx);
1291
1291
  const r = await eb.machines.releases(id, { limit: int(ctx, "limit", this.usage) });
1292
1292
  emit(
@@ -1295,21 +1295,21 @@ var machines = {
1295
1295
  () => table(
1296
1296
  r.items.map((x) => ({ ...x, size: fmtBytes(x.sizeBytes), createdAt: fmtDate(x.createdAt) })),
1297
1297
  [["version", "VERSION"], ["releaseId", "ID"], ["status", "STATUS"], ["size", "SIZE"], ["createdAt", "CREATED"], ["message", "MESSAGE"]],
1298
- "No releases yet. Publish one: easybits machines deploy <machine-id>"
1298
+ "No releases yet. Publish one: easybits machines deploy <machine>"
1299
1299
  )
1300
1300
  );
1301
1301
  }
1302
1302
  },
1303
1303
  logs: {
1304
1304
  summary: "Tail the app's own log",
1305
- usage: "easybits machines logs <machine-id> [--lines <n>] [--grep <text>]",
1305
+ usage: "easybits machines logs <machine> [--lines <n>] [--grep <text>]",
1306
1306
  options: {
1307
1307
  lines: { type: "string", value: "n", description: "Last N lines" },
1308
1308
  grep: { type: "string", value: "text", description: "Only lines matching" }
1309
1309
  },
1310
1310
  examples: ["easybits machines logs sb_abc123 --lines 100", "easybits machines logs sb_abc123 --grep ERROR"],
1311
1311
  async run(ctx) {
1312
- const id = need(ctx, 0, "machine-id", this.usage);
1312
+ const id = need(ctx, 0, "machine", this.usage);
1313
1313
  const eb = await getClient(ctx);
1314
1314
  const r = await eb.machines.logs(id, { lines: int(ctx, "lines", this.usage), grep: str(ctx, "grep") });
1315
1315
  emit(ctx, r, () => process.stdout.write(r.output.endsWith("\n") || !r.output ? r.output : r.output + "\n"));
@@ -1317,10 +1317,10 @@ var machines = {
1317
1317
  },
1318
1318
  rollback: {
1319
1319
  summary: "Put a previous release back on the same machine (data untouched)",
1320
- usage: "easybits machines rollback <machine-id> <release-id>",
1320
+ usage: "easybits machines rollback <machine> <release-id>",
1321
1321
  examples: ["easybits machines rollback sb_abc123 rel_789"],
1322
1322
  async run(ctx) {
1323
- const id = need(ctx, 0, "machine-id", this.usage);
1323
+ const id = need(ctx, 0, "machine", this.usage);
1324
1324
  const rel = need(ctx, 1, "release-id", this.usage);
1325
1325
  const eb = await getClient(ctx);
1326
1326
  const r = await eb.machines.rollback(id, rel);
@@ -1340,7 +1340,7 @@ var machines = {
1340
1340
  ],
1341
1341
  async run(ctx) {
1342
1342
  const action = need(ctx, 0, "ls|set|unset", SECRETS_USAGE);
1343
- const id = need(ctx, 1, "machine-id", SECRETS_USAGE);
1343
+ const id = need(ctx, 1, "machine", SECRETS_USAGE);
1344
1344
  const eb = await getClient(ctx);
1345
1345
  if (action === "ls" || action === "list") {
1346
1346
  const r = await eb.machines.secrets(id);
@@ -1368,7 +1368,7 @@ var machines = {
1368
1368
 
1369
1369
  // src/commands/domains.ts
1370
1370
  async function sandbox2(ctx, usage2) {
1371
- const id = need(ctx, 0, "sandbox-id", usage2);
1371
+ const id = need(ctx, 0, "sandbox", usage2);
1372
1372
  return (await getClient(ctx)).sandboxes.get(id);
1373
1373
  }
1374
1374
  var domains = {
@@ -1381,7 +1381,7 @@ var domains = {
1381
1381
  ls: {
1382
1382
  aliases: ["list"],
1383
1383
  summary: "List domains attached to a sandbox",
1384
- usage: "easybits domains ls <sandbox-id>",
1384
+ usage: "easybits domains ls <sandbox>",
1385
1385
  examples: ["easybits domains ls sb_abc123"],
1386
1386
  async run(ctx) {
1387
1387
  const items = await (await sandbox2(ctx, this.usage)).listDomains();
@@ -1390,7 +1390,7 @@ var domains = {
1390
1390
  },
1391
1391
  add: {
1392
1392
  summary: "Attach a domain to a port; prints the DNS record to create",
1393
- usage: "easybits domains add <sandbox-id> <domain> --port <port>",
1393
+ usage: "easybits domains add <sandbox> <domain> --port <port>",
1394
1394
  options: { port: { type: "string", value: "port", description: "Port inside the sandbox (required)" } },
1395
1395
  examples: ["easybits domains add sb_abc123 shop.example.com --port 3000"],
1396
1396
  async run(ctx) {
@@ -1407,7 +1407,7 @@ var domains = {
1407
1407
  },
1408
1408
  verify: {
1409
1409
  summary: "Check DNS + HTTPS for a domain",
1410
- usage: "easybits domains verify <sandbox-id> <domain>",
1410
+ usage: "easybits domains verify <sandbox> <domain>",
1411
1411
  examples: ["easybits domains verify sb_abc123 shop.example.com"],
1412
1412
  async run(ctx) {
1413
1413
  const domain = need(ctx, 1, "domain", this.usage);
@@ -1423,11 +1423,11 @@ var domains = {
1423
1423
  rm: {
1424
1424
  aliases: ["remove", "delete"],
1425
1425
  summary: "Detach a domain",
1426
- usage: "easybits domains rm <sandbox-id> <domain> [--yes]",
1426
+ usage: "easybits domains rm <sandbox> <domain> [--yes]",
1427
1427
  options: { ...YES_OPTION },
1428
1428
  examples: ["easybits domains rm sb_abc123 shop.example.com", "easybits domains rm sb_abc123 shop.example.com --yes"],
1429
1429
  async run(ctx) {
1430
- need(ctx, 0, "sandbox-id", this.usage);
1430
+ need(ctx, 0, "sandbox", this.usage);
1431
1431
  const domain = need(ctx, 1, "domain", this.usage);
1432
1432
  requireYesIfHeadless(ctx);
1433
1433
  await confirm(ctx, `Detach ${domain} from ${ctx.args[0]}? It stops serving right away.`);
@@ -1438,12 +1438,100 @@ var domains = {
1438
1438
  }
1439
1439
  };
1440
1440
 
1441
+ // src/refs.ts
1442
+ var ID_FORMAT = {
1443
+ agent: /^[0-9a-f]{24}$/i,
1444
+ db: /^[0-9a-f]{24}$/i,
1445
+ sandbox: /^sb_[0-9a-z-]+$/i
1446
+ };
1447
+ var looksLikeId = (kind, ref) => ID_FORMAT[kind].test(ref);
1448
+ var REF_ARGS = {
1449
+ "agents get": { index: 0, kind: "agent" },
1450
+ "agents message": { index: 0, kind: "agent" },
1451
+ "agents destroy": { index: 0, kind: "agent" },
1452
+ "sandboxes get": { index: 0, kind: "sandbox" },
1453
+ "sandboxes exec": { index: 0, kind: "sandbox" },
1454
+ "sandboxes logs": { index: 0, kind: "sandbox" },
1455
+ "sandboxes files": { index: 1, kind: "sandbox" },
1456
+ "sandboxes suspend": { index: 0, kind: "sandbox" },
1457
+ "sandboxes resume": { index: 0, kind: "sandbox" },
1458
+ "sandboxes destroy": { index: 0, kind: "sandbox" },
1459
+ "sandboxes snapshot": { index: 0, kind: "sandbox" },
1460
+ "machines deploy": { index: 0, kind: "sandbox" },
1461
+ "machines releases": { index: 0, kind: "sandbox" },
1462
+ "machines logs": { index: 0, kind: "sandbox" },
1463
+ "machines rollback": { index: 0, kind: "sandbox" },
1464
+ "machines secrets": { index: 1, kind: "sandbox" },
1465
+ "domains ls": { index: 0, kind: "sandbox" },
1466
+ "domains add": { index: 0, kind: "sandbox" },
1467
+ "domains verify": { index: 0, kind: "sandbox" },
1468
+ "domains rm": { index: 0, kind: "sandbox" },
1469
+ "db rm": { index: 0, kind: "db" },
1470
+ "db tables": { index: 0, kind: "db" },
1471
+ "db query": { index: 0, kind: "db" }
1472
+ };
1473
+ function matchRef(kind, ref, items) {
1474
+ if (looksLikeId(kind, ref)) return { ok: true, id: ref };
1475
+ const list2 = items ?? [];
1476
+ if (list2.some((x) => x.id === ref)) return { ok: true, id: ref };
1477
+ const want = ref.trim().toLowerCase();
1478
+ const matches = list2.filter((x) => (x.name ?? "").trim().toLowerCase() === want);
1479
+ if (matches.length === 1) return { ok: true, id: matches[0].id };
1480
+ if (matches.length === 0) return { ok: false, reason: "not_found" };
1481
+ return { ok: false, reason: "ambiguous", matches };
1482
+ }
1483
+
1484
+ // src/resolve.ts
1485
+ var LS_HINT = {
1486
+ agent: "easybits agents ls",
1487
+ sandbox: "easybits sandboxes ls",
1488
+ db: "easybits db ls"
1489
+ };
1490
+ var NOUN = { agent: "agent", sandbox: "sandbox or machine", db: "database" };
1491
+ var cache = /* @__PURE__ */ new Map();
1492
+ function listRefs(ctx, kind) {
1493
+ let p = cache.get(kind);
1494
+ if (!p) {
1495
+ p = (async () => {
1496
+ const eb = await getClient(ctx);
1497
+ if (kind === "agent") return (await eb.listAgents()).map((a) => ({ id: a.agentId, name: a.name, status: a.status }));
1498
+ if (kind === "db") return (await eb.listDatabases()).items.map((d) => ({ id: d.id, name: d.name }));
1499
+ return (await eb.sandboxes.list()).map((s) => ({ id: s.sandboxId, name: s.name, status: s.status }));
1500
+ })();
1501
+ p.catch(() => cache.delete(kind));
1502
+ cache.set(kind, p);
1503
+ }
1504
+ return p;
1505
+ }
1506
+ async function resolveRef(ctx, kind, ref) {
1507
+ if (looksLikeId(kind, ref)) return ref;
1508
+ const r = matchRef(kind, ref, await listRefs(ctx, kind));
1509
+ if (r.ok) return r.id;
1510
+ if (r.reason === "not_found") {
1511
+ throw new CliError(`No ${NOUN[kind]} with id or name "${ref}".`, EXIT.API, `List them with: ${LS_HINT[kind]}`, "not_found", 404);
1512
+ }
1513
+ const shown = r.matches.slice(0, 10).map((m) => ` ${m.id}${m.status ? ` (${m.status})` : ""}`);
1514
+ if (r.matches.length > 10) shown.push(` \u2026and ${r.matches.length - 10} more`);
1515
+ throw new CliError(
1516
+ `"${ref}" matches ${r.matches.length} ${kind === "db" ? "databases" : `${kind}s`}. Use the id:`,
1517
+ EXIT.USAGE,
1518
+ `${shown.join("\n")}
1519
+ List them with: ${LS_HINT[kind]}`,
1520
+ "ambiguous"
1521
+ );
1522
+ }
1523
+ async function resolveRefs(ctx, key) {
1524
+ const spec = REF_ARGS[key];
1525
+ const ref = spec ? ctx.args[spec.index] : void 0;
1526
+ if (!spec || !ref) return;
1527
+ ctx.args[spec.index] = await resolveRef(ctx, spec.kind, ref);
1528
+ }
1529
+
1441
1530
  // 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;
1531
+ async function findDb(ctx, id) {
1532
+ const hit = (await listRefs(ctx, "db")).find((d) => d.id === id);
1533
+ if (!hit) throw new CliError(`No database "${id}".`, 1, "List them with: easybits db ls", "not_found", 404);
1534
+ return { id, name: hit.name ?? id };
1447
1535
  }
1448
1536
  var db = {
1449
1537
  name: "db",
@@ -1493,7 +1581,7 @@ var db = {
1493
1581
  const ref = need(ctx, 0, "db-id|name", this.usage);
1494
1582
  requireYesIfHeadless(ctx);
1495
1583
  const eb = await getClient(ctx);
1496
- const hit = await findDb(eb, ref);
1584
+ const hit = await findDb(ctx, ref);
1497
1585
  await confirm(ctx, `Delete database ${hit.name} (${hit.id}) and all its data?`, { typeName: hit.name });
1498
1586
  const r = await eb.deleteDatabase(hit.id);
1499
1587
  emit(ctx, { ...r, id: hit.id, name: hit.name }, () => console.log(`Deleted ${hit.name} (${hit.id})`));
@@ -1508,7 +1596,7 @@ var db = {
1508
1596
  async run(ctx) {
1509
1597
  const ref = need(ctx, 0, "db-id|name", this.usage);
1510
1598
  const eb = await getClient(ctx);
1511
- const hit = await findDb(eb, ref);
1599
+ const hit = await findDb(ctx, ref);
1512
1600
  const q = (sql, args) => eb.queryDatabase(hit.id, sql, args);
1513
1601
  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
1602
  const ident = (n) => `"${n.replace(/"/g, '""')}"`;
@@ -1549,7 +1637,7 @@ var db = {
1549
1637
  const ref = need(ctx, 0, "db-id|name", this.usage);
1550
1638
  const sql = need(ctx, 1, "sql", this.usage);
1551
1639
  const eb = await getClient(ctx);
1552
- const hit = await findDb(eb, ref);
1640
+ const hit = await findDb(ctx, ref);
1553
1641
  const args = list(ctx, "arg");
1554
1642
  const r = await eb.queryDatabase(hit.id, sql, args.length ? args : void 0);
1555
1643
  emit(ctx, r, () => {
@@ -1595,6 +1683,27 @@ var agents = {
1595
1683
  );
1596
1684
  }
1597
1685
  },
1686
+ get: {
1687
+ aliases: ["show"],
1688
+ summary: "Show one agent (by id or name)",
1689
+ usage: "easybits agents get <agent>",
1690
+ examples: ["easybits agents get helper", "easybits agents get 6650f0c2a1b2c3d4e5f60718 --json"],
1691
+ async run(ctx) {
1692
+ const id = need(ctx, 0, "agent", this.usage);
1693
+ const eb = await getClient(ctx);
1694
+ const a = await eb.getAgent(id);
1695
+ emit(ctx, a, () => {
1696
+ console.log(`ID: ${a.agentId}`);
1697
+ if (a.name) console.log(`Name: ${a.name}`);
1698
+ console.log(`Template: ${a.template}`);
1699
+ console.log(`Status: ${a.status}`);
1700
+ console.log(`Sandbox: ${a.sandboxId}`);
1701
+ console.log(`URL: ${a.agentUrl}`);
1702
+ console.log(`Created: ${fmtDate(a.createdAt)}`);
1703
+ console.log(`Expires: ${a.expiresAt ? fmtDate(a.expiresAt) : "never"}`);
1704
+ });
1705
+ }
1706
+ },
1598
1707
  create: {
1599
1708
  summary: "Create an agent from a template",
1600
1709
  usage: "easybits agents create --template <template> [--name <name>] [--dotenv <path>] [--env K=V]... [--timeout <s>]",
@@ -1631,12 +1740,12 @@ var agents = {
1631
1740
  },
1632
1741
  message: {
1633
1742
  aliases: ["msg", "send"],
1634
- summary: "Send a message; streams the reply",
1635
- usage: "easybits agents message <agent-id> <text...> [--session <id>]",
1743
+ summary: "Send a message (agent by id or name); streams the reply",
1744
+ usage: "easybits agents message <agent> <text...> [--session <id>]",
1636
1745
  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"],
1746
+ examples: ['easybits agents message helper "summarize /data/work/README.md"', "easybits agents message 6650f0c2a1b2c3d4e5f60718 hi --json"],
1638
1747
  async run(ctx) {
1639
- const id = need(ctx, 0, "agent-id", this.usage);
1748
+ const id = need(ctx, 0, "agent", this.usage);
1640
1749
  const content = ctx.args.slice(1).join(" ");
1641
1750
  if (!content) throw usageError("Missing <text>.", this.usage);
1642
1751
  const eb = await getClient(ctx);
@@ -1653,11 +1762,11 @@ var agents = {
1653
1762
  destroy: {
1654
1763
  aliases: ["rm", "delete"],
1655
1764
  summary: "Destroy an agent and its sandbox",
1656
- usage: "easybits agents destroy <agent-id> [--yes]",
1765
+ usage: "easybits agents destroy <agent> [--yes]",
1657
1766
  options: { ...YES_OPTION },
1658
- examples: ["easybits agents destroy ag_123 # asks you to type the id", "easybits agents destroy ag_123 --yes"],
1767
+ examples: ["easybits agents destroy helper # asks you to type the id", "easybits agents destroy helper --yes"],
1659
1768
  async run(ctx) {
1660
- const id = need(ctx, 0, "agent-id", this.usage);
1769
+ const id = need(ctx, 0, "agent", this.usage);
1661
1770
  requireYesIfHeadless(ctx);
1662
1771
  await confirm(ctx, `Destroy agent ${id} and its sandbox (including /data)?`, { typeName: id });
1663
1772
  const eb = await getClient(ctx);
@@ -1770,6 +1879,9 @@ var GLOBAL_FLAGS = `Global flags:
1770
1879
  -h, --help Help (also: easybits <command> --help)
1771
1880
  -v, --version Print the CLI version
1772
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
+
1773
1885
  Deletes (sandboxes destroy, agents destroy, db rm, domains rm, files delete) ask first;
1774
1886
  without a terminal or with --json pass -y/--yes, or they exit 2.
1775
1887
 
@@ -1830,7 +1942,7 @@ 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
+ var VERSION = true ? "0.7.0" : "dev";
1834
1946
  var GLOBAL_OPTIONS = {
1835
1947
  json: { type: "boolean" },
1836
1948
  token: { type: "string" },
@@ -1922,17 +2034,20 @@ async function main(argv2) {
1922
2034
  const cmd = findCommand(cmdName);
1923
2035
  if (!cmd) throw unknownCommand(cmdName);
1924
2036
  let leaf = cmd.leaf;
2037
+ let subKey;
1925
2038
  const skip = [pos[0]];
1926
2039
  if (cmd.subs) {
1927
2040
  const subName = pos[1] != null ? argv2[pos[1]] : void 0;
1928
2041
  const entry = subName ? findSub(cmd, subName) : void 0;
1929
2042
  if (entry) {
1930
2043
  leaf = entry[1];
2044
+ subKey = entry[0];
1931
2045
  skip.push(pos[1]);
1932
2046
  } else if (subName) {
1933
2047
  throw unknownSub(cmd, subName);
1934
2048
  } else if (cmd.defaultSub && !argv2.includes("--help") && !argv2.includes("-h")) {
1935
2049
  leaf = cmd.subs[cmd.defaultSub];
2050
+ subKey = cmd.defaultSub;
1936
2051
  } else if (!argv2.includes("--help") && !argv2.includes("-h")) {
1937
2052
  throw usageError(`Missing subcommand for "${cmd.name}".`, `easybits ${cmd.name} <${Object.keys(cmd.subs).join("|")}>`);
1938
2053
  }
@@ -1952,11 +2067,17 @@ async function main(argv2) {
1952
2067
  args: positionals,
1953
2068
  opts: values
1954
2069
  };
2070
+ const refKey = `${cmd.name} ${subKey ?? ""}`.trim();
2071
+ const target = leaf;
2072
+ const run = async () => {
2073
+ await resolveRefs(ctx, refKey);
2074
+ await target.run.call(target, ctx);
2075
+ };
1955
2076
  try {
1956
- await leaf.run.call(leaf, ctx);
2077
+ await run();
1957
2078
  } catch (e) {
1958
2079
  if (e instanceof EasybitsError2 && e.status === 401 && usedSession() && await forceRefresh()) {
1959
- await leaf.run.call(leaf, ctx);
2080
+ await run();
1960
2081
  return;
1961
2082
  }
1962
2083
  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.7.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": {