@ghostystudio/cli 0.13.1 → 0.13.2

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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/dist/index.js +29 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -79,6 +79,11 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
79
79
  - `try --replay FILE --out DIR`: replays real conversations from your machine. The report is written after every step, survives Ghosty deploys (it honors `Retry-After`), and `--resume` continues a partial run.
80
80
  - `agents doctor` speaks English like the rest of the CLI; `channels whatsapp ls` returns a full URL.
81
81
 
82
+ ### Changes in 0.13.2
83
+
84
+ - `channels whatsapp ls` shows the real number, "records only" numbers, and whether Formmy forwards to gs. `whatsapp link` stores the number's name and phone.
85
+ - `agents set --channels wa=on,teams=off` (wa = personal WhatsApp).
86
+
82
87
  ### Changes in 0.13.1
83
88
 
84
89
  - `channels wa import --from-dir DIR`: import a Baileys multi-file auth folder (`creds.json` + one file per key, e.g. nanoclaw's `store/auth`); it undoes Baileys' file-name escaping per key type.
package/dist/index.js CHANGED
@@ -282,7 +282,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
282
282
  const req = httpsRequest(`${base}${path}`, {
283
283
  method: "POST",
284
284
  timeout: timeoutMs,
285
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.13.1"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
285
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.13.2"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
286
286
  }, (res) => {
287
287
  let buf = "";
288
288
  res.on("data", (c) => buf += c);
@@ -324,7 +324,7 @@ async function request(path, opts = {}, retried = false) {
324
324
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
325
325
  headers: {
326
326
  authorization: `Bearer ${token}`,
327
- "user-agent": `ghosty-cli/${"0.13.1"}`,
327
+ "user-agent": `ghosty-cli/${"0.13.2"}`,
328
328
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
329
329
  ...opts.headers
330
330
  },
@@ -548,6 +548,15 @@ async function agentsSet(id, v) {
548
548
  if (v.model) body.model = v.model;
549
549
  if (v.prompt) body.prompt = v.prompt;
550
550
  if (v["prompt-file"]) body.prompt = await readFile3(v["prompt-file"], "utf8");
551
+ if (v.channels) {
552
+ const ch = {};
553
+ for (const pair of v.channels.split(",").map((x) => x.trim()).filter(Boolean)) {
554
+ const [k, val] = pair.split("=");
555
+ if (!["teams", "web", "whatsapp", "messenger", "wa"].includes(k) || val !== "on" && val !== "off") throw usage(`Bad --channels entry: ${pair}.`, "Use teams|web|whatsapp|messenger|wa=on|off, comma-separated (wa = personal WhatsApp)");
556
+ ch[k] = val === "on";
557
+ }
558
+ body.channels = ch;
559
+ }
551
560
  const env = parseEnv(v.env);
552
561
  if (env) body.env = env;
553
562
  const connectors = {};
@@ -570,7 +579,7 @@ async function agentsSet(id, v) {
570
579
  if (v["auto-stage"] !== "on" && v["auto-stage"] !== "off") throw usage("--auto-stage takes on or off.");
571
580
  body.autoStage = v["auto-stage"] === "on";
572
581
  }
573
- if (!Object.keys(body).length) throw usage("Nothing to change.", "Use --name, --engine, --model, --prompt, --prompt-file, --env, --connect, --disconnect, --voice, --voice-replies or --auto-stage.");
582
+ if (!Object.keys(body).length) throw usage("Nothing to change.", "Use --name, --engine, --model, --prompt, --prompt-file, --env, --channels, --connect, --disconnect, --voice, --voice-replies or --auto-stage.");
574
583
  const r = await api(agentPath(id), { method: "PATCH", body });
575
584
  out(r, () => `${lavender("\u2713")} Updated ${Object.keys(body).join(", ")}.`);
576
585
  }
@@ -1136,7 +1145,7 @@ async function call(path, v, init = {}) {
1136
1145
  "x-ghosty-ts": ts,
1137
1146
  "x-ghosty-sig": sig,
1138
1147
  "content-type": "application/json",
1139
- "user-agent": `ghosty-cli/${"0.13.1"}`
1148
+ "user-agent": `ghosty-cli/${"0.13.2"}`
1140
1149
  },
1141
1150
  body: method === "GET" ? void 0 : raw
1142
1151
  });
@@ -1625,10 +1634,17 @@ async function channelsCmd(kind, sub, id, ref, v) {
1625
1634
  if (sub === "import" || sub === "status" || sub === "pause" || sub === "pair") return waCmd(sub, id, v);
1626
1635
  if (sub === "enable" && !v["dry-run"]) requireYesIfHeadless(v.yes, `ghosty channels whatsapp enable ${id} ${ref ?? "<number>"}`);
1627
1636
  const path = `/api/v2/agents/${enc7(id)}/whatsapp`;
1628
- const cur = await api(path);
1629
- if (!sub || sub === "ls" || sub === "list") {
1630
- const rows = cur.numbers.map((n2) => ({ number: n2.label, answers: n2.mine ? lavender("this agent") : n2.takenByOther ? "another agent" : dim("nobody (silent)"), id: n2.id }));
1631
- return out(cur, () => rows.length ? table(rows, ["number", "answers", "id"]) : dim(`No WhatsApp Business numbers yet. Pair one at ${cur.salesUrl}`));
1637
+ const listing = !sub || sub === "ls" || sub === "list";
1638
+ const cur = await api(listing ? `${path}?formmy=1` : path);
1639
+ if (listing) {
1640
+ const fwd = (n2) => !n2.formmy ? dim("\u2014") : "error" in n2.formmy ? dim("?") : n2.formmy.forwards ? "yes" : lavender("NO");
1641
+ const rows = cur.numbers.map((n2) => ({
1642
+ number: n2.phoneNumber ? `+${n2.phoneNumber}${n2.label !== n2.phoneNumber && n2.label !== n2.id ? ` ${n2.label}` : ""}` : n2.label,
1643
+ answers: n2.mine ? lavender("this agent") : n2.recordsOnly ? "this agent (records only)" : n2.takenByOther ? "another agent" : dim("nobody (silent)"),
1644
+ "formmy forwards": fwd(n2),
1645
+ id: n2.id
1646
+ }));
1647
+ return out(cur, () => rows.length ? table(rows, ["number", "answers", "formmy forwards", "id"]) : dim(`No WhatsApp Business numbers yet. Pair one at ${cur.salesUrl}`));
1632
1648
  }
1633
1649
  if (sub !== "enable" && sub !== "disable") throw usage(`Unknown subcommand: ${sub}.`, "Try: ghosty channels whatsapp ls | enable | disable | link | unlink \xB7 ghosty channels wa import | status | pause | groups");
1634
1650
  if (!ref) throw usage("Missing number.", `Example: ghosty channels whatsapp ${sub} ${id} +52 55 1234 5678`);
@@ -1791,6 +1807,7 @@ var HELPS = {
1791
1807
  ],
1792
1808
  flags: [
1793
1809
  ["--fields a,b", "get: return only these fields"],
1810
+ ["--channels k=on|off,\u2026", "set: turn channels on/off: teams, web, whatsapp, messenger, wa (personal WhatsApp)"],
1794
1811
  ["--voice E:ID", "set: voice for voice notes: elevenlabs:<voiceId> or kokoro:em_santa|em_alex|ef_dora"],
1795
1812
  ["--voice-replies M", "set: auto = answer with a voice note when the customer sends or asks for audio; off = always text"],
1796
1813
  ["--auto-stage on|off", "set: move board cards by itself after each customer turn (one extra model call per turn)"],
@@ -2108,6 +2125,7 @@ ${bold("Agents")}
2108
2125
  agents get <id> Show an agent's config [--fields name,model,prompt]
2109
2126
  agents set <id> Change identity or engine [--name] [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,\u2026]
2110
2127
  [--connect google-drive,\u2026] [--disconnect \u2026] give/remove access to your connectors
2128
+ [--channels wa=on,teams=off] turn channels on/off (wa = personal WhatsApp)
2111
2129
  [--voice elevenlabs:ID | kokoro:em_santa] [--voice-replies auto|off] [--auto-stage on|off]
2112
2130
  agents doctor <id> Check the config: overlapping skills, tools the prompt names, broken grants, failing turns
2113
2131
  agents docs ls|grant|revoke <id> [doc] Documents it reads and may send [--write] [--no-deliver]
@@ -2262,6 +2280,7 @@ async function main() {
2262
2280
  "own-number": { type: "string" },
2263
2281
  answer: { type: "string" },
2264
2282
  phone: { type: "string" },
2283
+ channels: { type: "string" },
2265
2284
  forward: { type: "boolean" },
2266
2285
  replace: { type: "boolean" },
2267
2286
  "no-connect": { type: "boolean" },
@@ -2274,7 +2293,7 @@ async function main() {
2274
2293
  const tokens = splitTokens(pos, v);
2275
2294
  globals.token = tokens.session;
2276
2295
  globals.profile = v.profile;
2277
- if (v.version) return out({ version: "0.13.1" }, () => "0.13.1");
2296
+ if (v.version) return out({ version: "0.13.2" }, () => "0.13.2");
2278
2297
  const [cmd, sub, a1, a2] = pos;
2279
2298
  if (cmd === "partner" && (v.help || !sub || sub === "help")) return out({ help: PARTNER_HELP }, () => PARTNER_HELP);
2280
2299
  const topic = cmd === "help" ? sub : v.help ? cmd : void 0;
@@ -2359,7 +2378,7 @@ async function main() {
2359
2378
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
2360
2379
  }
2361
2380
  }
2362
- var notice = updateNotice("0.13.1").catch(() => null);
2381
+ var notice = updateNotice("0.13.2").catch(() => null);
2363
2382
  main().then(
2364
2383
  async () => {
2365
2384
  const n = await notice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "Official Ghosty Studio CLI — configure, test and chat with your agents from the terminal. Built for humans and coding agents.",
5
5
  "type": "module",
6
6
  "bin": {