@ghostystudio/cli 0.16.1 → 0.18.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 +17 -6
- package/dist/index.js +187 -40
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -24,7 +24,8 @@ ghosty agents share|unshare <id> <email> [--role editor|admin] # share without
|
|
|
24
24
|
ghosty agents rm <id> [--yes]
|
|
25
25
|
ghosty agents restart <id>
|
|
26
26
|
ghosty agents box <id> [--check PATH]… [--contains PATH=TEXT]… [--recycle] # is its machine on the new image?
|
|
27
|
-
ghosty agents demo <id>
|
|
27
|
+
ghosty agents demo enable|disable <id>
|
|
28
|
+
ghosty agents demo <id> [--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N]
|
|
28
29
|
[--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate] # public /p/ link, no account needed
|
|
29
30
|
ghosty agents doctor <id> [--verbose] # exits 1 when it finds a problem
|
|
30
31
|
ghosty agents docs ls|grant|revoke <id> [doc] [--write] [--no-deliver]
|
|
@@ -33,7 +34,7 @@ ghosty agents set <id> --voice elevenlabs:ID --voice-replies auto --auto-stage o
|
|
|
33
34
|
|
|
34
35
|
ghosty files ls|get|put|rm <id> [path] [--file LOCAL] [--out LOCAL]
|
|
35
36
|
ghosty skills ls|get|add|rm <id> [slug] [--file SKILL.md | --dir FOLDER] [--out DIR] # get without --out only lists
|
|
36
|
-
ghosty skills
|
|
37
|
+
ghosty skills enable|disable <id> <slug> # turn one of its skills (or one of Ghosty's) on or off
|
|
37
38
|
ghosty skills templates <id> # pdf-doc templates: its own and Ghosty's, and which folder wins
|
|
38
39
|
ghosty dbs ls|tables|query|import|grant|revoke <id> [db] [--write] [--arg V]… [--external-write t1,t2] [--external-rows t:col+col] [--from easybits:ID --tables a,b --append --dry-run]
|
|
39
40
|
ghosty turns ls <id> [--errors | --status running|done|stopped|error] [--since 1h] [--limit N]
|
|
@@ -43,11 +44,11 @@ ghosty channels whatsapp link|unlink <id> --integration ID --answer on|off [--
|
|
|
43
44
|
ghosty channels whatsapp history <id> --integration ID [--days 7] [--dry-run] # its recent cards, messages and orders (no duplicates)
|
|
44
45
|
ghosty channels wa import <id> --from-file F | --from-dir DIR --own-number yes|no [--dry-run] # personal WhatsApp session, no QR
|
|
45
46
|
ghosty channels wa pair|status|pause <id> [--phone N]
|
|
46
|
-
ghosty channels wa groups ls|set|create <id> [group]
|
|
47
|
+
ghosty channels wa groups ls|enable|disable|set|create <id> [group] [--role equipo|cliente] [--dry-run] # personal WhatsApp groups
|
|
47
48
|
ghosty boards ls|create|assign|move-cards|rm <workspace|agent> … [--columns-from main] [--from A --to B --integration X --dry-run]
|
|
48
|
-
ghosty
|
|
49
|
-
ghosty
|
|
50
|
-
ghosty
|
|
49
|
+
ghosty cards ls <id> [--q TEXT] [--limit 20] # recent cards on its sales board
|
|
50
|
+
ghosty cards show <id> <folio|phone|id> [--limit 20] # a card's thread, attachments and whether gs kept a copy
|
|
51
|
+
ghosty cards archive <id> --integration X | --column X | --before 30d [--dry-run]
|
|
51
52
|
ghosty mcp get|set <id> [--file servers.json]
|
|
52
53
|
|
|
53
54
|
ghosty try <id> "message" [--session] [--reset]
|
|
@@ -87,6 +88,16 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
|
87
88
|
- `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.
|
|
88
89
|
- `agents doctor` speaks English like the rest of the CLI; `channels whatsapp ls` returns a full URL.
|
|
89
90
|
|
|
91
|
+
### Changes in 0.18.0
|
|
92
|
+
|
|
93
|
+
- One naming rule (like `gh`): noun then verb; turning things on/off is `enable|disable`; `on|off` only as a flag value. `ghosty cards` (an agent's board cards; `board` still works), `skills enable|disable`, `agents demo enable|disable <agent>`, `channels wa groups enable|disable <agent> <group>`, `--expires` (was `--vence`). Old forms still work and print the new one.
|
|
94
|
+
- Help for `login`, `logout`, `whoami`, `docs`, `use` and `cards`.
|
|
95
|
+
|
|
96
|
+
### Changes in 0.17.0
|
|
97
|
+
|
|
98
|
+
- Agents by **name** everywhere: `ghosty agents get pia-0`, `ghosty try "Ghosty Ventas" "hola"` (ids still work). Same-name agents: the one in the `ghosty use` workspace wins, otherwise the CLI lists their ids.
|
|
99
|
+
- `ghosty use <workspace>`: default workspace for `boards`, `agents create` and `agents ls` (`--all`, `--workspace none`, `use --clear`).
|
|
100
|
+
|
|
90
101
|
### Changes in 0.16.1
|
|
91
102
|
|
|
92
103
|
- `agents create --like`: `--board same|main|new|NAME` (`new` = its own board with the source's columns, one per number) and `--no-notify` (shared with the same people, no email).
|
package/dist/index.js
CHANGED
|
@@ -288,7 +288,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
288
288
|
const req = httpsRequest(`${base}${path}`, {
|
|
289
289
|
method: "POST",
|
|
290
290
|
timeout: timeoutMs,
|
|
291
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
291
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.18.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
292
292
|
}, (res) => {
|
|
293
293
|
let buf = "";
|
|
294
294
|
res.on("data", (c) => buf += c);
|
|
@@ -330,7 +330,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
330
330
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
331
331
|
headers: {
|
|
332
332
|
authorization: `Bearer ${token}`,
|
|
333
|
-
"user-agent": `ghosty-cli/${"0.
|
|
333
|
+
"user-agent": `ghosty-cli/${"0.18.0"}`,
|
|
334
334
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
335
335
|
...opts.headers
|
|
336
336
|
},
|
|
@@ -529,17 +529,21 @@ var need = (v, what, example) => {
|
|
|
529
529
|
if (!v) throw usage(`Missing ${what}.`, `Example: ${example}`);
|
|
530
530
|
return v;
|
|
531
531
|
};
|
|
532
|
-
async function agentsLs() {
|
|
532
|
+
async function agentsLs(workspace) {
|
|
533
533
|
const r = await api("/api/v2/me/agents?client=cli");
|
|
534
|
-
const
|
|
534
|
+
const list = workspace ? r.agentes.filter((a) => a.espacio?.nombre === workspace) : r.agentes;
|
|
535
|
+
const spaceOf = (a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "";
|
|
536
|
+
const rows = list.map((a) => ({
|
|
535
537
|
id: a.id,
|
|
536
538
|
name: a.nombre,
|
|
537
539
|
engine: a.motor ?? a.origen,
|
|
538
540
|
source: a.origen,
|
|
541
|
+
workspace: spaceOf(a),
|
|
539
542
|
lastActivity: a.ultimaActividad ?? null,
|
|
540
543
|
...a.compartidoPor ? { sharedBy: a.compartidoPor } : {}
|
|
541
544
|
}));
|
|
542
|
-
out(rows, () => table(rows, ["id", "name", "engine", "lastActivity"])
|
|
545
|
+
out(rows, () => `${table(rows, ["id", "name", "engine", "workspace", "lastActivity"])}${workspace ? `
|
|
546
|
+
${dim(`only ${workspace} (ghosty use) \xB7 all: --all`)}` : ""}`);
|
|
543
547
|
}
|
|
544
548
|
function parseEnv(raw) {
|
|
545
549
|
if (!raw) return void 0;
|
|
@@ -1269,7 +1273,7 @@ async function call(path, v, init = {}) {
|
|
|
1269
1273
|
"x-ghosty-ts": ts,
|
|
1270
1274
|
"x-ghosty-sig": sig,
|
|
1271
1275
|
"content-type": "application/json",
|
|
1272
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1276
|
+
"user-agent": `ghosty-cli/${"0.18.0"}`
|
|
1273
1277
|
},
|
|
1274
1278
|
body: method === "GET" ? void 0 : raw
|
|
1275
1279
|
});
|
|
@@ -2002,7 +2006,11 @@ Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a new mes
|
|
|
2002
2006
|
|
|
2003
2007
|
// src/commands/boards.ts
|
|
2004
2008
|
var enc8 = encodeURIComponent;
|
|
2005
|
-
async function boardsCmd(sub, a1, a2, v) {
|
|
2009
|
+
async function boardsCmd(sub, a1, a2, v, context) {
|
|
2010
|
+
if (context && sub !== "assign") {
|
|
2011
|
+
if ((sub === "ls" || sub === "list" || !sub || sub === "move-cards") && !a1) a1 = context;
|
|
2012
|
+
else if ((sub === "create" || sub === "rm") && a1 && !a2) [a1, a2] = [context, a1];
|
|
2013
|
+
}
|
|
2006
2014
|
switch (sub ?? "ls") {
|
|
2007
2015
|
case "ls":
|
|
2008
2016
|
case "list": {
|
|
@@ -2071,7 +2079,8 @@ var HELPS = {
|
|
|
2071
2079
|
"ghosty agents shares <agent-id>",
|
|
2072
2080
|
"ghosty agents restart <agent-id>",
|
|
2073
2081
|
"ghosty agents box <agent-id> [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]",
|
|
2074
|
-
|
|
2082
|
+
"ghosty agents demo enable|disable <agent-id>",
|
|
2083
|
+
'ghosty agents demo <agent-id> [--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate]',
|
|
2075
2084
|
"ghosty agents rm <agent-id> [--yes]"
|
|
2076
2085
|
],
|
|
2077
2086
|
flags: [
|
|
@@ -2097,8 +2106,7 @@ var HELPS = {
|
|
|
2097
2106
|
["--role R", "share: editor (configures and uses it) or admin (also shares it); again on a shared email changes the role"],
|
|
2098
2107
|
["--yes", "rm: skip the confirmation (required without a terminal)"],
|
|
2099
2108
|
["--slug NAME", "demo: link name; a 6-char code is appended (/p/NAME-CODE)"],
|
|
2100
|
-
["--
|
|
2101
|
-
["--vence DATE|no", `demo: fixed expiry in your machine's time ("2026-10-15T20:00"); no = never`],
|
|
2109
|
+
["--expires DATE|no", `demo: fixed expiry in your machine's time ("2026-10-15T20:00"); no = never`],
|
|
2102
2110
|
["--turns N", "demo: messages per visitor (default 150)"],
|
|
2103
2111
|
["--per-ip N", "demo: messages per IP per day (default 30)"],
|
|
2104
2112
|
["--welcome TEXT", "demo: chat greeting (empty = default)"],
|
|
@@ -2125,7 +2133,8 @@ var HELPS = {
|
|
|
2125
2133
|
"ghosty agents create --name tania-mkt --like <pia-id> --board new --no-notify --dry-run",
|
|
2126
2134
|
'ghosty agents docs grant <id> "Cat\xE1logo.pdf" "Precios.xlsx" \xB7 ghosty agents docs grant <id> --like <other-id>',
|
|
2127
2135
|
"ghosty agents set <pia-id> --prompt-base <tania-id> --prompt-overlay capa.json --dry-run --out /tmp/pia.md",
|
|
2128
|
-
|
|
2136
|
+
"ghosty agents demo enable <id>",
|
|
2137
|
+
'ghosty agents demo <id> --slug guardias --expires "2026-10-15T20:00" --starter "Ens\xE9\xF1ame c\xF3mo empezar"',
|
|
2129
2138
|
"ghosty agents box <id> --contains /opt/claude-worker/skills/pdf-doc/render-doc.mjs=pdf-templates"
|
|
2130
2139
|
],
|
|
2131
2140
|
notes: ["doctor exits 1 when it finds a problem, so it can gate a script or CI.", "A derived prompt can't be edited directly (409): change the base or the overlay, or unlink it with --prompt-base none.", "demo with no flags only prints its status: link, expiry, caps and numbers."],
|
|
@@ -2135,7 +2144,7 @@ var HELPS = {
|
|
|
2135
2144
|
summary: "Skills of an agent (agentskills.io format: a folder with SKILL.md).",
|
|
2136
2145
|
usage: [
|
|
2137
2146
|
"ghosty skills ls <agent-id>",
|
|
2138
|
-
"ghosty skills
|
|
2147
|
+
"ghosty skills enable|disable <agent-id> <slug>",
|
|
2139
2148
|
"ghosty skills house <agent-id> [--only a,b | --enable a,b | --disable a,b] [--dry-run]",
|
|
2140
2149
|
"ghosty skills get <agent-id> <slug> [--out DIR]",
|
|
2141
2150
|
"ghosty skills templates <agent-id>",
|
|
@@ -2300,14 +2309,14 @@ HELPS.channels = {
|
|
|
2300
2309
|
"ghosty channels wa pair <agent-id> --phone 5215512345678",
|
|
2301
2310
|
"ghosty channels wa status|pause <agent-id>",
|
|
2302
2311
|
"ghosty channels wa groups ls <agent-id> [--cached]",
|
|
2303
|
-
"ghosty channels wa groups
|
|
2312
|
+
"ghosty channels wa groups enable|disable <agent-id> <group> [--role equipo|cliente] [--dry-run] [--yes]",
|
|
2313
|
+
"ghosty channels wa groups set <agent-id> <group> --role equipo|cliente [--dry-run]",
|
|
2304
2314
|
'ghosty channels wa groups create <agent-id> "Name" [--role equipo|cliente]'
|
|
2305
2315
|
],
|
|
2306
2316
|
flags: [
|
|
2307
2317
|
["-n, --dry-run", "print the plan (+ starts answering, - stops) and change nothing"],
|
|
2308
|
-
["--yes", "enable / groups
|
|
2318
|
+
["--yes", "enable / groups enable: skip the confirmation (required without a terminal)"],
|
|
2309
2319
|
["--role R", "groups: equipo (the team: writes to databases, admin tools) or cliente (like any customer)"],
|
|
2310
|
-
["--on | --off", "groups set: the agent answers in that group, or stops"],
|
|
2311
2320
|
["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
|
|
2312
2321
|
["--integration ID", "link/unlink/history: the WhatsApp Business number's Integration id in Formmy"],
|
|
2313
2322
|
["--days N", "history: conversations active in the last N days (1-30, default 7)"],
|
|
@@ -2324,7 +2333,7 @@ HELPS.channels = {
|
|
|
2324
2333
|
"ghosty channels whatsapp enable <id> 5512345678 --dry-run",
|
|
2325
2334
|
"ghosty channels whatsapp disable <id> 5512345678",
|
|
2326
2335
|
"ghosty channels wa groups ls <id>",
|
|
2327
|
-
'ghosty channels wa groups
|
|
2336
|
+
'ghosty channels wa groups enable <id> "Pia-cotizadora" --role equipo --dry-run',
|
|
2328
2337
|
"ghosty channels whatsapp link <id> --integration 6a3af395\u2026 --answer off --dry-run",
|
|
2329
2338
|
"ghosty channels whatsapp history <id> --integration 6a3af395\u2026 --days 7 --dry-run",
|
|
2330
2339
|
"ghosty channels wa import <id> --from-file pia-wa.json --own-number no --dry-run"
|
|
@@ -2339,6 +2348,24 @@ HELPS.channels = {
|
|
|
2339
2348
|
],
|
|
2340
2349
|
learn: `${REF}#canales-de-whatsapp`
|
|
2341
2350
|
};
|
|
2351
|
+
HELPS.login = {
|
|
2352
|
+
summary: "Sign in with your browser (OAuth2, no password here). An agent can run it for you: it prints the URL and waits.",
|
|
2353
|
+
usage: ["ghosty login", "ghosty login --device"],
|
|
2354
|
+
flags: [["--device", "sign in from another device with a link + code (automatic over SSH or CI)"], ["--profile NAME", "a separate saved session, e.g. one per account"]],
|
|
2355
|
+
examples: ["ghosty login", "ghosty login --profile cliente", "GHOSTY_TOKEN=gat_\u2026 ghosty agents get <id> # no login: an agent token"],
|
|
2356
|
+
learn: `${REF}#sesion`
|
|
2357
|
+
};
|
|
2358
|
+
HELPS.logout = { summary: "Sign out and revoke this device's session on the server.", usage: ["ghosty logout [--profile NAME]"], examples: ["ghosty logout"], learn: `${REF}#sesion` };
|
|
2359
|
+
HELPS.whoami = { summary: "Who you are signed in as (with --json, also your account's plan).", usage: ["ghosty whoami [--json]"], examples: ["ghosty whoami"], learn: `${REF}#sesion` };
|
|
2360
|
+
HELPS.docs = { summary: "Open the docs in your browser (with --json, just print the URL).", usage: ["ghosty docs [topic]"], examples: ["ghosty docs", "ghosty docs cli"], learn: REF };
|
|
2361
|
+
HELPS.use = {
|
|
2362
|
+
summary: "A default workspace for this session (profile): boards take it without typing it, agents create is born there and agents ls lists only its agents.",
|
|
2363
|
+
usage: ["ghosty use # which one", "ghosty use <workspace>", "ghosty use --clear"],
|
|
2364
|
+
flags: [["--clear", "no default workspace"]],
|
|
2365
|
+
examples: ["ghosty use siiqtec", "ghosty boards ls", "ghosty agents create --name tania-mkt --like pia-0 --board new", "ghosty agents ls --all"],
|
|
2366
|
+
notes: ["Agents can be named instead of using their id anywhere; with several of the same name, the one in this workspace wins.", "agents create --workspace none makes a personal agent even with a default workspace."],
|
|
2367
|
+
learn: `${REF}#sesion`
|
|
2368
|
+
};
|
|
2342
2369
|
HELPS.boards = {
|
|
2343
2370
|
summary: "A WORKSPACE's sales boards: list them, create one (copying another's columns), choose where an agent sends its cards, and move cards between boards.",
|
|
2344
2371
|
usage: [
|
|
@@ -2359,12 +2386,12 @@ HELPS.boards = {
|
|
|
2359
2386
|
notes: ["assign changes where NEW cards go; move-cards brings the existing ones (thread, orders, activity) with a new folio. A card whose conversation is already on the target board is skipped.", "Reading needs membership; changing needs owner or staff of the workspace.", "rm only deletes an empty board that is not the main one."],
|
|
2360
2387
|
learn: `${REF}#tablero`
|
|
2361
2388
|
};
|
|
2362
|
-
HELPS.
|
|
2363
|
-
summary: "The agent's sales board
|
|
2389
|
+
HELPS.cards = {
|
|
2390
|
+
summary: "The cards on an agent's sales board (its board belongs to the WORKSPACE: you must be a member, a shared agent is not enough). The workspace's boards themselves: ghosty boards.",
|
|
2364
2391
|
usage: [
|
|
2365
|
-
"ghosty
|
|
2366
|
-
"ghosty
|
|
2367
|
-
"ghosty
|
|
2392
|
+
"ghosty cards ls <agent-id> [--q TEXT] [--limit 20]",
|
|
2393
|
+
"ghosty cards show <agent-id> <folio|phone|session|id> [--limit 20]",
|
|
2394
|
+
"ghosty cards archive <agent-id> [--integration ID] [--column NAME] [--before 30d|ISO] [--dry-run] [--yes]"
|
|
2368
2395
|
],
|
|
2369
2396
|
flags: [
|
|
2370
2397
|
["--q TEXT", "ls: cards whose name, phone or last message contains TEXT"],
|
|
@@ -2375,10 +2402,11 @@ HELPS.board = {
|
|
|
2375
2402
|
["-n, --dry-run", "count and show a sample, change nothing"],
|
|
2376
2403
|
["--yes", "skip the confirmation (required without a terminal)"]
|
|
2377
2404
|
],
|
|
2378
|
-
examples: ["ghosty
|
|
2405
|
+
examples: ["ghosty cards ls <id> --q 7712", 'ghosty cards show <id> "#12"', "ghosty cards show <id> 5210000009922 --limit 50", "ghosty cards archive <id> --integration prueba --dry-run", "ghosty cards archive <id> --column Perdido --before 30d --yes"],
|
|
2379
2406
|
notes: ["show prints each message with who wrote it (customer, agent, operator), its attachment and whether gs keeps a copy of it.", "Archiving is reversible: when the customer writes again the card comes back. At least one filter is required."],
|
|
2380
2407
|
learn: `${REF}#tablero`
|
|
2381
2408
|
};
|
|
2409
|
+
HELPS.board = HELPS.cards;
|
|
2382
2410
|
HELPS.conversations = HELPS.chat;
|
|
2383
2411
|
HELPS.usage = { ...HELPS.me, summary: "Your plan and how much you've used this week and month.", usage: ["ghosty usage", "ghosty me usage"], examples: ["ghosty usage", "ghosty usage --json"], learn: `${REF}#uso` };
|
|
2384
2412
|
function helpText(cmd) {
|
|
@@ -2414,28 +2442,84 @@ function newer(a, b) {
|
|
|
2414
2442
|
}
|
|
2415
2443
|
async function latestVersion() {
|
|
2416
2444
|
if (process.env.GHOSTY_NO_UPDATE_CHECK || process.env.CI) return null;
|
|
2417
|
-
let
|
|
2445
|
+
let cache2 = {};
|
|
2418
2446
|
try {
|
|
2419
|
-
|
|
2447
|
+
cache2 = JSON.parse(await readFile9(FILE, "utf8"));
|
|
2420
2448
|
} catch {
|
|
2421
2449
|
}
|
|
2422
|
-
if (!
|
|
2450
|
+
if (!cache2.checkedAt || Date.now() - cache2.checkedAt > DAY) {
|
|
2423
2451
|
try {
|
|
2424
2452
|
const r = await fetch("https://registry.npmjs.org/@ghostystudio/cli/latest", { signal: AbortSignal.timeout(1500) });
|
|
2425
2453
|
const latest = (await r.json()).version;
|
|
2426
|
-
if (latest)
|
|
2454
|
+
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
2427
2455
|
await mkdir4(dirname3(FILE), { recursive: true });
|
|
2428
|
-
await writeFile4(FILE, JSON.stringify(
|
|
2456
|
+
await writeFile4(FILE, JSON.stringify(cache2));
|
|
2429
2457
|
} catch {
|
|
2430
2458
|
}
|
|
2431
2459
|
}
|
|
2432
|
-
return
|
|
2460
|
+
return cache2.latest ?? null;
|
|
2433
2461
|
}
|
|
2434
2462
|
async function updateNotice(current) {
|
|
2435
2463
|
const latest = await latestVersion();
|
|
2436
2464
|
return latest && newer(latest, current) ? `Update available: ${current} \u2192 ${latest}. Run: ${UPGRADE}` : null;
|
|
2437
2465
|
}
|
|
2438
2466
|
|
|
2467
|
+
// src/resolve.ts
|
|
2468
|
+
var looksLikeId = (s) => /^c[a-z0-9]{20,}$/.test(s) || /^[0-9a-f]{24}$/.test(s);
|
|
2469
|
+
function pickAgent(list, ref, workspace) {
|
|
2470
|
+
const want = ref.trim().toLowerCase();
|
|
2471
|
+
let hits = list.filter((a) => a.nombre.trim().toLowerCase() === want);
|
|
2472
|
+
if (hits.length > 1 && workspace) {
|
|
2473
|
+
const here = hits.filter((a) => a.espacio?.nombre === workspace);
|
|
2474
|
+
if (here.length) hits = here;
|
|
2475
|
+
}
|
|
2476
|
+
if (hits.length === 1) return hits[0].id;
|
|
2477
|
+
if (!hits.length) {
|
|
2478
|
+
const near = list.filter((a) => a.nombre.toLowerCase().includes(want)).slice(0, 5);
|
|
2479
|
+
throw new CliError(`No agent named "${ref}".`, EXIT.USAGE, near.length ? `Did you mean: ${near.map((a) => `${a.nombre} (${a.id})`).join(", ")}?` : "List them: ghosty agents ls");
|
|
2480
|
+
}
|
|
2481
|
+
const where = (a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "?";
|
|
2482
|
+
throw new CliError(`${hits.length} agents are named "${ref}".`, EXIT.USAGE, `Use the id:
|
|
2483
|
+
${hits.map((a) => ` ${a.id} ${a.motor ?? ""} ${where(a)}`).join("\n")}`);
|
|
2484
|
+
}
|
|
2485
|
+
var cache = null;
|
|
2486
|
+
function listAgents() {
|
|
2487
|
+
cache ??= api("/api/v2/me/agents?client=cli").then((r) => r.agentes);
|
|
2488
|
+
return cache;
|
|
2489
|
+
}
|
|
2490
|
+
async function resolveAgent(ref, workspace) {
|
|
2491
|
+
if (!ref || looksLikeId(ref)) return ref;
|
|
2492
|
+
return pickAgent(await listAgents(), ref, workspace);
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
// src/aliases.ts
|
|
2496
|
+
function normalizeArgs(pos, v) {
|
|
2497
|
+
const notes = [];
|
|
2498
|
+
const [cmd, sub] = pos;
|
|
2499
|
+
if (cmd === "cards") pos[0] = "board";
|
|
2500
|
+
else if (cmd === "board") notes.push("`ghosty board` is now `ghosty cards` (same subcommands).");
|
|
2501
|
+
if (cmd === "skills") {
|
|
2502
|
+
if (sub === "enable") pos[1] = "on";
|
|
2503
|
+
else if (sub === "disable") pos[1] = "off";
|
|
2504
|
+
else if (sub === "on" || sub === "off") notes.push(`\`skills ${sub}\` is now \`skills ${sub === "on" ? "enable" : "disable"}\`.`);
|
|
2505
|
+
}
|
|
2506
|
+
if (cmd === "agents" && sub === "demo") {
|
|
2507
|
+
if (pos[2] === "enable" || pos[2] === "disable") {
|
|
2508
|
+
v[pos[2] === "enable" ? "on" : "off"] = true;
|
|
2509
|
+
pos.splice(2, 1);
|
|
2510
|
+
} else if (v.on || v.off) notes.push(`\`agents demo <agent> --${v.on ? "on" : "off"}\` is now \`agents demo ${v.on ? "enable" : "disable"} <agent>\`.`);
|
|
2511
|
+
if (v.expires !== void 0) v.vence = v.expires;
|
|
2512
|
+
else if (v.vence !== void 0) notes.push("`--vence` is now `--expires`.");
|
|
2513
|
+
}
|
|
2514
|
+
if (cmd === "channels" && pos[2] === "groups") {
|
|
2515
|
+
if (pos[3] === "enable" || pos[3] === "disable") {
|
|
2516
|
+
v[pos[3] === "enable" ? "on" : "off"] = true;
|
|
2517
|
+
pos[3] = "set";
|
|
2518
|
+
} else if (pos[3] === "set" && (v.on || v.off)) notes.push(`\`groups set \u2026 --${v.on ? "on" : "off"}\` is now \`groups ${v.on ? "enable" : "disable"} <agent> <group>\` (set keeps --role).`);
|
|
2519
|
+
}
|
|
2520
|
+
return notes;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2439
2523
|
// src/index.ts
|
|
2440
2524
|
var HELP = `${bold("Usage:")} ghosty <command> [options]
|
|
2441
2525
|
|
|
@@ -2444,6 +2528,7 @@ ${bold("Session")}
|
|
|
2444
2528
|
--device Sign in from another device with a link + code (auto over SSH/CI)
|
|
2445
2529
|
logout Sign out and revoke this device's session
|
|
2446
2530
|
whoami Show who you are signed in as
|
|
2531
|
+
use [workspace] Default workspace for boards, agents create and agents ls [--clear]
|
|
2447
2532
|
usage Your plan and how much you've used this week and month
|
|
2448
2533
|
|
|
2449
2534
|
${bold("Agents")}
|
|
@@ -2466,12 +2551,13 @@ ${bold("Agents")}
|
|
|
2466
2551
|
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes)
|
|
2467
2552
|
agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
|
|
2468
2553
|
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]
|
|
2554
|
+
agents demo enable|disable <id> Turn its public demo link on or off
|
|
2469
2555
|
agents demo <id> Public demo link /p/<slug>: anyone tries it without an account, "Keep it" clones it to theirs
|
|
2470
|
-
[--slug NAME] [--
|
|
2556
|
+
[--slug NAME] [--expires "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--chips "a|b|c"] [--starter TEXT] [--sponsor on|off] [--rotate]
|
|
2471
2557
|
|
|
2472
2558
|
${bold("Configure an agent")}
|
|
2473
2559
|
files ls|get|put|rm <id> [path] Files in the agent's machine [--file LOCAL] [--out LOCAL]
|
|
2474
|
-
skills
|
|
2560
|
+
skills enable|disable <id> <slug> Turn one of its skills (or one of Ghosty's) on or off
|
|
2475
2561
|
skills house <id> Ghosty's skills in bulk [--only a,b | --enable a,b | --disable a,b] [--dry-run]
|
|
2476
2562
|
skills ls|get|add|rm <id> [slug] Skills (add from catalog, --file SKILL.md or --dir FOLDER; get lists, --out DIR saves; rm keeps a copy)
|
|
2477
2563
|
skills templates <id> pdf-doc templates it sees: its own and Ghosty's, and which folder wins
|
|
@@ -2483,11 +2569,11 @@ ${bold("Configure an agent")}
|
|
|
2483
2569
|
channels wa import <id> --from-file F | --from-dir DIR --own-number yes|no Move a personal WhatsApp session here, no QR [--dry-run] [--replace] [--no-connect]
|
|
2484
2570
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
2485
2571
|
channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
|
|
2486
|
-
channels wa groups ls|set|create <id> [group]
|
|
2572
|
+
channels wa groups ls|enable|disable|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--dry-run]
|
|
2487
2573
|
boards ls|create|assign|move-cards|rm A workspace's boards: create (--columns-from), where an agent sends cards, move cards
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2574
|
+
cards ls <id> Recent cards on its sales board [--q TEXT] [--limit 20]
|
|
2575
|
+
cards show <id> <card> A card's thread: who wrote, text, attachments and whether gs kept a copy (folio, phone or id)
|
|
2576
|
+
cards archive <id> Archive cards in bulk (reversible) --integration X | --column X | --before 30d [--dry-run]
|
|
2491
2577
|
|
|
2492
2578
|
${bold("Talk to an agent")}
|
|
2493
2579
|
try <id> "message" One test turn, prints the full answer [--session] [--reset]
|
|
@@ -2513,6 +2599,7 @@ ${bold("Your account")}
|
|
|
2513
2599
|
docs [topic] Open the docs
|
|
2514
2600
|
|
|
2515
2601
|
${bold("Global options")}
|
|
2602
|
+
<agent> Everywhere: an agent's NAME (case-insensitive) or its id
|
|
2516
2603
|
--json Machine-readable output (stdout is JSON only)
|
|
2517
2604
|
--token <token> Use this token (or env GHOSTY_TOKEN). A gat_\u2026 agent token reaches only that agent.
|
|
2518
2605
|
--profile <name> Use a separate saved session (or env GHOSTY_PROFILE), e.g. one per account
|
|
@@ -2523,7 +2610,7 @@ ${bold("Global options")}
|
|
|
2523
2610
|
${bold("Exit codes")} 0 ok \xB7 1 API error \xB7 2 usage error \xB7 3 not signed in
|
|
2524
2611
|
${dim("Note: `ghosty` may collide with another binary on your PATH (e.g. the Ghosty TUI). Check with `which -a ghosty`; run `npx @ghostystudio/cli` to be sure.")}
|
|
2525
2612
|
${dim("Docs: https://www.ghosty.studio/docs/cli")}`;
|
|
2526
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
2613
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
2527
2614
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
2528
2615
|
function unknown(what, input, known, helpCmd) {
|
|
2529
2616
|
const s = suggest(input, known);
|
|
@@ -2533,6 +2620,45 @@ function showHelp() {
|
|
|
2533
2620
|
if (process.stdout.isTTY && !process.env.NO_COLOR && !globals.json) process.stdout.write(BANNER + "\n\n");
|
|
2534
2621
|
process.stdout.write(HELP + "\n");
|
|
2535
2622
|
}
|
|
2623
|
+
function agentArgIndex(pos) {
|
|
2624
|
+
const [cmd, sub] = pos;
|
|
2625
|
+
switch (cmd) {
|
|
2626
|
+
case "agents":
|
|
2627
|
+
if (sub === "docs" || sub === "documents") return 3;
|
|
2628
|
+
return sub && !["ls", "list", "create", "new"].includes(sub) ? 2 : null;
|
|
2629
|
+
case "files":
|
|
2630
|
+
case "skills":
|
|
2631
|
+
case "mcp":
|
|
2632
|
+
case "dbs":
|
|
2633
|
+
case "db":
|
|
2634
|
+
case "turns":
|
|
2635
|
+
case "board":
|
|
2636
|
+
case "conversations":
|
|
2637
|
+
case "schedule":
|
|
2638
|
+
return 2;
|
|
2639
|
+
case "try":
|
|
2640
|
+
case "chat":
|
|
2641
|
+
return 1;
|
|
2642
|
+
case "channels":
|
|
2643
|
+
return pos[2] === "groups" ? 4 : 3;
|
|
2644
|
+
case "boards":
|
|
2645
|
+
return sub === "assign" ? 2 : null;
|
|
2646
|
+
default:
|
|
2647
|
+
return null;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
async function useCmd(slug, v) {
|
|
2651
|
+
const cfg = await readConfig();
|
|
2652
|
+
if (v.clear) {
|
|
2653
|
+
delete cfg.workspace;
|
|
2654
|
+
await writeConfig(cfg);
|
|
2655
|
+
return out({ workspace: null }, () => `${lavender("\u2713")} No default workspace.`);
|
|
2656
|
+
}
|
|
2657
|
+
if (!slug) return out({ workspace: cfg.workspace ?? null }, () => cfg.workspace ? `Workspace: ${bold(cfg.workspace)}` : dim("No default workspace. Set one: ghosty use <workspace>"));
|
|
2658
|
+
await api(`/api/v2/workspaces/${encodeURIComponent(slug)}/boards`);
|
|
2659
|
+
await writeConfig({ ...cfg, workspace: slug });
|
|
2660
|
+
return out({ workspace: slug }, () => `${lavender("\u2713")} Using ${bold(slug)}: boards, agents create and agents ls default to it. ${dim("(ghosty use --clear to stop)")}`);
|
|
2661
|
+
}
|
|
2536
2662
|
async function main() {
|
|
2537
2663
|
const { values: v, positionals: pos } = parseArgs({
|
|
2538
2664
|
allowPositionals: true,
|
|
@@ -2614,6 +2740,8 @@ async function main() {
|
|
|
2614
2740
|
"columns-from": { type: "string" },
|
|
2615
2741
|
to: { type: "string" },
|
|
2616
2742
|
all: { type: "boolean" },
|
|
2743
|
+
clear: { type: "boolean" },
|
|
2744
|
+
expires: { type: "string" },
|
|
2617
2745
|
q: { type: "string" },
|
|
2618
2746
|
"prompt-out": { type: "string" },
|
|
2619
2747
|
toolsets: { type: "string" },
|
|
@@ -2654,7 +2782,17 @@ async function main() {
|
|
|
2654
2782
|
const tokens = splitTokens(pos, v);
|
|
2655
2783
|
globals.token = tokens.session;
|
|
2656
2784
|
globals.profile = v.profile;
|
|
2657
|
-
if (v.version) return out({ version: "0.
|
|
2785
|
+
if (v.version) return out({ version: "0.18.0" }, () => "0.18.0");
|
|
2786
|
+
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
2787
|
+
const context = (await readConfig()).workspace;
|
|
2788
|
+
if (!explicitToken()?.startsWith("gat_")) {
|
|
2789
|
+
const at = agentArgIndex(pos);
|
|
2790
|
+
if (at !== null && pos[at]) pos[at] = await resolveAgent(pos[at], context);
|
|
2791
|
+
for (const f of ["like", "prompt-base"]) {
|
|
2792
|
+
const val = v[f];
|
|
2793
|
+
if (typeof val === "string" && val !== "none") v[f] = await resolveAgent(val, context);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2658
2796
|
const [cmd, sub, a1, a2] = pos;
|
|
2659
2797
|
if (cmd === "partner" && (v.help || !sub || sub === "help")) return out({ help: PARTNER_HELP }, () => PARTNER_HELP);
|
|
2660
2798
|
const topic = cmd === "help" ? sub : v.help ? cmd : void 0;
|
|
@@ -2680,9 +2818,18 @@ async function main() {
|
|
|
2680
2818
|
const me = await api("/api/v2/me");
|
|
2681
2819
|
return out(me, () => `${bold(me.email)}${dim(` \xB7 ${new URL(process.env.GHOSTY_URL || "https://www.ghosty.studio").host}`)}`);
|
|
2682
2820
|
}
|
|
2821
|
+
case "use":
|
|
2822
|
+
return useCmd(sub, v);
|
|
2683
2823
|
case "agents":
|
|
2684
|
-
if (sub === "ls" || sub === "list" || !sub) return agentsLs();
|
|
2685
|
-
if (sub === "create" || sub === "new")
|
|
2824
|
+
if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : context);
|
|
2825
|
+
if (sub === "create" || sub === "new") {
|
|
2826
|
+
if (v.workspace === "none") delete v.workspace;
|
|
2827
|
+
else if (!v.workspace && context) {
|
|
2828
|
+
v.workspace = context;
|
|
2829
|
+
if (!globals.json) info(dim(`workspace ${context} (from ghosty use; --workspace none for a personal agent)`));
|
|
2830
|
+
}
|
|
2831
|
+
return agentsCreate(v);
|
|
2832
|
+
}
|
|
2686
2833
|
if (sub === "get") return agentsGet(a1, v.fields, v["prompt-out"]);
|
|
2687
2834
|
if (sub === "share" || sub === "unshare" || sub === "shares") return agentsShare(sub, a1, a2, v);
|
|
2688
2835
|
if (sub === "set") return agentsSet(a1, v);
|
|
@@ -2707,7 +2854,7 @@ async function main() {
|
|
|
2707
2854
|
case "board":
|
|
2708
2855
|
return boardCmd(sub, a1, v, a2);
|
|
2709
2856
|
case "boards":
|
|
2710
|
-
return boardsCmd(sub, a1, a2, v);
|
|
2857
|
+
return boardsCmd(sub, a1, a2, v, context);
|
|
2711
2858
|
case "channels":
|
|
2712
2859
|
if (a1 === "groups") return groupsCmd(pos.slice(3).filter((x) => x !== void 0), v);
|
|
2713
2860
|
return channelsCmd(sub, a1, a2, pos.slice(4).join(" ") || void 0, v);
|
|
@@ -2742,7 +2889,7 @@ async function main() {
|
|
|
2742
2889
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
2743
2890
|
}
|
|
2744
2891
|
}
|
|
2745
|
-
var notice = updateNotice("0.
|
|
2892
|
+
var notice = updateNotice("0.18.0").catch(() => null);
|
|
2746
2893
|
main().then(
|
|
2747
2894
|
async () => {
|
|
2748
2895
|
const n = await notice;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostystudio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
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": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/index.js --banner:js='#!/usr/bin/env node' --define:__VERSION__=\\\"$npm_package_version\\\"",
|
|
18
18
|
"prepack": "npm run build",
|
|
19
|
-
"test": "for t in flags prompt api rows wa-auth; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
19
|
+
"test": "for t in flags prompt api rows wa-auth resolve aliases; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"ghosty",
|