@ghostystudio/cli 0.37.0 → 0.39.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 +12 -1
- package/dist/index.js +97 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ ghosty bags ls|create|set|rm <workspace> ["bag"] [--tokens 20M] [--recurrin
|
|
|
21
21
|
ghosty spaces create <slug> [--combo …: staff] [--dry-run] # yours, trial
|
|
22
22
|
ghosty spaces renew <workspace> --until YYYY-MM-DD [--note] [--dry-run] # staff
|
|
23
23
|
ghosty spaces rename <old> <new> [--dry-run] # staff, slug only
|
|
24
|
+
ghosty spaces transfer <workspace> --to EMAIL [--keep-as admin] [--copy-keys] [--dry-run] # staff, new owner
|
|
24
25
|
ghosty agents ls [--engine E] [--ids] [--sponsor EMAIL | --owner EMAIL] # --sponsor/--owner: any account (Ghosty staff)
|
|
25
26
|
ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,…] [--workspace SLUG] [--channels teams=off]
|
|
26
27
|
ghosty agents get <id> [--fields name,model,prompt] [--prompt-out FILE]
|
|
@@ -90,7 +91,7 @@ ghosty demos try <slug> "message" ["message 2"…] [--new] [--recycle-between --
|
|
|
90
91
|
ghosty demos history <id> [--since 2d] [--session web:…] [--timeline] [--full] # what demo visitors did (Ghosty staff)
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
Global: `--json` (stdout is JSON only), `--token <token>` / `GHOSTY_TOKEN` (a `gat_…` agent token reaches only that agent), `--profile <name>` / `GHOSTY_PROFILE` (a separate saved session, e.g. one per account), `--yes`, `--help`, `--version`. Env: `GHOSTY_URL`, `XDG_CONFIG_HOME`, `NO_COLOR`, `GHOSTY_NO_UPDATE_CHECK`; partner commands sign with `GHOSTY_PARTNER_KEY` + `
|
|
94
|
+
Global: `--json` (stdout is JSON only), `--token <token>` / `GHOSTY_TOKEN` (a `gat_…` agent token reaches only that agent), `--profile <name>` / `GHOSTY_PROFILE` (a separate saved session, e.g. one per account), `--yes`, `--help`, `--version`. Env: `GHOSTY_URL`, `XDG_CONFIG_HOME`, `NO_COLOR`, `GHOSTY_NO_UPDATE_CHECK`; partner commands sign with `GHOSTY_PARTNER_KEY` + `GHOSTY_PARTNER_KEY_SECRET` when set (`GHOSTY_PARTNER_SECRET` is the old name).
|
|
94
95
|
|
|
95
96
|
Aliases: `creds` = `credentials`, `db` = `dbs`, `dbs sql` = `dbs query`.
|
|
96
97
|
|
|
@@ -98,6 +99,16 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
98
99
|
|
|
99
100
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
100
101
|
|
|
102
|
+
### Changes in 0.39.0
|
|
103
|
+
|
|
104
|
+
- `spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys]` (staff): hands a workspace to a new owner in one step. Its agents go with it and spend the new owner's key, so `--copy-keys` copies the ones they lack (the dry run lists what's missing); the previous owner keeps the role you pick, and the agents' boxes are recycled so the new owner shows up everywhere.
|
|
105
|
+
|
|
106
|
+
### Changes in 0.38.0
|
|
107
|
+
|
|
108
|
+
- `spaces set <workspace> --model-cap 0.25|1|4|none --house-claude on|off` (staff): the most expensive model a workspace can pick, and whether its agents run on Ghosty's Claude. Both used to be only in /admin/tenants; a trial caps at 0.25 and Claude doesn't fit.
|
|
109
|
+
- `partner credentials create|rotate --out-env PATH`: Node 22 swallows `--env-file` as its own flag (`node: ./.env.partner: not found`) even after the script. `--env-file` still works as an alias where your Node lets it through.
|
|
110
|
+
- Docs and help use one name for the secret: `GHOSTY_PARTNER_KEY_SECRET` (`GHOSTY_PARTNER_SECRET` is the old name and still works).
|
|
111
|
+
|
|
101
112
|
### Changes in 0.35.0
|
|
102
113
|
|
|
103
114
|
- `spaces create <slug> [--dry-run]`: create a workspace of your own from the terminal (trial, same rules as the web form). Then `partner enable <slug>`.
|
package/dist/index.js
CHANGED
|
@@ -7829,6 +7829,7 @@ var SERVER_MESSAGES = [
|
|
|
7829
7829
|
["Ya tienes {} cajas de trabajo (tope {}). Suelta una con destroy \u2014 las de tu lista: {}.", "You already have {} work boxes (limit {}). Release one with destroy \u2014 the ones on your list: {}."],
|
|
7830
7830
|
["Template desconocido. Los que puedes pedir: {}.", "Unknown template. The ones you can ask for: {}."],
|
|
7831
7831
|
["Tama\xF1o desconocido. Elige: {}.", "Unknown size. Choose: {}."],
|
|
7832
|
+
["keepHours va de 0 a {}.", "keepHours goes from 0 to {}."],
|
|
7832
7833
|
["La caja no termin\xF3 de despertar. Reintenta en unos segundos.", "The box didn't finish waking up. Try again in a few seconds."],
|
|
7833
7834
|
["command requerido", "command required"],
|
|
7834
7835
|
["Puerto inv\xE1lido: va entre 1024 y 65535, y los puertos {} (runtime de la caja) y {} (sistema) no se publican \u2014 levanta el server en otro (5173, 4321, 8080\u2026).", "Invalid port: it must be between 1024 and 65535, and ports {} (box runtime) and {} (system) aren't published \u2014 run the server on another one (5173, 4321, 8080\u2026)."],
|
|
@@ -7974,6 +7975,13 @@ var SERVER_MESSAGES = [
|
|
|
7974
7975
|
["las bolsas son topes pactados: s\xF3lo el staff de Ghosty", "bags are agreed caps: Ghosty staff only"],
|
|
7975
7976
|
["renovar un espacio: s\xF3lo el staff de Ghosty", "renewing a workspace: Ghosty staff only"],
|
|
7976
7977
|
["renombrar un espacio: s\xF3lo el staff de Ghosty", "renaming a workspace: Ghosty staff only"],
|
|
7978
|
+
["traspasar un espacio: s\xF3lo el staff de Ghosty", "transferring a workspace: Ghosty staff only"],
|
|
7979
|
+
["correo inv\xE1lido: \xAB{}\xBB", "invalid email: \xAB{}\xBB"],
|
|
7980
|
+
["{} ya es due\xF1o de {}", "{} already owns {}"],
|
|
7981
|
+
["{} no tiene cuenta: se precrea y la reclama al entrar con ese correo", "{} has no account: it is pre-created and claimed on login with that email"],
|
|
7982
|
+
["sin llave de {}: sus agentes dejan de contestar (agrega --copy-keys)", "no {} key: its agents stop answering (add --copy-keys)"],
|
|
7983
|
+
["sin llave de {}: sus agentes dejan de contestar", "no {} key: its agents stop answering"],
|
|
7984
|
+
["no se recicl\xF3 {}: {}", "{} was not recycled: {}"],
|
|
7977
7985
|
["s\xF3lo el due\xF1o de \xAB{}\xBB o el staff de Ghosty", "only the owner of \xAB{}\xBB or Ghosty staff"],
|
|
7978
7986
|
["la bolsa de un agente s\xF3lo la cambia el staff de Ghosty", "only Ghosty staff changes an agent's bag"],
|
|
7979
7987
|
["este agente no tiene espacio: no puede tener bolsa", "this agent has no workspace: it can't have a bag"],
|
|
@@ -8070,6 +8078,9 @@ var SERVER_MESSAGES = [
|
|
|
8070
8078
|
['el espacio "{}" ya existe', 'workspace "{}" already exists'],
|
|
8071
8079
|
["{} ya tiene {} espacio(s) y su plan permite {}", "{} already owns {} workspace(s) and their plan allows {}"],
|
|
8072
8080
|
["no se pudo crear el espacio", "couldn't create the workspace"],
|
|
8081
|
+
["model-cap: {} | none (el de su plan)", "model-cap: {} | none (its plan's)"],
|
|
8082
|
+
["cambiar el tope de modelo de un espacio: s\xF3lo el staff de Ghosty", "changing a workspace's model cap: Ghosty staff only"],
|
|
8083
|
+
["cambiar la Claude de casa de un espacio: s\xF3lo el staff de Ghosty", "changing a workspace's house Claude: Ghosty staff only"],
|
|
8073
8084
|
["${turn.token} s\xF3lo funciona en motores del pool (claude-worker y similares), no en ACP", "${turn.token} only works on pool engines (claude-worker and similar), not ACP"],
|
|
8074
8085
|
["servers[{}]: ${turn.token} s\xF3lo va en headers de un MCP http (url)", "servers[{}]: ${turn.token} only goes in the headers of an http MCP (url)"],
|
|
8075
8086
|
["turnToken debe ser un string no vac\xEDo", "turnToken must be a non-empty string"],
|
|
@@ -8379,7 +8390,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8379
8390
|
const req = httpsRequest(`${base}${path}`, {
|
|
8380
8391
|
method: "POST",
|
|
8381
8392
|
timeout: timeoutMs,
|
|
8382
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8393
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.39.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8383
8394
|
}, (res) => {
|
|
8384
8395
|
let buf = "";
|
|
8385
8396
|
res.on("data", (c) => buf += c);
|
|
@@ -8421,7 +8432,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8421
8432
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8422
8433
|
headers: {
|
|
8423
8434
|
authorization: `Bearer ${token}`,
|
|
8424
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8435
|
+
"user-agent": `ghosty-cli/${"0.39.0"}`,
|
|
8425
8436
|
"accept-language": lang(),
|
|
8426
8437
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8427
8438
|
...opts.headers
|
|
@@ -9502,7 +9513,7 @@ ${bold("Partner space")} ${dim("(you, signed in as the space owner)")}
|
|
|
9502
9513
|
partner ls Your partner spaces
|
|
9503
9514
|
partner enable <workspace> Turn one of your spaces into a partner space
|
|
9504
9515
|
partner credentials ls Server credentials (no secrets)
|
|
9505
|
-
partner credentials create [--label L] Issue one \u2014 the secret is shown ONCE [--env
|
|
9516
|
+
partner credentials create [--label L] Issue one \u2014 the secret is shown ONCE [--out-env PATH | --format env]
|
|
9506
9517
|
partner credentials rotate <keyId> [--grace MIN] New secret, same keyId (old one valid MIN, default 60)
|
|
9507
9518
|
partner credentials revoke <keyId> Revoke it (idempotent)
|
|
9508
9519
|
partner audit Who did what, when
|
|
@@ -9547,7 +9558,7 @@ ${bold("Espacio de partner")} ${dim("(t\xFA, con sesi\xF3n como due\xF1a del es
|
|
|
9547
9558
|
partner ls Tus espacios de partner
|
|
9548
9559
|
partner enable <workspace> Convierte uno de tus espacios en espacio de partner
|
|
9549
9560
|
partner credentials ls Credenciales de servidor (sin secretos)
|
|
9550
|
-
partner credentials create [--label L] Emite una \u2014 el secreto se muestra UNA vez [--env
|
|
9561
|
+
partner credentials create [--label L] Emite una \u2014 el secreto se muestra UNA vez [--out-env RUTA | --format env]
|
|
9551
9562
|
partner credentials rotate <keyId> [--grace MIN] Secreto nuevo, mismo keyId (el viejo vale MIN, default 60)
|
|
9552
9563
|
partner credentials revoke <keyId> La revoca (idempotente)
|
|
9553
9564
|
partner audit Qui\xE9n hizo qu\xE9 y cu\xE1ndo
|
|
@@ -9607,7 +9618,7 @@ async function call(path, v, init = {}) {
|
|
|
9607
9618
|
"x-ghosty-ts": ts,
|
|
9608
9619
|
"x-ghosty-sig": sig,
|
|
9609
9620
|
"content-type": "application/json",
|
|
9610
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9621
|
+
"user-agent": `ghosty-cli/${"0.39.0"}`
|
|
9611
9622
|
},
|
|
9612
9623
|
body: method === "GET" ? void 0 : raw
|
|
9613
9624
|
});
|
|
@@ -9630,9 +9641,10 @@ var envLines = (r) => `GHOSTY_PARTNER_KEY=${r.keyId}
|
|
|
9630
9641
|
GHOSTY_PARTNER_KEY_SECRET=${r.secret}
|
|
9631
9642
|
`;
|
|
9632
9643
|
async function showSecret(r, v) {
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9644
|
+
const envFile = v["out-env"] ?? v["env-file"];
|
|
9645
|
+
if (envFile) {
|
|
9646
|
+
await writeFile5(envFile, envLines(r), { mode: 384 });
|
|
9647
|
+
return out({ keyId: r.keyId, envFile }, () => `${lavender("\u2713")} ${r.keyId} ${t(`\u2192 ${envFile} (mode 600; the secret was not printed)`, `\u2192 ${envFile} (modo 600; el secreto no se imprimi\xF3)`)}`);
|
|
9636
9648
|
}
|
|
9637
9649
|
if (v.format === "env") return void process.stdout.write(envLines(r));
|
|
9638
9650
|
if (v.format) throw usage(t("--format takes env.", "--format acepta env."));
|
|
@@ -9642,7 +9654,7 @@ async function showSecret(r, v) {
|
|
|
9642
9654
|
`${lavender("\u2713")} ${t("Save these now \u2014 the secret is not shown again:", "Gu\xE1rdalas ya \u2014 el secreto no se vuelve a mostrar:")}`,
|
|
9643
9655
|
` GHOSTY_PARTNER_KEY=${r.keyId}`,
|
|
9644
9656
|
` GHOSTY_PARTNER_KEY_SECRET=${r.secret}`,
|
|
9645
|
-
dim(t(" Next time: --env
|
|
9657
|
+
dim(t(" Next time: --out-env PATH (0600) or --format env | fly secrets import", " La pr\xF3xima: --out-env RUTA (0600) o --format env | fly secrets import"))
|
|
9646
9658
|
].join("\n")
|
|
9647
9659
|
);
|
|
9648
9660
|
}
|
|
@@ -9655,7 +9667,7 @@ async function credentialsCmd(sub, id, v) {
|
|
|
9655
9667
|
() => r.credentials.length ? table(
|
|
9656
9668
|
r.credentials.map((c) => ({ keyId: c.keyId, label: c.label, status: c.status, lastUsed: fmt(c.lastUsedAt), created: fmt(c.createdAt) })),
|
|
9657
9669
|
["keyId", "label", "status", "lastUsed", "created"]
|
|
9658
|
-
) : dim(t("No credentials yet. Run: ghosty partner credentials create --label prod --env
|
|
9670
|
+
) : dim(t("No credentials yet. Run: ghosty partner credentials create --label prod --out-env .env.partner", "Todav\xEDa no hay credenciales. Corre: ghosty partner credentials create --label prod --out-env .env.partner"))
|
|
9659
9671
|
);
|
|
9660
9672
|
}
|
|
9661
9673
|
if (sub === "create" || sub === "new") {
|
|
@@ -11261,16 +11273,21 @@ var HELPS_ES = {
|
|
|
11261
11273
|
notes: ["ls dice, de una ya enviada, cu\xE1ntos entreg\xF3 y rechaz\xF3 FCM (del push log, desde el 1-oct).", "--image se publica en img.ghosty.studio (WebP 1200 px). --to vac\xEDo = todos. send sin --at la manda ya (pregunta); --at '' quita la programaci\xF3n.", "--min-version N (versionCode de Android): si la app del tester es m\xE1s vieja, el bot\xF3n dice \xABActualizar\xBB y lanza la actualizaci\xF3n de Play desde la app; '' la quita."]
|
|
11262
11274
|
},
|
|
11263
11275
|
spaces: {
|
|
11264
|
-
summary: "Espacios: crea uno a tu nombre (prueba), lista los tuyos (o, staff, los de cualquiera) y operaciones del staff de Ghosty \u2014 entrar/salir del espacio de un cliente, cambiar su combo, mover \xABpagado hasta\xBB, renombrar su slug. Cada cambio queda en la bit\xE1cora del espacio.",
|
|
11276
|
+
summary: "Espacios: crea uno a tu nombre (prueba), lista los tuyos (o, staff, los de cualquiera) y operaciones del staff de Ghosty \u2014 entrar/salir del espacio de un cliente, cambiar su combo, mover \xABpagado hasta\xBB, renombrar su slug, cambiar de due\xF1o. Cada cambio queda en la bit\xE1cora del espacio.",
|
|
11265
11277
|
flags: [
|
|
11266
11278
|
["--user EMAIL", "ls (staff): los espacios de esa persona"],
|
|
11267
11279
|
["--combo C", "set (staff): teams | sales | both \u2014 sin Ventas sus tableros existen pero nadie los ve; create: distinto de teams, s\xF3lo staff"],
|
|
11280
|
+
["--model-cap X", "set (staff): el modelo m\xE1s caro que puede elegir el espacio \u2014 0.25 econ\xF3micos, 1 est\xE1ndar (Sonnet), 4 todos; none = el de su plan"],
|
|
11281
|
+
["--house-claude on|off", "set (staff): sus agentes corren con la Claude de Ghosty si el due\xF1o no trae llave; recicla sus cajas de Claude"],
|
|
11268
11282
|
["--until DATE", "renew: el nuevo \xABpagado hasta\xBB (AAAA-MM-DD)"],
|
|
11269
11283
|
["--note TEXT", "renew: por qu\xE9 (va a la bit\xE1cora)"],
|
|
11284
|
+
["--to EMAIL", "transfer: el due\xF1o nuevo (si no tiene cuenta se precrea y la reclama al entrar)"],
|
|
11285
|
+
["--keep-as R", "transfer: el rol del due\xF1o anterior \u2014 admin | member (default) | none (sale del espacio)"],
|
|
11286
|
+
["--copy-keys", "transfer: copia las llaves de proveedor del anterior que le falten al nuevo (los agentes gastan la llave del due\xF1o nuevo)"],
|
|
11270
11287
|
["-n, --dry-run", "ense\xF1a qu\xE9 cambia, no cambia nada"],
|
|
11271
11288
|
["--yes", "s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
|
|
11272
11289
|
],
|
|
11273
|
-
notes: ["create sigue las reglas de la web: queda a tu nombre, nace como prueba de 30 d\xEDas, tu correo debe estar confirmado y tu plan topa cu\xE1ntos tienes. Sigue: ghosty partner enable <slug>.", "Con suscripci\xF3n de Stripe renew se niega: la fecha la mueve la factura pagada.", "rename cambia s\xF3lo el slug (id y namespace se quedan, as\xED que tablero, Stripe y WhatsApp siguen funcionando); las ligas viejas <slug>.sales dejan de servir."]
|
|
11290
|
+
notes: ["create sigue las reglas de la web: queda a tu nombre, nace como prueba de 30 d\xEDas, tu correo debe estar confirmado y tu plan topa cu\xE1ntos tienes. Sigue: ghosty partner enable <slug>.", "Con suscripci\xF3n de Stripe renew se niega: la fecha la mueve la factura pagada.", "rename cambia s\xF3lo el slug (id y namespace se quedan, as\xED que tablero, Stripe y WhatsApp siguen funcionando); las ligas viejas <slug>.sales dejan de servir.", "transfer se lleva los agentes con el espacio y recicla sus cajas (el due\xF1o va horneado en la caja); sin --copy-keys la simulaci\xF3n dice qu\xE9 llaves le faltan al nuevo \u2014 esos agentes dejar\xEDan de contestar."]
|
|
11274
11291
|
}
|
|
11275
11292
|
};
|
|
11276
11293
|
var HELP_ALIASES = { creds: "credentials", board: "cards", conversations: "chat", workspaces: "spaces" };
|
|
@@ -11834,18 +11851,23 @@ HELPS.novedades = {
|
|
|
11834
11851
|
learn: `${REF}#soporte`
|
|
11835
11852
|
};
|
|
11836
11853
|
HELPS.spaces = {
|
|
11837
|
-
summary: "Workspaces: create one of your own (trial), list yours (or, staff, anyone's), and Ghosty staff operations \u2014 join/leave a client's workspace, change its combo, move \xABpaid until\xBB, rename its slug. Each change is logged in the workspace's audit.",
|
|
11838
|
-
usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces create <slug> [--combo teams|sales|both] [--dry-run]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]"],
|
|
11854
|
+
summary: "Workspaces: create one of your own (trial), list yours (or, staff, anyone's), and Ghosty staff operations \u2014 join/leave a client's workspace, change its combo, move \xABpaid until\xBB, rename its slug, change its owner. Each change is logged in the workspace's audit.",
|
|
11855
|
+
usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces create <slug> [--combo teams|sales|both] [--dry-run]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]", "ghosty spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] [--yes]"],
|
|
11839
11856
|
flags: [
|
|
11840
11857
|
["--user EMAIL", "ls (staff): that person's workspaces"],
|
|
11841
11858
|
["--combo C", "set (staff): teams | sales | both \u2014 without Sales its boards exist but nobody sees them; create: other than teams, staff only"],
|
|
11859
|
+
["--model-cap X", "set (staff): the most expensive model the workspace can pick \u2014 0.25 economy, 1 standard (Sonnet), 4 everything; none = its plan's"],
|
|
11860
|
+
["--house-claude on|off", "set (staff): its agents run on Ghosty's Claude when the owner brings no key; recycles their Claude boxes"],
|
|
11842
11861
|
["--until DATE", "renew: the new \xABpaid until\xBB (YYYY-MM-DD)"],
|
|
11843
11862
|
["--note TEXT", "renew: why (goes into the audit)"],
|
|
11863
|
+
["--to EMAIL", "transfer: the new owner (if they have no account it is pre-created and claimed on login)"],
|
|
11864
|
+
["--keep-as R", "transfer: the previous owner's role \u2014 admin | member (default) | none (removed)"],
|
|
11865
|
+
["--copy-keys", "transfer: copy the previous owner's provider keys the new one lacks (agents spend the new owner's key)"],
|
|
11844
11866
|
["-n, --dry-run", "show what changes, change nothing"],
|
|
11845
11867
|
["--yes", "skip the confirmation (required without a terminal)"]
|
|
11846
11868
|
],
|
|
11847
|
-
examples: ["ghosty spaces ls", "ghosty spaces create mailmask --dry-run", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run"],
|
|
11848
|
-
notes: ["create follows the same rules as the web: you own it, it starts as a 30-day trial, your email must be confirmed, and your plan caps how many you own. Next: ghosty partner enable <slug>.", "With a Stripe subscription renew refuses: the paid invoice moves the date.", "rename changes only the slug (id and namespace stay, so board, Stripe and WhatsApp keep working); old <slug>.sales links stop working."],
|
|
11869
|
+
examples: ["ghosty spaces ls", "ghosty spaces create mailmask --dry-run", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", "ghosty spaces set mailmask --model-cap 4 --house-claude on", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run", "ghosty spaces transfer siiqtec --to siiqtec@gmail.com --keep-as admin --copy-keys --dry-run"],
|
|
11870
|
+
notes: ["create follows the same rules as the web: you own it, it starts as a 30-day trial, your email must be confirmed, and your plan caps how many you own. Next: ghosty partner enable <slug>.", "With a Stripe subscription renew refuses: the paid invoice moves the date.", "rename changes only the slug (id and namespace stay, so board, Stripe and WhatsApp keep working); old <slug>.sales links stop working.", "transfer moves the agents with the workspace and recycles their boxes (the owner is baked into the box); without --copy-keys the dry run lists which keys the new owner lacks \u2014 those agents would stop answering."],
|
|
11849
11871
|
learn: `${REF}#espacios`
|
|
11850
11872
|
};
|
|
11851
11873
|
HELPS.workspaces = HELPS.spaces;
|
|
@@ -12330,7 +12352,7 @@ ${bagsText(r)}`);
|
|
|
12330
12352
|
}
|
|
12331
12353
|
}
|
|
12332
12354
|
async function spacesCmd(sub, slug, arg, v, context) {
|
|
12333
|
-
const ex = "ghosty spaces ls [--user EMAIL] \xB7 create <slug> [--combo teams|sales|both] [--dry-run] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run]";
|
|
12355
|
+
const ex = "ghosty spaces ls [--user EMAIL] \xB7 create <slug> [--combo teams|sales|both] [--dry-run] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run] \xB7 transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run]";
|
|
12334
12356
|
if (!sub || sub === "ls" || sub === "list") {
|
|
12335
12357
|
const r = await api(`/api/v2/workspaces${v.user ? `?user=${enc10(v.user)}` : ""}`);
|
|
12336
12358
|
const rows = r.workspaces.map((w) => ({ workspace: w.slug, role: w.role ?? "", plan: w.plan, combo: w.combo, owner: w.owner ?? "", paid: w.paidUntil ?? "" }));
|
|
@@ -12352,19 +12374,28 @@ ${dim(t(`Next: ghosty use ${r.workspace} \xB7 ghosty partner enable ${r.workspac
|
|
|
12352
12374
|
return out(r, () => `${lavender("\u2713")} ${r.joined ? t(`You're in ${r.workspace} as staff (no seat; logged). Leave: ghosty spaces leave ${r.workspace}`, `Entraste a ${r.workspace} como staff (sin asiento; queda en bit\xE1cora). Salir: ghosty spaces leave ${r.workspace}`) : t(`You left ${r.workspace}.`, `Saliste de ${r.workspace}.`)}`);
|
|
12353
12375
|
}
|
|
12354
12376
|
if (sub === "set") {
|
|
12355
|
-
slug = needSpace(slug ?? context, "ghosty spaces set <workspace> --combo sales");
|
|
12356
|
-
|
|
12357
|
-
const
|
|
12358
|
-
|
|
12359
|
-
const
|
|
12360
|
-
if (
|
|
12361
|
-
if (
|
|
12377
|
+
slug = needSpace(slug ?? context, "ghosty spaces set <workspace> --combo sales | --model-cap 4");
|
|
12378
|
+
const cap = v["model-cap"];
|
|
12379
|
+
const house = parseSwitch("--house-claude", v["house-claude"]);
|
|
12380
|
+
if (!v.combo && cap === void 0 && house === void 0) throw usage(t("Nothing to change.", "Nada que cambiar."), "--combo teams|sales|both \xB7 --model-cap 0.25|1|4|none \xB7 --house-claude on|off");
|
|
12381
|
+
const steps = [];
|
|
12382
|
+
if (v.combo) steps.push({ label: "combo", path: wsPath(slug, "/combo"), body: { combo: v.combo } });
|
|
12383
|
+
if (cap !== void 0) steps.push({ label: "model-cap", path: wsPath(slug, "/model-cap"), body: { cap } });
|
|
12384
|
+
if (house !== void 0) steps.push({ label: "house-claude", path: wsPath(slug, "/house-claude"), body: { on: house } });
|
|
12385
|
+
const show = (x) => x === null ? t("plan's", "el del plan") : x === true ? "on" : x === false ? "off" : String(x);
|
|
12386
|
+
const plans = await Promise.all(steps.map(async (st) => ({ ...st, plan: await api(st.path, { method: "POST", body: { ...st.body, dryRun: true } }) })));
|
|
12387
|
+
const lines = plans.map((p) => `${p.plan.workspace}: ${p.label} ${show(p.plan.from)} \u2192 ${show(p.plan.to)}`);
|
|
12388
|
+
const pending = plans.filter((p) => p.plan.changed);
|
|
12389
|
+
if (!pending.length) return out(plans.map((p) => p.plan), () => dim(t(`Nothing to do: ${slug} already has that.`, `Nada que hacer: ${slug} ya lo tiene.`)));
|
|
12390
|
+
if (v["dry-run"]) return out(plans.map((p) => p.plan), () => `${lines.join("\n")}
|
|
12362
12391
|
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12392
|
+
const retry = `ghosty spaces set ${slug}${v.combo ? ` --combo ${v.combo}` : ""}${cap !== void 0 ? ` --model-cap ${cap}` : ""}${house !== void 0 ? ` --house-claude ${house ? "on" : "off"}` : ""}`;
|
|
12393
|
+
requireYesIfHeadless(v.yes, retry);
|
|
12394
|
+
await confirm(`${lines.join("\n")}
|
|
12395
|
+
${t("Change it?", "\xBFCambiarlo?")}`, { yes: v.yes, retry });
|
|
12396
|
+
const done = [];
|
|
12397
|
+
for (const p of pending) done.push(await api(p.path, { method: "POST", body: p.body }));
|
|
12398
|
+
return out(done, () => done.map((r, i) => `${lavender("\u2713")} ${r.workspace}: ${pending[i].label} ${show(r.to)}`).join("\n"));
|
|
12368
12399
|
}
|
|
12369
12400
|
if (sub === "renew") {
|
|
12370
12401
|
slug = needSpace(slug ?? context, ex);
|
|
@@ -12395,6 +12426,29 @@ ${t("Rename?", "\xBFRenombrar?")}`, { yes: v.yes, expect: plan.from, retry: `gho
|
|
|
12395
12426
|
const r = await api(path, { method: "POST", body: { to: arg } });
|
|
12396
12427
|
return out(r, () => `${lavender("\u2713")} ${t(`Renamed: ${r.from} \u2192 ${r.to}. Undo: ghosty spaces rename ${r.to} ${r.from}`, `Renombrado: ${r.from} \u2192 ${r.to}. Deshacer: ghosty spaces rename ${r.to} ${r.from}`)}`);
|
|
12397
12428
|
}
|
|
12429
|
+
if (sub === "transfer") {
|
|
12430
|
+
slug = needSpace(slug ?? context, "ghosty spaces transfer <workspace> --to EMAIL --dry-run");
|
|
12431
|
+
const toEmail = v.to ?? arg;
|
|
12432
|
+
if (!toEmail) throw usage(t("Missing --to EMAIL.", "Falta --to CORREO."), `ghosty spaces transfer ${slug} --to new@owner.com --keep-as admin --copy-keys --dry-run`);
|
|
12433
|
+
const body = { to: toEmail, ...v["keep-as"] ? { keepAs: v["keep-as"] } : {}, ...v["copy-keys"] ? { copyKeys: true } : {} };
|
|
12434
|
+
const path = wsPath(slug, "/transfer");
|
|
12435
|
+
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
12436
|
+
const text = [
|
|
12437
|
+
`${plan.workspace}: ${plan.from ?? "\u2014"} \u2192 ${bold(plan.to)} ${dim(t(`(previous owner stays as ${plan.keepAs})`, `(el anterior queda como ${plan.keepAs})`))}`,
|
|
12438
|
+
`${t("agents", "agentes")}: ${plan.agents.join(", ") || "\u2014"}`,
|
|
12439
|
+
`${t("keys", "llaves")}: ${plan.keys.copy.length ? t(`copies ${plan.keys.copy.join(", ")}`, `copia ${plan.keys.copy.join(", ")}`) : t("copies none", "no copia")}${plan.keys.alreadyHas.length ? dim(t(` \xB7 already has ${plan.keys.alreadyHas.join(", ")}`, ` \xB7 ya tiene ${plan.keys.alreadyHas.join(", ")}`)) : ""}`,
|
|
12440
|
+
...plan.warnings.map((w) => red(` \xB7 ${serverText(w)}`))
|
|
12441
|
+
].join("\n");
|
|
12442
|
+
if (v["dry-run"]) return out(plan, () => `${text}
|
|
12443
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
12444
|
+
const retry = `ghosty spaces transfer ${slug} --to ${toEmail}${v["keep-as"] ? ` --keep-as ${v["keep-as"]}` : ""}${v["copy-keys"] ? " --copy-keys" : ""}`;
|
|
12445
|
+
requireYesIfHeadless(v.yes, retry);
|
|
12446
|
+
await confirm(`${text}
|
|
12447
|
+
${t("Transfer it? Its agents' boxes are recycled (a turn in flight finishes first).", "\xBFTraspasarlo? Se reciclan las cajas de sus agentes (un turno en vuelo termina antes).")}`, { yes: v.yes, expect: plan.workspace, retry });
|
|
12448
|
+
const r = await api(path, { method: "POST", body });
|
|
12449
|
+
return out(r, () => `${lavender("\u2713")} ${t(`${r.workspace} now belongs to ${r.to}; ${r.from ?? "\u2014"} stays as ${r.keepAs}. ${r.recycled} agent(s) recycled.`, `${r.workspace} ya es de ${r.to}; ${r.from ?? "\u2014"} queda como ${r.keepAs}. ${r.recycled} agente(s) reciclado(s).`)}${r.warnings.length ? `
|
|
12450
|
+
${r.warnings.map((w) => red(` \xB7 ${serverText(w)}`)).join("\n")}` : ""}`);
|
|
12451
|
+
}
|
|
12398
12452
|
throw usage(t(`Unknown spaces subcommand: ${sub ?? "(none)"}.`, `Subcomando de spaces desconocido: ${sub ?? "(ninguno)"}.`), ex);
|
|
12399
12453
|
}
|
|
12400
12454
|
async function workspaceUsage(slug, v) {
|
|
@@ -12784,9 +12838,10 @@ ${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
|
|
|
12784
12838
|
spaces ls Your workspaces (role, plan, combo) [--user EMAIL: someone else's]
|
|
12785
12839
|
spaces create <slug> New workspace owned by you (trial) [--combo teams|sales|both: staff] [--dry-run]
|
|
12786
12840
|
spaces join|leave <workspace> Enter or leave a client's workspace as staff (no seat; logged)
|
|
12787
|
-
spaces set <workspace> --combo sales teams | sales | both (without Sales its boards are invisible)
|
|
12841
|
+
spaces set <workspace> --combo sales teams | sales | both (without Sales its boards are invisible) [--model-cap 0.25|1|4|none: most expensive model] [--house-claude on|off]
|
|
12788
12842
|
spaces renew <workspace> --until DATE Move \xABpaid until\xBB (logged) [--note] [--dry-run]
|
|
12789
12843
|
spaces rename <old> <new> Change its slug only [--dry-run]
|
|
12844
|
+
spaces transfer <workspace> --to EMAIL New owner; agents go with it and their boxes are recycled [--keep-as admin|member|none] [--copy-keys] [--dry-run]
|
|
12790
12845
|
|
|
12791
12846
|
${bold("Agents")}
|
|
12792
12847
|
agents ls List your agents [--engine E] [--ids: only ids, for xargs] \xB7 --user EMAIL --app all|android|ios|desktop: as their app sees it (staff)
|
|
@@ -12913,9 +12968,10 @@ ${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
|
|
|
12913
12968
|
spaces ls Tus espacios (rol, plan, combo) [--user CORREO: los de alguien m\xE1s]
|
|
12914
12969
|
spaces create <slug> Espacio nuevo a tu nombre (trial) [--combo teams|sales|both: staff] [--dry-run]
|
|
12915
12970
|
spaces join|leave <espacio> Entra o sal del espacio de un cliente como staff (sin asiento; queda en bit\xE1cora)
|
|
12916
|
-
spaces set <espacio> --combo sales teams | sales | both (sin Ventas sus tableros no se ven)
|
|
12971
|
+
spaces set <espacio> --combo sales teams | sales | both (sin Ventas sus tableros no se ven) [--model-cap 0.25|1|4|none: el modelo m\xE1s caro] [--house-claude on|off]
|
|
12917
12972
|
spaces renew <espacio> --until FECHA Mueve \xABpagado hasta\xBB (queda en bit\xE1cora) [--note] [--dry-run]
|
|
12918
12973
|
spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
|
|
12974
|
+
spaces transfer <espacio> --to CORREO Due\xF1o nuevo; los agentes se van con \xE9l y se reciclan sus cajas [--keep-as admin|member|none] [--copy-keys] [--dry-run]
|
|
12919
12975
|
|
|
12920
12976
|
${bold("Agentes")}
|
|
12921
12977
|
agents ls Lista tus agentes [--engine E] [--ids: s\xF3lo ids, para xargs] \xB7 --user CORREO --app all|android|ios|desktop: como lo ve su app (staff)
|
|
@@ -13134,6 +13190,8 @@ async function main() {
|
|
|
13134
13190
|
boxes: { type: "boolean" },
|
|
13135
13191
|
like: { type: "string" },
|
|
13136
13192
|
"copy-dbs": { type: "boolean" },
|
|
13193
|
+
"copy-keys": { type: "boolean" },
|
|
13194
|
+
"keep-as": { type: "string" },
|
|
13137
13195
|
"no-notify": { type: "boolean" },
|
|
13138
13196
|
board: { type: "string" },
|
|
13139
13197
|
only: { type: "string" },
|
|
@@ -13219,6 +13277,8 @@ async function main() {
|
|
|
13219
13277
|
"only-ads": { type: "boolean" },
|
|
13220
13278
|
phones: { type: "string" },
|
|
13221
13279
|
combo: { type: "string" },
|
|
13280
|
+
"model-cap": { type: "string" },
|
|
13281
|
+
"house-claude": { type: "string" },
|
|
13222
13282
|
app: { type: "string" },
|
|
13223
13283
|
images: { type: "boolean" },
|
|
13224
13284
|
plan: { type: "string" },
|
|
@@ -13231,7 +13291,10 @@ async function main() {
|
|
|
13231
13291
|
timeline: { type: "boolean" },
|
|
13232
13292
|
new: { type: "boolean" },
|
|
13233
13293
|
"recycle-between": { type: "boolean" },
|
|
13234
|
-
"env-file": { type: "string" }
|
|
13294
|
+
"env-file": { type: "string" },
|
|
13295
|
+
// Node 22 se come `--env-file` aunque vaya después del script («node: ./x: not found»): el
|
|
13296
|
+
// nombre bueno es `--out-env`; `--env-file` queda como alias para quien lo corra con otro node.
|
|
13297
|
+
"out-env": { type: "string" }
|
|
13235
13298
|
}
|
|
13236
13299
|
});
|
|
13237
13300
|
globals.json = Boolean(v.json);
|
|
@@ -13239,7 +13302,7 @@ async function main() {
|
|
|
13239
13302
|
const tokens = splitTokens(pos, v);
|
|
13240
13303
|
globals.token = tokens.session;
|
|
13241
13304
|
globals.profile = v.profile;
|
|
13242
|
-
if (v.version) return out({ version: "0.
|
|
13305
|
+
if (v.version) return out({ version: "0.39.0" }, () => "0.39.0");
|
|
13243
13306
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
13244
13307
|
const context = (await readConfig()).workspace;
|
|
13245
13308
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -13388,7 +13451,7 @@ async function main() {
|
|
|
13388
13451
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
13389
13452
|
}
|
|
13390
13453
|
}
|
|
13391
|
-
var notice = updateNotice("0.
|
|
13454
|
+
var notice = updateNotice("0.39.0").catch(() => null);
|
|
13392
13455
|
main().then(
|
|
13393
13456
|
async () => {
|
|
13394
13457
|
const n = await notice;
|
package/package.json
CHANGED