@easybits.cloud/cli 0.11.0 → 0.12.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 +7 -0
- package/dist/index.js +149 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,8 @@ easybits docs --open # open the docs in your browser
|
|
|
119
119
|
pass its name instead of the id (`easybits sb exec scratch -- ls`, `easybits domains ls shop`).
|
|
120
120
|
The match is exact and case-insensitive; if two share the name the CLI exits 2 and lists the ids.
|
|
121
121
|
|
|
122
|
+
Shell completion: `source <(easybits completion zsh)` (or `bash`, `fish`).
|
|
123
|
+
|
|
122
124
|
Help and messages follow your locale (`LANG=es_*` → Spanish) or `--lang es|en`.
|
|
123
125
|
|
|
124
126
|
Every command has help with examples: `easybits <command> <subcommand> --help`
|
|
@@ -161,6 +163,11 @@ Host *.ghosty
|
|
|
161
163
|
|
|
162
164
|
Full reference: https://www.easybits.cloud/docs#cli · https://www.easybits.cloud/en/docs/cli.md
|
|
163
165
|
|
|
166
|
+
## Changes in 0.12.0
|
|
167
|
+
|
|
168
|
+
- `easybits completion zsh|bash|fish`: TAB completes commands, subcommands, flags and the names of
|
|
169
|
+
your agents, sandboxes and databases (cached 5 min in `~/.cache/easybits/`).
|
|
170
|
+
|
|
164
171
|
## Changes in 0.11.0
|
|
165
172
|
|
|
166
173
|
- **Spanish or English, following your locale** (`LANG=es_*` → Spanish; otherwise English), or
|
package/dist/index.js
CHANGED
|
@@ -2957,6 +2957,130 @@ var agents = {
|
|
|
2957
2957
|
}
|
|
2958
2958
|
};
|
|
2959
2959
|
|
|
2960
|
+
// src/commands/completion.ts
|
|
2961
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
2962
|
+
import { homedir as homedir4 } from "os";
|
|
2963
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
2964
|
+
var GLOBAL_FLAGS = ["--json", "--token", "--lang", "--help", "--version"];
|
|
2965
|
+
var AGENT_FLAGS = /* @__PURE__ */ new Set(["--agent", "--like"]);
|
|
2966
|
+
var findSub = (c, name) => Object.entries(c.subs ?? {}).find(([n, l]) => n === name || l.aliases?.includes(name));
|
|
2967
|
+
function completionFor(words, current2, commands) {
|
|
2968
|
+
const names = commands.filter((c) => !c.name.startsWith("__")).map((c) => c.name);
|
|
2969
|
+
const prev = words[words.length - 1];
|
|
2970
|
+
if (prev && AGENT_FLAGS.has(prev)) return { kind: "names", ref: "agent" };
|
|
2971
|
+
if (!words.length) return { kind: "words", words: [...names, "help"] };
|
|
2972
|
+
const find = (n2) => commands.find((c) => c.name === n2 || c.aliases?.includes(n2));
|
|
2973
|
+
const cmd = find(words[0]);
|
|
2974
|
+
if (words[0] === "help") return { kind: "words", words: words.length === 1 ? names : Object.keys(find(words[1])?.subs ?? {}) };
|
|
2975
|
+
if (!cmd) return { kind: "words", words: [] };
|
|
2976
|
+
const sub = cmd.subs && words[1] ? findSub(cmd, words[1]) : void 0;
|
|
2977
|
+
const leaf = sub?.[1] ?? cmd.leaf;
|
|
2978
|
+
if (current2.startsWith("-")) {
|
|
2979
|
+
const own = Object.keys(leaf?.options ?? {}).map((n2) => `--${n2}`);
|
|
2980
|
+
return { kind: "words", words: [...own, ...GLOBAL_FLAGS] };
|
|
2981
|
+
}
|
|
2982
|
+
if (cmd.subs && !sub) return { kind: "words", words: words.length === 1 ? Object.keys(cmd.subs) : [] };
|
|
2983
|
+
if (cmd.name === "completion") return { kind: "words", words: words.length === 1 ? ["zsh", "bash", "fish"] : [] };
|
|
2984
|
+
const opts = leaf?.options ?? {};
|
|
2985
|
+
let n = 0;
|
|
2986
|
+
for (let i = sub ? 2 : 1; i < words.length; i++) {
|
|
2987
|
+
const w = words[i];
|
|
2988
|
+
if (w.startsWith("--")) {
|
|
2989
|
+
const o = opts[w.slice(2)];
|
|
2990
|
+
if ((o?.type === "string" || w === "--token" || w === "--lang") && !w.includes("=")) i++;
|
|
2991
|
+
continue;
|
|
2992
|
+
}
|
|
2993
|
+
if (w.startsWith("-") && w !== "-") continue;
|
|
2994
|
+
n++;
|
|
2995
|
+
}
|
|
2996
|
+
const spec = REF_ARGS[sub ? `${cmd.name} ${sub[0]}` : cmd.name];
|
|
2997
|
+
if (spec && spec.index === n) return { kind: "names", ref: spec.kind };
|
|
2998
|
+
return { kind: "words", words: [] };
|
|
2999
|
+
}
|
|
3000
|
+
var cacheFile = () => join6(process.env.XDG_CACHE_HOME || join6(homedir4(), ".cache"), "easybits", "complete.json");
|
|
3001
|
+
async function namesOf(ctx, kind) {
|
|
3002
|
+
let cache2 = {};
|
|
3003
|
+
try {
|
|
3004
|
+
cache2 = JSON.parse(readFileSync10(cacheFile(), "utf8"));
|
|
3005
|
+
const c = cache2[kind];
|
|
3006
|
+
if (c && Date.now() - c.at < 5 * 6e4) return c.names;
|
|
3007
|
+
} catch {
|
|
3008
|
+
}
|
|
3009
|
+
const names = [...new Set((await listRefs(ctx, kind)).map((x) => x.name).filter((x) => !!x))];
|
|
3010
|
+
try {
|
|
3011
|
+
mkdirSync5(dirname3(cacheFile()), { recursive: true });
|
|
3012
|
+
writeFileSync6(cacheFile(), JSON.stringify({ ...cache2, [kind]: { at: Date.now(), names } }));
|
|
3013
|
+
} catch {
|
|
3014
|
+
}
|
|
3015
|
+
return names;
|
|
3016
|
+
}
|
|
3017
|
+
async function completeCmd(args, commands) {
|
|
3018
|
+
const rest = args[0] === "--" ? args.slice(1) : args;
|
|
3019
|
+
const current2 = rest.length ? rest[rest.length - 1] : "";
|
|
3020
|
+
const words = rest.slice(0, -1);
|
|
3021
|
+
let out = [];
|
|
3022
|
+
try {
|
|
3023
|
+
const want = completionFor(words, current2, commands);
|
|
3024
|
+
const ctx = { json: true, args: [], opts: {} };
|
|
3025
|
+
out = want.kind === "words" ? want.words : await namesOf(ctx, want.ref);
|
|
3026
|
+
} catch {
|
|
3027
|
+
out = [];
|
|
3028
|
+
}
|
|
3029
|
+
const cur = current2.toLowerCase();
|
|
3030
|
+
process.stdout.write(out.filter((w) => w.toLowerCase().startsWith(cur)).join("\n") + "\n");
|
|
3031
|
+
}
|
|
3032
|
+
function completionScript(shell) {
|
|
3033
|
+
if (shell === "bash") {
|
|
3034
|
+
return `# easybits \u2014 bash completion: eval "$(easybits completion bash)" en ~/.bashrc
|
|
3035
|
+
_easybits() {
|
|
3036
|
+
# Las palabras ANTES de cambiar IFS: el bash 3.2 de macOS junta \${arr[@]:1:n} con un IFS propio.
|
|
3037
|
+
local words=("\${COMP_WORDS[@]:1:COMP_CWORD-1}") cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
3038
|
+
local IFS=$'\\n'
|
|
3039
|
+
COMPREPLY=( $(easybits __complete -- "\${words[@]}" "$cur" 2>/dev/null) )
|
|
3040
|
+
}
|
|
3041
|
+
complete -o default -F _easybits easybits
|
|
3042
|
+
`;
|
|
3043
|
+
}
|
|
3044
|
+
if (shell === "zsh") {
|
|
3045
|
+
return `#compdef easybits
|
|
3046
|
+
# easybits \u2014 zsh completion: source <(easybits completion zsh) en ~/.zshrc (despu\xE9s de compinit)
|
|
3047
|
+
_easybits() {
|
|
3048
|
+
local -a opts
|
|
3049
|
+
opts=("\${(@f)$(easybits __complete -- "\${(@)words[2,CURRENT-1]}" "\${words[CURRENT]}" 2>/dev/null)}")
|
|
3050
|
+
compadd -a opts
|
|
3051
|
+
}
|
|
3052
|
+
compdef _easybits easybits
|
|
3053
|
+
`;
|
|
3054
|
+
}
|
|
3055
|
+
if (shell === "fish") {
|
|
3056
|
+
return `# easybits \u2014 fish completion: easybits completion fish > ~/.config/fish/completions/easybits.fish
|
|
3057
|
+
complete -c easybits -f -a "(easybits __complete -- (commandline -opc)[2..-1] (commandline -ct) 2>/dev/null)"
|
|
3058
|
+
`;
|
|
3059
|
+
}
|
|
3060
|
+
return null;
|
|
3061
|
+
}
|
|
3062
|
+
var completion = {
|
|
3063
|
+
name: "completion",
|
|
3064
|
+
group: "Help",
|
|
3065
|
+
summary: "Shell completion script (zsh, bash, fish)",
|
|
3066
|
+
synopsis: "completion <shell>",
|
|
3067
|
+
leaf: {
|
|
3068
|
+
summary: "Print the completion script for your shell: commands, flags and your agents, sandboxes and databases",
|
|
3069
|
+
usage: "easybits completion <zsh|bash|fish>",
|
|
3070
|
+
examples: [
|
|
3071
|
+
"source <(easybits completion zsh) # add to ~/.zshrc, after compinit",
|
|
3072
|
+
'eval "$(easybits completion bash)" # add to ~/.bashrc',
|
|
3073
|
+
"easybits completion fish > ~/.config/fish/completions/easybits.fish"
|
|
3074
|
+
],
|
|
3075
|
+
async run(ctx) {
|
|
3076
|
+
const shell = need(ctx, 0, "zsh|bash|fish", this.usage);
|
|
3077
|
+
const script = completionScript(shell);
|
|
3078
|
+
if (!script) throw usageError(t(`Unknown shell "${shell}".`, `Shell desconocido "${shell}".`), this.usage);
|
|
3079
|
+
process.stdout.write(script);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
};
|
|
3083
|
+
|
|
2960
3084
|
// src/commands/docs.ts
|
|
2961
3085
|
import { spawn as spawn2 } from "child_process";
|
|
2962
3086
|
import { resolveBaseUrl as resolveBaseUrl5 } from "@easybits.cloud/sdk";
|
|
@@ -3005,15 +3129,15 @@ var docs = {
|
|
|
3005
3129
|
|
|
3006
3130
|
// src/commands/doctor.ts
|
|
3007
3131
|
import { existsSync as existsSync10, statSync as statSync5 } from "fs";
|
|
3008
|
-
import { homedir as
|
|
3009
|
-
import { join as
|
|
3132
|
+
import { homedir as homedir6 } from "os";
|
|
3133
|
+
import { join as join8 } from "path";
|
|
3010
3134
|
import { resolveBaseUrl as resolveBaseUrl6 } from "@easybits.cloud/sdk";
|
|
3011
3135
|
|
|
3012
3136
|
// src/update.ts
|
|
3013
|
-
import { mkdirSync as
|
|
3014
|
-
import { homedir as
|
|
3015
|
-
import { dirname as
|
|
3016
|
-
var FILE =
|
|
3137
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
3138
|
+
import { homedir as homedir5 } from "os";
|
|
3139
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
3140
|
+
var FILE = join7(process.env.XDG_CONFIG_HOME || join7(homedir5(), ".config"), "easybits", "update.json");
|
|
3017
3141
|
var DAY = 24 * 60 * 60 * 1e3;
|
|
3018
3142
|
var UPGRADE = "npm i -g @easybits.cloud/cli@latest";
|
|
3019
3143
|
function newer(a, b) {
|
|
@@ -3025,7 +3149,7 @@ function newer(a, b) {
|
|
|
3025
3149
|
var updateCheckDisabled = () => Boolean(process.env.EASYBITS_NO_UPDATE_CHECK || process.env.CI);
|
|
3026
3150
|
function readCache() {
|
|
3027
3151
|
try {
|
|
3028
|
-
return JSON.parse(
|
|
3152
|
+
return JSON.parse(readFileSync11(FILE, "utf8"));
|
|
3029
3153
|
} catch {
|
|
3030
3154
|
return {};
|
|
3031
3155
|
}
|
|
@@ -3035,8 +3159,8 @@ async function fetchLatest(timeoutMs = 1500) {
|
|
|
3035
3159
|
const r = await fetch("https://registry.npmjs.org/@easybits.cloud/cli/latest", { signal: AbortSignal.timeout(timeoutMs) });
|
|
3036
3160
|
const latest = (await r.json()).version;
|
|
3037
3161
|
if (!latest) return null;
|
|
3038
|
-
|
|
3039
|
-
|
|
3162
|
+
mkdirSync6(dirname4(FILE), { recursive: true });
|
|
3163
|
+
writeFileSync7(FILE, JSON.stringify({ latest, checkedAt: Date.now() }));
|
|
3040
3164
|
return latest;
|
|
3041
3165
|
} catch {
|
|
3042
3166
|
return null;
|
|
@@ -3060,7 +3184,7 @@ async function updateNotice(current2) {
|
|
|
3060
3184
|
}
|
|
3061
3185
|
|
|
3062
3186
|
// src/commands/doctor.ts
|
|
3063
|
-
var VERSION = true ? "0.
|
|
3187
|
+
var VERSION = true ? "0.12.0" : "dev";
|
|
3064
3188
|
var doctor2 = {
|
|
3065
3189
|
name: "doctor",
|
|
3066
3190
|
group: "Account",
|
|
@@ -3103,7 +3227,7 @@ var doctor2 = {
|
|
|
3103
3227
|
checks.push({ check: "credential-env", ok: true, detail: t("EASYBITS_API_KEY wins over the saved login in ~/.easybitsrc", "EASYBITS_API_KEY le gana a la sesi\xF3n guardada en ~/.easybitsrc") });
|
|
3104
3228
|
}
|
|
3105
3229
|
}
|
|
3106
|
-
const rcPath =
|
|
3230
|
+
const rcPath = join8(homedir6(), ".easybitsrc");
|
|
3107
3231
|
if (existsSync10(rcPath)) {
|
|
3108
3232
|
const mode = statSync5(rcPath).mode & 511;
|
|
3109
3233
|
const ok2 = (mode & 63) === 0;
|
|
@@ -3142,7 +3266,8 @@ var COMMANDS = [
|
|
|
3142
3266
|
mcp,
|
|
3143
3267
|
sshKey,
|
|
3144
3268
|
sshProxy,
|
|
3145
|
-
docs
|
|
3269
|
+
docs,
|
|
3270
|
+
completion
|
|
3146
3271
|
];
|
|
3147
3272
|
function findCommand(name) {
|
|
3148
3273
|
return COMMANDS.find((c) => c.name === name || c.aliases?.includes(name));
|
|
@@ -3337,7 +3462,11 @@ var HELP_ES = {
|
|
|
3337
3462
|
"Help": "Ayuda",
|
|
3338
3463
|
"Print the docs URL, or a docs section as markdown": "Imprime la URL de los docs, o una secci\xF3n de los docs en markdown",
|
|
3339
3464
|
"English docs": "Docs en ingl\xE9s",
|
|
3340
|
-
"Open the docs in your browser": "Abre los docs en tu navegador"
|
|
3465
|
+
"Open the docs in your browser": "Abre los docs en tu navegador",
|
|
3466
|
+
"Shell completion script (zsh, bash, fish)": "Script de autocompletado para tu shell (zsh, bash, fish)",
|
|
3467
|
+
"Print the completion script for your shell: commands, flags and your agents, sandboxes and databases": "Imprime el script de autocompletado de tu shell: comandos, flags y tus agentes, sandboxes y bases",
|
|
3468
|
+
"add to ~/.zshrc, after compinit": "agr\xE9galo a ~/.zshrc, despu\xE9s de compinit",
|
|
3469
|
+
"add to ~/.bashrc": "agr\xE9galo a ~/.bashrc"
|
|
3341
3470
|
};
|
|
3342
3471
|
|
|
3343
3472
|
// src/help.ts
|
|
@@ -3528,7 +3657,7 @@ function normalizeArgs(input) {
|
|
|
3528
3657
|
}
|
|
3529
3658
|
|
|
3530
3659
|
// src/index.ts
|
|
3531
|
-
var VERSION2 = true ? "0.
|
|
3660
|
+
var VERSION2 = true ? "0.12.0" : "dev";
|
|
3532
3661
|
var GLOBAL_OPTIONS = {
|
|
3533
3662
|
json: { type: "boolean" },
|
|
3534
3663
|
token: { type: "string" },
|
|
@@ -3572,7 +3701,7 @@ function parse(argv2, skip, leaf) {
|
|
|
3572
3701
|
throw usageError(e.message.split("\n")[0].split(". To specify")[0].replace(/\.?$/, "."), leaf?.usage);
|
|
3573
3702
|
}
|
|
3574
3703
|
}
|
|
3575
|
-
function
|
|
3704
|
+
function findSub2(cmd, name) {
|
|
3576
3705
|
return Object.entries(cmd.subs ?? {}).find(([n, l]) => n === name || l.aliases?.includes(name));
|
|
3577
3706
|
}
|
|
3578
3707
|
function unknownCommand(name) {
|
|
@@ -3586,7 +3715,7 @@ ${t("Run", "Corre")}: easybits --help`;
|
|
|
3586
3715
|
function unknownSub(cmd, name) {
|
|
3587
3716
|
const e = usageError(t(`Unknown subcommand "${cmd.name} ${name}".`, `Subcomando desconocido "${cmd.name} ${name}".`), `easybits ${cmd.name} <${Object.keys(cmd.subs ?? {}).join("|")}>`);
|
|
3588
3717
|
const names = Object.entries(cmd.subs ?? {}).flatMap(([n, l]) => [n, ...l.aliases ?? []]);
|
|
3589
|
-
const hint = didYouMean(name, names, `easybits ${cmd.name} `, (n) =>
|
|
3718
|
+
const hint = didYouMean(name, names, `easybits ${cmd.name} `, (n) => findSub2(cmd, n)?.[0] ?? n);
|
|
3590
3719
|
if (hint) e.hint = `${localHint(hint)}
|
|
3591
3720
|
${e.hint}`;
|
|
3592
3721
|
return e;
|
|
@@ -3600,6 +3729,7 @@ function aliasNote([en, es]) {
|
|
|
3600
3729
|
}
|
|
3601
3730
|
var localHint = (hint) => hint.replace("Did you mean this?", t("Did you mean this?", "\xBFQuisiste decir esto?"));
|
|
3602
3731
|
async function main(input) {
|
|
3732
|
+
if (input[0] === "__complete") return completeCmd(input.slice(1), COMMANDS);
|
|
3603
3733
|
const { argv: argv2, notes } = normalizeArgs(input);
|
|
3604
3734
|
if (!wantsJson(argv2)) notes.forEach(aliasNote);
|
|
3605
3735
|
const pos = positionalIndexes(argv2);
|
|
@@ -3609,7 +3739,7 @@ async function main(input) {
|
|
|
3609
3739
|
const topic = topicName ? findCommand(topicName) : void 0;
|
|
3610
3740
|
if (topicName && !topic) throw unknownCommand(topicName);
|
|
3611
3741
|
const subName = topic?.subs && pos[2] != null ? argv2[pos[2]] : void 0;
|
|
3612
|
-
const sub = topic && subName ?
|
|
3742
|
+
const sub = topic && subName ? findSub2(topic, subName) : void 0;
|
|
3613
3743
|
if (topic && subName && !sub) throw unknownSub(topic, subName);
|
|
3614
3744
|
const { values: values2 } = parse(argv2, pos.slice(0, sub ? 3 : 2), void 0);
|
|
3615
3745
|
if (values2.version) {
|
|
@@ -3635,7 +3765,7 @@ async function main(input) {
|
|
|
3635
3765
|
const skip = [pos[0]];
|
|
3636
3766
|
if (cmd.subs) {
|
|
3637
3767
|
const subName = pos[1] != null ? argv2[pos[1]] : void 0;
|
|
3638
|
-
const entry = subName ?
|
|
3768
|
+
const entry = subName ? findSub2(cmd, subName) : void 0;
|
|
3639
3769
|
if (entry) {
|
|
3640
3770
|
leaf = entry[1];
|
|
3641
3771
|
subKey = entry[0];
|
|
@@ -3682,7 +3812,7 @@ async function main(input) {
|
|
|
3682
3812
|
}
|
|
3683
3813
|
var argv = process.argv.slice(2);
|
|
3684
3814
|
setLang(detectLang(langFlag(argv)));
|
|
3685
|
-
var notice = process.stderr.isTTY && !wantsJson(argv) && !argv.includes("doctor") ? updateNotice(VERSION2) : null;
|
|
3815
|
+
var notice = process.stderr.isTTY && !wantsJson(argv) && !argv.includes("doctor") && argv[0] !== "__complete" ? updateNotice(VERSION2) : null;
|
|
3686
3816
|
main(argv).catch((err) => {
|
|
3687
3817
|
const e = toCliError(err);
|
|
3688
3818
|
const latest = e.code === "usage" ? cachedNewer(VERSION2) : null;
|
package/package.json
CHANGED