@ghostystudio/cli 0.33.1 → 0.34.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 +1 -0
- package/dist/index.js +30 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,6 +135,7 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
|
135
135
|
- `turns ls --session S`, and on Claude agents a «resumed» column: ✓ when the turn kept its SDK session, ⚠ from scratch (orphan|stale|replaced) when it lost its memory. `turns show` says the same, with the Claude CLI version.
|
|
136
136
|
- `partner credentials create|rotate --env-file PATH` (mode 600, nothing printed) or `--format env` (pipe to `fly secrets import`). The secret's env var is now `GHOSTY_PARTNER_KEY_SECRET` (`GHOSTY_PARTNER_SECRET` still works).
|
|
137
137
|
- `partner agent apply --prompt-file prompt.md`; `partner try --verbose` prints each tool's arguments and milliseconds.
|
|
138
|
+
- `partner agent get|apply --agent SLUG` and `partner try --agent SLUG`: several agents per partner (e.g. `nik-admin`, `nik-public`). `channels whatsapp link … --partner-tenant <externalId> --partner-agent <slug>`: a business's WhatsApp Business number answered by that partner agent.
|
|
138
139
|
- Empty `partner ls|credentials ls|tenants ls` say what to do next. `login --device --profile <client>` explains how to sign in a client's account (they approve the link).
|
|
139
140
|
|
|
140
141
|
### Changes in 0.27.1
|
package/dist/index.js
CHANGED
|
@@ -8367,7 +8367,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8367
8367
|
const req = httpsRequest(`${base}${path}`, {
|
|
8368
8368
|
method: "POST",
|
|
8369
8369
|
timeout: timeoutMs,
|
|
8370
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8370
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.34.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8371
8371
|
}, (res) => {
|
|
8372
8372
|
let buf = "";
|
|
8373
8373
|
res.on("data", (c) => buf += c);
|
|
@@ -8409,7 +8409,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8409
8409
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8410
8410
|
headers: {
|
|
8411
8411
|
authorization: `Bearer ${token}`,
|
|
8412
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8412
|
+
"user-agent": `ghosty-cli/${"0.34.0"}`,
|
|
8413
8413
|
"accept-language": lang(),
|
|
8414
8414
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8415
8415
|
...opts.headers
|
|
@@ -9495,9 +9495,9 @@ ${bold("Partner space")} ${dim("(you, signed in as the space owner)")}
|
|
|
9495
9495
|
partner credentials revoke <keyId> Revoke it (idempotent)
|
|
9496
9496
|
partner audit Who did what, when
|
|
9497
9497
|
|
|
9498
|
-
${bold("Shared
|
|
9499
|
-
partner agent get
|
|
9500
|
-
partner agent apply --file agent.json Create or update it (idempotent) [--prompt-file prompt.md]
|
|
9498
|
+
${bold("Shared agents (solo-MCP)")} ${dim("(--agent SLUG picks another agent of the partner: nik-admin, nik-public\u2026)")}
|
|
9499
|
+
partner agent get [--agent SLUG] The agent template and its version
|
|
9500
|
+
partner agent apply --file agent.json Create or update it (idempotent) [--prompt-file prompt.md] [--agent SLUG]
|
|
9501
9501
|
|
|
9502
9502
|
${bold("Tenants")} ${dim("(your customers, by externalId \u2014 config only, no box)")}
|
|
9503
9503
|
partner tenants ls
|
|
@@ -9506,7 +9506,7 @@ ${bold("Tenants")} ${dim("(your customers, by externalId \u2014 config only, no
|
|
|
9506
9506
|
partner tenants rm <externalId> Deletes its config AND its memory
|
|
9507
9507
|
|
|
9508
9508
|
${bold("Try it")}
|
|
9509
|
-
partner try <externalId> "message" One turn [--group END_USER_ID] [--turn-token SHORT_LIVED_TOKEN] [--verbose: tool args and ms]
|
|
9509
|
+
partner try <externalId> "message" One turn [--agent SLUG] [--group END_USER_ID] [--turn-token SHORT_LIVED_TOKEN] [--verbose: tool args and ms]
|
|
9510
9510
|
(the turn token goes to YOUR MCP as \${turn.token}; --token also works here)
|
|
9511
9511
|
|
|
9512
9512
|
${bold("Options")}
|
|
@@ -9527,9 +9527,9 @@ ${bold("Espacio de partner")} ${dim("(t\xFA, con sesi\xF3n como due\xF1a del es
|
|
|
9527
9527
|
partner credentials revoke <keyId> La revoca (idempotente)
|
|
9528
9528
|
partner audit Qui\xE9n hizo qu\xE9 y cu\xE1ndo
|
|
9529
9529
|
|
|
9530
|
-
${bold("
|
|
9531
|
-
partner agent get
|
|
9532
|
-
partner agent apply --file agent.json Lo crea o lo actualiza (idempotente) [--prompt-file prompt.md]
|
|
9530
|
+
${bold("Agentes compartidos (solo-MCP)")} ${dim("(--agent SLUG elige otro agente del partner: nik-admin, nik-public\u2026)")}
|
|
9531
|
+
partner agent get [--agent SLUG] La plantilla del agente y su versi\xF3n
|
|
9532
|
+
partner agent apply --file agent.json Lo crea o lo actualiza (idempotente) [--prompt-file prompt.md] [--agent SLUG]
|
|
9533
9533
|
|
|
9534
9534
|
${bold("Tenants")} ${dim("(tus clientes, por externalId \u2014 s\xF3lo config, sin caja)")}
|
|
9535
9535
|
partner tenants ls
|
|
@@ -9538,7 +9538,7 @@ ${bold("Tenants")} ${dim("(tus clientes, por externalId \u2014 s\xF3lo config,
|
|
|
9538
9538
|
partner tenants rm <externalId> Borra su config Y su memoria
|
|
9539
9539
|
|
|
9540
9540
|
${bold("Pru\xE9balo")}
|
|
9541
|
-
partner try <externalId> "mensaje" Un turno [--group END_USER_ID] [--turn-token TOKEN_DE_VIDA_CORTA] [--verbose: args y ms de cada tool]
|
|
9541
|
+
partner try <externalId> "mensaje" Un turno [--agent SLUG] [--group END_USER_ID] [--turn-token TOKEN_DE_VIDA_CORTA] [--verbose: args y ms de cada tool]
|
|
9542
9542
|
(el token del turno llega a TU MCP como \${turn.token}; --token tambi\xE9n sirve aqu\xED)
|
|
9543
9543
|
|
|
9544
9544
|
${bold("Opciones")}
|
|
@@ -9569,7 +9569,7 @@ async function call(path, v, init = {}) {
|
|
|
9569
9569
|
"x-ghosty-ts": ts,
|
|
9570
9570
|
"x-ghosty-sig": sig,
|
|
9571
9571
|
"content-type": "application/json",
|
|
9572
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9572
|
+
"user-agent": `ghosty-cli/${"0.34.0"}`
|
|
9573
9573
|
},
|
|
9574
9574
|
body: method === "GET" ? void 0 : raw
|
|
9575
9575
|
});
|
|
@@ -9637,11 +9637,11 @@ async function credentialsCmd(sub, id, v) {
|
|
|
9637
9637
|
}
|
|
9638
9638
|
async function agentCmd(sub, v) {
|
|
9639
9639
|
if (!sub || sub === "get") {
|
|
9640
|
-
const r = await json(
|
|
9640
|
+
const r = await json(`/api/v2/partner/agent${v.agent ? `?slug=${enc5(v.agent)}` : ""}`, v);
|
|
9641
9641
|
return out(r, () => {
|
|
9642
9642
|
const a = r.agent;
|
|
9643
9643
|
return [
|
|
9644
|
-
`${bold(a.name)} ${dim(`v${a.version} \xB7 solo-MCP \xB7 ${a.engine}/${a.model}`)}`,
|
|
9644
|
+
`${bold(a.name)} ${dim(`${a.slug ? `${a.slug} \xB7 ` : ""}v${a.version} \xB7 solo-MCP \xB7 ${a.engine}/${a.model} \xB7 ${a.id}`)}`,
|
|
9645
9645
|
`MCP: ${a.mcp.map((m) => `${m.name} (${m.url})`).join(", ") || t("none", "ninguno")}`,
|
|
9646
9646
|
dim(a.prompt.slice(0, 300))
|
|
9647
9647
|
].join("\n");
|
|
@@ -9654,6 +9654,7 @@ async function agentCmd(sub, v) {
|
|
|
9654
9654
|
body.prompt = await readFile5(v["prompt-file"], "utf8");
|
|
9655
9655
|
if (body.prompt.length > 6e4) throw usage(t(`The prompt has ${body.prompt.length} chars; the limit is 60,000.`, `El prompt tiene ${body.prompt.length} caracteres; el tope es 60,000.`));
|
|
9656
9656
|
}
|
|
9657
|
+
if (v.agent) body.slug = v.agent;
|
|
9657
9658
|
const r = await json("/api/v2/partner/agent", v, { method: "PUT", body });
|
|
9658
9659
|
return out(r, () => `${lavender("\u2713")} ${r.created ? t("Created", "Creado") : r.changed ? t("Updated", "Actualizado") : t("No changes", "Sin cambios")} \xB7 v${r.agent.version}`);
|
|
9659
9660
|
}
|
|
@@ -9688,7 +9689,7 @@ async function tryCmd(externalId, text, v) {
|
|
|
9688
9689
|
if (!externalId || !text) throw usage(t("Missing externalId or message.", "Falta el externalId o el mensaje."), t("Example", "Ejemplo") + ': ghosty partner try org_123 "\xBFqu\xE9 citas tengo hoy?"');
|
|
9689
9690
|
const res = await call("/api/v2/partner/turns", v, {
|
|
9690
9691
|
method: "POST",
|
|
9691
|
-
body: { tenant: { externalId }, groupId: v.group ?? "cli", text, ...v.token ? { token: v.token } : {} }
|
|
9692
|
+
body: { tenant: { externalId }, groupId: v.group ?? "cli", text, ...v.token ? { token: v.token } : {}, ...v.agent ? { agent: v.agent } : {} }
|
|
9692
9693
|
});
|
|
9693
9694
|
const dec = new TextDecoder();
|
|
9694
9695
|
let buf = "";
|
|
@@ -10109,7 +10110,9 @@ function skipReason(r) {
|
|
|
10109
10110
|
paused: ["conversation paused on the board", "conversaci\xF3n pausada en el tablero"],
|
|
10110
10111
|
reaction: ["only a reaction", "s\xF3lo una reacci\xF3n"],
|
|
10111
10112
|
channel_off: ["WhatsApp channel off for this agent", "canal de WhatsApp apagado en este agente"],
|
|
10112
|
-
no_quota: ["out of balance", "sin saldo"]
|
|
10113
|
+
no_quota: ["out of balance", "sin saldo"],
|
|
10114
|
+
partner_token: ["partner did not issue a turn token", "el partner no dio token de turno"],
|
|
10115
|
+
partner_busy: ["partner at its concurrent-turn limit", "el partner lleg\xF3 a su tope de turnos a la vez"]
|
|
10113
10116
|
};
|
|
10114
10117
|
const pair = es[r];
|
|
10115
10118
|
return pair ? t(pair[0], pair[1]) : r;
|
|
@@ -10561,8 +10564,11 @@ ${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integrati
|
|
|
10561
10564
|
}
|
|
10562
10565
|
if (sub === "link" && v.answer !== "on" && v.answer !== "off") throw usage(t("Missing --answer on|off.", "Falta --answer on|off."), t("on = this agent answers real customers \xB7 off = it only records (add --forward so gs still sees the conversations)", "on = este agente contesta a clientes reales \xB7 off = s\xF3lo registra (agrega --forward para que gs siga viendo las conversaciones)"));
|
|
10563
10566
|
const path = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
|
|
10564
|
-
|
|
10565
|
-
const
|
|
10567
|
+
if (v["partner-agent"] && !v["partner-tenant"]) throw usage(t("--partner-agent needs --partner-tenant <externalId>.", "--partner-agent necesita --partner-tenant <externalId>."));
|
|
10568
|
+
const partner = v["partner-tenant"] ? { partnerExternalId: v["partner-tenant"], ...v["partner-agent"] ? { partnerAgentSlug: v["partner-agent"] } : {} } : {};
|
|
10569
|
+
const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {}, ...adsOnly !== void 0 ? { adsOnly } : {}, ...partner } };
|
|
10570
|
+
const partnerFlags = v["partner-tenant"] ? ` --partner-tenant ${v["partner-tenant"]}${v["partner-agent"] ? ` --partner-agent ${v["partner-agent"]}` : ""}` : "";
|
|
10571
|
+
const cmd = `ghosty channels whatsapp ${sub} ${id} --integration ${v.integration}${sub === "link" ? ` --answer ${v.answer}${v.forward ? " --forward" : ""}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${partnerFlags}` : ""}`;
|
|
10566
10572
|
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
10567
10573
|
const text = `${plan.plan.map((x) => serverText(x)).join("\n")}
|
|
10568
10574
|
${dim(`Formmy: ${plan.formmy.before.url ?? "null"} \u2192 ${plan.formmy.after.url ?? "null"} (${t("as", "como")} ${plan.formmy.by})`)}`;
|
|
@@ -10992,6 +10998,7 @@ var HELPS_ES = {
|
|
|
10992
10998
|
["--media", "history: s\xF3lo re-copia los archivos que faltan (fotos, PDF, audios) de las conversaciones ya importadas"],
|
|
10993
10999
|
["--answer on|off", "link (obligatorio, sin default): on = este agente contesta a clientes reales; off = s\xF3lo registra"],
|
|
10994
11000
|
["--forward", "link --answer off: igual hace que Formmy reenv\xEDe, para que gs vea las conversaciones"],
|
|
11001
|
+
["--partner-tenant ID / --partner-agent SLUG", "link: el n\xFAmero es de un negocio del partner (externalId); <id> es el agente de ese partner (ghosty partner agent get --agent SLUG) y contesta solo-MCP con un token de turno que gs le pide al partner"],
|
|
10995
11002
|
["--from easybits:ID", "import: directo del agente de EasyBits ($EASYBITS_API_KEY del due\xF1o); primero dice si su sesi\xF3n est\xE1 viva, sus llaves y grupos; las llaves s\xF3lo viven en memoria"],
|
|
10996
11003
|
["--from-file F", "import: {authCreds, authKeys, phone, groups} exportado de EasyBits"],
|
|
10997
11004
|
["--from-dir DIR", "import: una carpeta de auth multi-archivo de Baileys (creds.json + un archivo por llave; p. ej. store/auth de nanoclaw)"],
|
|
@@ -11499,6 +11506,7 @@ HELPS.channels = {
|
|
|
11499
11506
|
["--media", "history: only re-copy the missing files (photos, PDFs, audio) of conversations already imported"],
|
|
11500
11507
|
["--answer on|off", "link (required, no default): on = this agent answers real customers; off = it only records"],
|
|
11501
11508
|
["--forward", "link --answer off: still make Formmy forward, so gs sees the conversations"],
|
|
11509
|
+
["--partner-tenant ID / --partner-agent SLUG", "link: the number belongs to a partner's business (externalId); <id> is that partner's agent (ghosty partner agent get --agent SLUG) and it answers solo-MCP with a turn token gs asks the partner for"],
|
|
11502
11510
|
["--from easybits:ID", "import: straight from the EasyBits agent (owner's $EASYBITS_API_KEY); first prints whether its session is alive, its keys and groups; the keys stay in memory"],
|
|
11503
11511
|
["--from-file F", "import: {authCreds, authKeys, phone, groups} exported from EasyBits"],
|
|
11504
11512
|
["--from-dir DIR", "import: a Baileys multi-file auth folder (creds.json + one file per key; e.g. nanoclaw's store/auth)"],
|
|
@@ -13082,6 +13090,8 @@ async function main() {
|
|
|
13082
13090
|
publish: { type: "boolean" },
|
|
13083
13091
|
draft: { type: "boolean" },
|
|
13084
13092
|
"ads-only": { type: "string" },
|
|
13093
|
+
"partner-tenant": { type: "string" },
|
|
13094
|
+
"partner-agent": { type: "string" },
|
|
13085
13095
|
list: { type: "boolean" },
|
|
13086
13096
|
"only-ads": { type: "boolean" },
|
|
13087
13097
|
phones: { type: "string" },
|
|
@@ -13106,13 +13116,14 @@ async function main() {
|
|
|
13106
13116
|
const tokens = splitTokens(pos, v);
|
|
13107
13117
|
globals.token = tokens.session;
|
|
13108
13118
|
globals.profile = v.profile;
|
|
13109
|
-
if (v.version) return out({ version: "0.
|
|
13119
|
+
if (v.version) return out({ version: "0.34.0" }, () => "0.34.0");
|
|
13110
13120
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
13111
13121
|
const context = (await readConfig()).workspace;
|
|
13112
13122
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
13113
13123
|
const at = agentArgIndex(pos);
|
|
13114
13124
|
if (at !== null && pos[at]) pos[at] = await resolveAgent(pos[at], context);
|
|
13115
13125
|
for (const f of ["like", "prompt-base", "agent"]) {
|
|
13126
|
+
if (f === "agent" && pos[0] === "partner") continue;
|
|
13116
13127
|
const val = v[f];
|
|
13117
13128
|
if (typeof val === "string" && val !== "none") v[f] = await resolveAgent(val, context);
|
|
13118
13129
|
}
|
|
@@ -13254,7 +13265,7 @@ async function main() {
|
|
|
13254
13265
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
13255
13266
|
}
|
|
13256
13267
|
}
|
|
13257
|
-
var notice = updateNotice("0.
|
|
13268
|
+
var notice = updateNotice("0.34.0").catch(() => null);
|
|
13258
13269
|
main().then(
|
|
13259
13270
|
async () => {
|
|
13260
13271
|
const n = await notice;
|
package/package.json
CHANGED