@easybits.cloud/cli 0.20.0 → 0.21.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 CHANGED
@@ -54,6 +54,9 @@ easybits sandboxes bg kill sb_abc123 exec_123 # the whole process group
54
54
  easybits sandboxes git clone sb_abc123 --repo https://github.com/you/app.git --dir /data/work/app --git-token '$secret:GITHUB_TOKEN'
55
55
  easybits sandboxes git status sb_abc123 --dir /data/work/app
56
56
  easybits sandboxes git commit sb_abc123 --dir /data/work/app --all -m "fix: checkout"
57
+ easybits sandboxes codex login sb_abc123 # Codex on YOUR ChatGPT plan: shows a link + code, you sign in, saved to your vault
58
+ easybits sandboxes codex connect sb_def456 # puts that session in another box (installs Codex if missing)
59
+ easybits sandboxes codex save sb_abc123 # saves the renewed session back to the vault
57
60
  easybits sandboxes git push sb_abc123 --dir /data/work/app --git-token '$secret:GITHUB_TOKEN'
58
61
  easybits sandboxes git pull|checkout|log sb_abc123 --dir /data/work/app …
59
62
  easybits sandboxes suspend sb_abc123
package/dist/index.js CHANGED
@@ -1754,6 +1754,14 @@ function printGit(op, r) {
1754
1754
  async function sandboxAt(ctx, i, usage2) {
1755
1755
  return (await getClient(ctx)).sandboxes.get(need(ctx, i, "sandbox", usage2));
1756
1756
  }
1757
+ var CODEX_USAGE = "easybits sandboxes codex <login|connect|save> <sandbox> [--force] [--no-install]";
1758
+ function parseDeviceAuth(out) {
1759
+ const plain = out.replace(/\x1b\[[0-9;]*m/g, "");
1760
+ return {
1761
+ url: plain.match(/https:\/\/auth\.openai\.com\/\S+/)?.[0],
1762
+ code: plain.match(/\b[A-Z0-9]{4}-[A-Z0-9]{4,6}\b/)?.[0]
1763
+ };
1764
+ }
1757
1765
  var FILES_USAGE = "easybits sandboxes files <ls|read|write|rm|mv|mkdir> <sandbox> <path> [local-file|new-path]";
1758
1766
  var sandboxes = {
1759
1767
  name: "sandboxes",
@@ -2066,6 +2074,89 @@ var sandboxes = {
2066
2074
  }
2067
2075
  }
2068
2076
  },
2077
+ codex: {
2078
+ summary: "Codex on YOUR ChatGPT plan in a box: login once, then connect any box from your vault",
2079
+ usage: CODEX_USAGE,
2080
+ options: {
2081
+ force: { type: "boolean", description: "connect: replace the session the box already has" },
2082
+ "no-install": { type: "boolean", description: "connect/login: don't install Codex if it's missing" }
2083
+ },
2084
+ examples: [
2085
+ "easybits sb codex login sb_abc123 # shows a link + code; you sign in; the session goes to your vault",
2086
+ "easybits sb codex connect sb_def456 # puts that session in another box (installs Codex if missing)",
2087
+ "easybits sb codex save sb_abc123 # saves the box's (renewed) session back to the vault"
2088
+ ],
2089
+ async run(ctx) {
2090
+ const action = need(ctx, 0, "login|connect|save", CODEX_USAGE);
2091
+ const id = need(ctx, 1, "sandbox", CODEX_USAGE);
2092
+ const path = `/sandboxes/${encodeURIComponent(id)}`;
2093
+ if (action === "connect") {
2094
+ const r2 = await api(ctx, "POST", `${path}/codex-connect`, {
2095
+ force: bool(ctx, "force"),
2096
+ install: !bool(ctx, "no-install")
2097
+ });
2098
+ return emit(ctx, r2, () => {
2099
+ console.log(
2100
+ r2.session === "kept" ? t("The box already had a Codex session: kept it (use --force to replace it).", "La caja ya ten\xEDa sesi\xF3n de Codex: se conserv\xF3 (usa --force para reemplazarla).") : t("Codex session from your vault is in the box.", "La sesi\xF3n de Codex de tu vault ya est\xE1 en la caja.")
2101
+ );
2102
+ if (r2.codex === "no_npm") console.log(t("Codex isn't installed and the box has no npm: use a node template.", "Codex no est\xE1 instalado y la caja no tiene npm: usa un template node."));
2103
+ console.log(t(`Try it: easybits sb exec ${id} -- codex exec "hola"`, `Pru\xE9balo: easybits sb exec ${id} -- codex exec "hola"`));
2104
+ });
2105
+ }
2106
+ if (action === "save") {
2107
+ const r2 = await api(ctx, "POST", `${path}/codex-save`, {});
2108
+ return emit(
2109
+ ctx,
2110
+ r2,
2111
+ () => console.log(r2.saved ? t("Saved to your vault as CODEX_AUTH.", "Guardada en tu vault como CODEX_AUTH.") : t(`Not saved: ${r2.reason}`, `No se guard\xF3: ${r2.reason}`))
2112
+ );
2113
+ }
2114
+ if (action !== "login") throw usageError(t(`Unknown codex action "${action}".`, `Acci\xF3n de codex desconocida "${action}".`), CODEX_USAGE);
2115
+ const sb = await sandboxAt(ctx, 1, CODEX_USAGE);
2116
+ if (!bool(ctx, "no-install")) {
2117
+ const i = await sb.exec("command -v codex >/dev/null 2>&1 || npm i -g @openai/codex >/dev/null 2>&1; command -v codex", { timeoutSeconds: 240 });
2118
+ if (!i.stdout.trim()) throw new CliError(t("Couldn't install Codex in the box (needs npm: use a node template).", "No se pudo instalar Codex en la caja (necesita npm: usa un template node)."));
2119
+ }
2120
+ const bg = await sb.execBackground("codex login --device-auth");
2121
+ const started = Date.now();
2122
+ let shown = false;
2123
+ for (; ; ) {
2124
+ await new Promise((r2) => setTimeout(r2, 2e3));
2125
+ const st = await sb.bgStatus(bg.execId);
2126
+ const { url, code } = parseDeviceAuth(`${st.stdout ?? ""}
2127
+ ${st.stderr ?? ""}`);
2128
+ if (!shown && url && code) {
2129
+ shown = true;
2130
+ if (ctx.json) console.log(JSON.stringify({ url, code, expiresInMinutes: 15 }));
2131
+ else {
2132
+ console.log(t("1. Open: ", "1. Abre: ") + url);
2133
+ console.log(t("2. Code: ", "2. C\xF3digo: ") + code + t(" (15 min)", " (15 min)"));
2134
+ console.log(t("Waiting for you to sign in with your ChatGPT account\u2026", "Esperando a que inicies sesi\xF3n con tu cuenta de ChatGPT\u2026"));
2135
+ }
2136
+ }
2137
+ if (st.status !== "running") {
2138
+ if (st.exitCode !== 0) {
2139
+ const tail = `${st.stderr ?? ""}${st.stdout ?? ""}`.replace(/\x1b\[[0-9;]*m/g, "").trim().split("\n").slice(-3).join(" ");
2140
+ throw new CliError(t(`Codex login failed: ${tail}`, `El login de Codex fall\xF3: ${tail}`));
2141
+ }
2142
+ break;
2143
+ }
2144
+ if (Date.now() - started > 16 * 6e4) {
2145
+ await sb.bgKill(bg.execId).catch(() => void 0);
2146
+ throw new CliError(t("The code expired (15 min). Run it again.", "El c\xF3digo venci\xF3 (15 min). Vuelve a correrlo."));
2147
+ }
2148
+ }
2149
+ const r = await api(ctx, "POST", `${path}/codex-save`, {});
2150
+ return emit(
2151
+ ctx,
2152
+ { signedIn: true, ...r },
2153
+ () => console.log(t(
2154
+ `Signed in. Session saved to your vault (CODEX_AUTH): connect any other box with easybits sb codex connect <sandbox>`,
2155
+ `Listo. Sesi\xF3n guardada en tu vault (CODEX_AUTH): conecta cualquier otra caja con easybits sb codex connect <sandbox>`
2156
+ ))
2157
+ );
2158
+ }
2159
+ },
2069
2160
  git: {
2070
2161
  summary: "Git inside a sandbox: clone, status, commit, push, pull, checkout, log",
2071
2162
  usage: GIT_USAGE,
@@ -4940,7 +5031,7 @@ async function updateNotice(current2) {
4940
5031
  }
4941
5032
 
4942
5033
  // src/commands/doctor.ts
4943
- var VERSION = true ? "0.20.0" : "dev";
5034
+ var VERSION = true ? "0.21.0" : "dev";
4944
5035
  var doctor2 = {
4945
5036
  name: "doctor",
4946
5037
  group: "Account",
@@ -7377,6 +7468,12 @@ var HELP_ES = {
7377
7468
  "Long-running processes: start one in the background, list, poll its output, kill it": "Procesos largos: arranca uno en segundo plano, enl\xEDstalos, lee su salida, detenlo",
7378
7469
  "kill: seconds between SIGTERM and SIGKILL (default 5)": "kill: segundos entre SIGTERM y SIGKILL (default 5)",
7379
7470
  "status, exit code, stdout/stderr": "estado, c\xF3digo de salida, stdout/stderr",
7471
+ "Codex on YOUR ChatGPT plan in a box: login once, then connect any box from your vault": "Codex con TU plan de ChatGPT en una caja: inicia sesi\xF3n una vez y conecta cualquier caja desde tu vault",
7472
+ "connect: replace the session the box already has": "connect: reemplaza la sesi\xF3n que ya tenga la caja",
7473
+ "shows a link + code; you sign in; the session goes to your vault": "muestra un link y un c\xF3digo; inicias sesi\xF3n; la sesi\xF3n se guarda en tu vault",
7474
+ "puts that session in another box (installs Codex if missing)": "pone esa sesi\xF3n en otra caja (instala Codex si falta)",
7475
+ "saves the box's (renewed) session back to the vault": "devuelve al vault la sesi\xF3n (renovada) de la caja",
7476
+ "connect/login: don't install Codex if it's missing": "connect/login: no instalar Codex si falta",
7380
7477
  "Git inside a sandbox: clone, status, commit, push, pull, checkout, log": "Git dentro de un sandbox: clone, status, commit, push, pull, checkout, log",
7381
7478
  "Repo folder inside the sandbox (required)": "Carpeta del repo dentro del sandbox (obligatoria)",
7382
7479
  "clone: repo URL (clean; the token goes in --git-token)": "clone: URL del repo (limpia; el token va en --git-token)",
@@ -7680,7 +7777,7 @@ function normalizeArgs(input) {
7680
7777
  }
7681
7778
 
7682
7779
  // src/index.ts
7683
- var VERSION2 = true ? "0.20.0" : "dev";
7780
+ var VERSION2 = true ? "0.21.0" : "dev";
7684
7781
  var GLOBAL_OPTIONS = {
7685
7782
  json: { type: "boolean" },
7686
7783
  token: { type: "string" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/cli",
3
- "version": "0.20.0",
3
+ "version": "0.21.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": {