@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.
- package/README.md +30 -9
- package/dist/index.js +308 -115
- 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 --
|
|
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
|
|
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
|
|
79
|
-
easybits agents message
|
|
80
|
-
easybits agents
|
|
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
|
|
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
|
|
92
|
-
easybits mcp
|
|
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
|
|
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
|
|
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
|
|
512
|
-
name: "
|
|
512
|
+
var mcp = {
|
|
513
|
+
name: "mcp",
|
|
513
514
|
group: "MCP",
|
|
514
|
-
summary: "Print MCP config JSON
|
|
515
|
-
synopsis: "
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
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
|
|
542
|
-
name: "
|
|
543
|
-
group: "
|
|
544
|
-
summary: "
|
|
545
|
-
synopsis: "
|
|
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: "
|
|
548
|
-
usage: "easybits
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
-
|
|
678
|
-
aliases: ["
|
|
675
|
+
rm: {
|
|
676
|
+
aliases: ["delete", "remove"],
|
|
679
677
|
summary: "Delete a file (soft delete, 7-day trash)",
|
|
680
|
-
usage: "easybits files
|
|
678
|
+
usage: "easybits files rm <file-id> [--yes]",
|
|
681
679
|
options: { ...YES_OPTION },
|
|
682
|
-
examples: ["easybits files
|
|
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
|
|
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
|
|
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>] [--
|
|
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
|
-
|
|
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, "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1241
|
+
var SECRETS_USAGE = "easybits machines secrets <ls|set|unset> <machine> [--dotenv <path> | NAME]";
|
|
1244
1242
|
var machines = {
|
|
1245
1243
|
name: "machines",
|
|
1246
|
-
aliases: ["
|
|
1244
|
+
aliases: ["machine"],
|
|
1247
1245
|
group: "Hosting",
|
|
1248
|
-
summary: "Permanent machines and their releases (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1445
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1762
|
+
usage: "easybits agents destroy <agent> [--yes]",
|
|
1657
1763
|
options: { ...YES_OPTION },
|
|
1658
|
-
examples: ["easybits agents destroy
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
2149
|
+
await run();
|
|
1957
2150
|
} catch (e) {
|
|
1958
2151
|
if (e instanceof EasybitsError2 && e.status === 401 && usedSession() && await forceRefresh()) {
|
|
1959
|
-
await
|
|
2152
|
+
await run();
|
|
1960
2153
|
return;
|
|
1961
2154
|
}
|
|
1962
2155
|
throw e;
|
package/package.json
CHANGED