@easybits.cloud/cli 0.7.0 → 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 +22 -6
  2. package/dist/index.js +141 -69
  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
@@ -83,14 +83,15 @@ easybits agents destroy helper --yes
83
83
  # Files, websites, account
84
84
  easybits files ls
85
85
  easybits files upload ./report.pdf
86
- easybits files delete FILE_ID --yes
86
+ easybits files rm FILE_ID --yes
87
87
  easybits websites ls
88
88
  easybits providers # storage provider (Tigris by default)
89
89
  easybits usage
90
90
 
91
91
  # MCP, SSH, docs
92
- easybits config # MCP config (streamable HTTP)
93
- 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)
94
95
  easybits ssh-key # public key for sandbox SSH
95
96
  easybits docs cli # a docs section as markdown (--en for English)
96
97
  easybits docs --open # open the docs in your browser
@@ -104,7 +105,7 @@ Every command has help with examples: `easybits <command> <subcommand> --help`
104
105
  (or `easybits help <command> <subcommand>`). A typo gets a "Did you mean" suggestion.
105
106
 
106
107
  **Destructive commands** (`db rm`, `agents destroy`, `sandboxes destroy`, `domains rm`,
107
- `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
108
109
  irreversible ones (a database, an agent, a permanent sandbox). Without a terminal they
109
110
  never prompt — and neither do they with `--json`: pass `--yes` / `-y`, or they exit `2`.
110
111
 
@@ -134,6 +135,21 @@ Host *.ghosty
134
135
 
135
136
  Full reference: https://www.easybits.cloud/docs#cli · https://www.easybits.cloud/en/docs/cli.md
136
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
+
137
153
  ## Changes in 0.6.1
138
154
 
139
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);
@@ -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")
@@ -1243,16 +1241,16 @@ var sandboxes = {
1243
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,11 +1266,10 @@ 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
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
1274
  const id = need(ctx, 0, "machine", this.usage);
1278
1275
  const eb = await getClient(ctx);
@@ -1535,7 +1532,7 @@ async function findDb(ctx, id) {
1535
1532
  }
1536
1533
  var db = {
1537
1534
  name: "db",
1538
- aliases: ["databases"],
1535
+ aliases: ["databases", "database"],
1539
1536
  group: "Data",
1540
1537
  summary: "SQL databases (libSQL)",
1541
1538
  synopsis: "db",
@@ -1827,6 +1824,7 @@ var docs = {
1827
1824
  var COMMANDS = [
1828
1825
  login,
1829
1826
  logout,
1827
+ whoami,
1830
1828
  usage,
1831
1829
  sandboxes,
1832
1830
  agents,
@@ -1837,7 +1835,6 @@ var COMMANDS = [
1837
1835
  files,
1838
1836
  websites,
1839
1837
  providers,
1840
- config,
1841
1838
  mcp,
1842
1839
  sshKey,
1843
1840
  sshProxy,
@@ -1879,10 +1876,13 @@ var GLOBAL_FLAGS = `Global flags:
1879
1876
  -h, --help Help (also: easybits <command> --help)
1880
1877
  -v, --version Print the CLI version
1881
1878
 
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
1882
  Names or ids: agents, sandboxes/machines and databases take either
1883
1883
  (exact, case-insensitive; two with the same name \u2192 exit 2 with the ids).
1884
1884
 
1885
- Deletes (sandboxes destroy, agents destroy, db rm, domains rm, files delete) ask first;
1885
+ Deletes (sandboxes destroy, agents destroy, db rm, domains rm, files rm) ask first;
1886
1886
  without a terminal or with --json pass -y/--yes, or they exit 2.
1887
1887
 
1888
1888
  Env: EASYBITS_API_KEY (API key), EASYBITS_URL (API server, default https://www.easybits.cloud)
@@ -1942,7 +1942,71 @@ var BANNER = " \x1B[38;2;244;245;251m\u2860\u2812\u2812\u28E4\x1B[38;
1942
1942
 
1943
1943
  // src/index.ts
1944
1944
  import { EasybitsError as EasybitsError2 } from "@easybits.cloud/sdk";
1945
- var VERSION = true ? "0.7.0" : "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";
1946
2010
  var GLOBAL_OPTIONS = {
1947
2011
  json: { type: "boolean" },
1948
2012
  token: { type: "string" },
@@ -1974,13 +2038,13 @@ function positionalIndexes(argv2) {
1974
2038
  function parse(argv2, skip, leaf) {
1975
2039
  const rest = argv2.filter((_, i) => !skip.includes(i));
1976
2040
  try {
1977
- const { values, positionals } = parseArgs({
2041
+ const { values, positionals: positionals2 } = parseArgs({
1978
2042
  args: rest,
1979
2043
  options: { ...GLOBAL_OPTIONS, ...leaf?.options ?? {} },
1980
2044
  allowPositionals: true,
1981
2045
  strict: true
1982
2046
  });
1983
- return { values, positionals };
2047
+ return { values, positionals: positionals2 };
1984
2048
  } catch (e) {
1985
2049
  throw usageError(e.message.split("\n")[0].split(". To specify")[0].replace(/\.?$/, "."), leaf?.usage);
1986
2050
  }
@@ -2004,7 +2068,15 @@ function unknownSub(cmd, name) {
2004
2068
  ${e.hint}`;
2005
2069
  return e;
2006
2070
  }
2007
- 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);
2008
2080
  const pos = positionalIndexes(argv2);
2009
2081
  const cmdName = pos.length ? argv2[pos[0]] : void 0;
2010
2082
  if (!cmdName || cmdName === "help") {
@@ -2052,7 +2124,7 @@ async function main(argv2) {
2052
2124
  throw usageError(`Missing subcommand for "${cmd.name}".`, `easybits ${cmd.name} <${Object.keys(cmd.subs).join("|")}>`);
2053
2125
  }
2054
2126
  }
2055
- const { values, positionals } = parse(argv2, skip, leaf);
2127
+ const { values, positionals: positionals2 } = parse(argv2, skip, leaf);
2056
2128
  if (values.version) {
2057
2129
  console.log(VERSION);
2058
2130
  return;
@@ -2064,7 +2136,7 @@ async function main(argv2) {
2064
2136
  const ctx = {
2065
2137
  json: values.json === true,
2066
2138
  token: typeof values.token === "string" ? values.token : void 0,
2067
- args: positionals,
2139
+ args: positionals2,
2068
2140
  opts: values
2069
2141
  };
2070
2142
  const refKey = `${cmd.name} ${subKey ?? ""}`.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.7.0",
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": {