@ghostystudio/cli 0.9.0 → 0.10.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 +30 -6
- package/dist/index.js +179 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,24 +8,32 @@ npx -y @ghostystudio/cli login
|
|
|
8
8
|
npm i -g @ghostystudio/cli && ghosty login
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
`ghosty login` opens your browser (OAuth2 + PKCE). If you already have a session on ghosty.studio, opening the link is all it takes. A coding agent can drive it for you: `ghosty login --json` prints `{"event":"login_url","url":…}` first so the agent can hand you the link.
|
|
11
|
+
`ghosty login` opens your browser (OAuth2 + PKCE). If you already have a session on ghosty.studio, opening the link is all it takes. A coding agent can drive it for you: `ghosty login --json` prints `{"event":"login_url","url":…}` first so the agent can hand you the link. Over SSH, CI or from another device, `ghosty login --device` gives you a link + code instead (automatic when there is no local browser).
|
|
12
12
|
|
|
13
13
|
## Commands
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
ghosty whoami
|
|
17
|
+
ghosty usage # plan and usage this week / month (also: ghosty me usage)
|
|
17
18
|
ghosty agents ls
|
|
19
|
+
ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,…]
|
|
18
20
|
ghosty agents get <id> [--fields name,model,prompt]
|
|
19
|
-
ghosty agents set <id> [--name] [--model] [--prompt | --prompt-file FILE]
|
|
21
|
+
ghosty agents set <id> [--name] [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,…] [--connect ID,…] [--disconnect ID,…]
|
|
22
|
+
ghosty agents rm <id> [--yes]
|
|
20
23
|
ghosty agents restart <id>
|
|
24
|
+
ghosty agents box <id> [--check PATH]… [--contains PATH=TEXT]… [--recycle] # is its machine on the new image?
|
|
25
|
+
ghosty agents demo <id> [--slug NAME] [--on | --off] [--vence "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N]
|
|
26
|
+
[--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate] # public /p/ link, no account needed
|
|
21
27
|
ghosty agents doctor <id> [--verbose] # exits 1 when it finds a problem
|
|
22
28
|
ghosty agents docs ls|grant|revoke <id> [doc] [--write] [--no-deliver]
|
|
23
29
|
ghosty agents set <id> --voice elevenlabs:ID --voice-replies auto --auto-stage on
|
|
24
30
|
|
|
25
31
|
ghosty files ls|get|put|rm <id> [path] [--file LOCAL] [--out LOCAL]
|
|
26
|
-
ghosty skills ls|get|add|rm <id> [slug] [--file SKILL.md | --dir FOLDER] [--out DIR]
|
|
27
|
-
ghosty
|
|
28
|
-
ghosty
|
|
32
|
+
ghosty skills ls|get|add|rm <id> [slug] [--file SKILL.md | --dir FOLDER] [--out DIR] # get without --out only lists
|
|
33
|
+
ghosty skills templates <id> # pdf-doc templates: its own and Ghosty's, and which folder wins
|
|
34
|
+
ghosty dbs ls|tables|query|import|grant|revoke <id> [db] [--write] [--arg V]… [--external-write t1,t2] [--from easybits:ID --tables a,b --append --dry-run]
|
|
35
|
+
ghosty turns ls <id> [--errors | --status running|done|stopped|error] [--since 1h] [--limit N]
|
|
36
|
+
ghosty turns show <id> [turn-id|last] # where a turn's time went: each tool with its seconds, output tokens
|
|
29
37
|
ghosty channels whatsapp ls|enable|disable <id> [number] [--dry-run]
|
|
30
38
|
ghosty board archive <id> --integration X | --column X | --before 30d [--dry-run]
|
|
31
39
|
ghosty mcp get|set <id> [--file servers.json]
|
|
@@ -43,20 +51,36 @@ ghosty schedule rm <id> <schedule-id>
|
|
|
43
51
|
ghosty me files ls|upload|rm [--kind image|video|audio|document|other]
|
|
44
52
|
ghosty credentials ls|set|rm [provider] [--from-env VAR] # never as an argument
|
|
45
53
|
ghosty docs
|
|
54
|
+
|
|
55
|
+
ghosty partner ls|enable|credentials|agent|tenants|audit|try # one agent for each of YOUR customers: ghosty partner --help
|
|
46
56
|
```
|
|
47
57
|
|
|
48
|
-
Global: `--json` (stdout is JSON only), `--token <token>` / `GHOSTY_TOKEN` (a `gat_…` agent token reaches only that agent), `--help`, `--version`.
|
|
58
|
+
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_SECRET` when set.
|
|
59
|
+
|
|
60
|
+
Aliases: `creds` = `credentials`, `db` = `dbs`, `dbs sql` = `dbs query`.
|
|
49
61
|
|
|
50
62
|
Every command has its own help with examples: `ghosty <command> --help` or `ghosty help <command>`.
|
|
51
63
|
|
|
52
64
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
53
65
|
|
|
66
|
+
### Changes in 0.9.1
|
|
67
|
+
|
|
68
|
+
- `ghosty agents --help` describes every `agents demo` flag; `ghosty usage --help` has its own page.
|
|
69
|
+
- "Learn more" links point to the exact section of https://www.ghosty.studio/docs/cli/comandos; `partner --help` links to /docs/cli/partners.
|
|
70
|
+
|
|
54
71
|
### Changes in 0.9.0
|
|
55
72
|
|
|
56
73
|
- `try --waba`: a test turn through the **real** WhatsApp Business path (inherited context, price rule, board, voice decision). It prints what it would have sent (text, voice note, files) and sends nothing. `--media` for photos/audio/PDF and `--history` to seed what was said before. `--whatsapp` is now an alias.
|
|
57
74
|
- `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.
|
|
58
75
|
- `agents doctor` speaks English like the rest of the CLI; `channels whatsapp ls` returns a full URL.
|
|
59
76
|
|
|
77
|
+
### Changes in 0.10.0
|
|
78
|
+
|
|
79
|
+
- Tells you when a new version is out (checks npm once a day, on stderr; `GHOSTY_NO_UPDATE_CHECK=1` or `CI` turns it off). An unknown option on an old version says to update.
|
|
80
|
+
- `skills get` without `--out` only lists the files; with `--out` it prints the absolute path. It no longer writes into the current folder silently.
|
|
81
|
+
- New: `turns show` (timeline of one turn), `skills templates` (pdf-doc templates and their source), `agents box` (check / recycle the machine serving an agent).
|
|
82
|
+
- `agents get` includes `env` (secret-looking values masked).
|
|
83
|
+
|
|
60
84
|
### Changes in 0.8.0
|
|
61
85
|
|
|
62
86
|
- Destructive commands (`agents rm`, `files rm`, `skills rm`, `credentials rm`, `board archive`, `dbs import` over existing rows, `channels whatsapp enable`) ask first. **Without a terminal they need `--yes`** or exit `2`.
|
package/dist/index.js
CHANGED
|
@@ -129,7 +129,7 @@ async function login() {
|
|
|
129
129
|
const verifier = b64url(randomBytes(32));
|
|
130
130
|
const challenge = b64url(createHash("sha256").update(verifier).digest());
|
|
131
131
|
const state = b64url(randomBytes(16));
|
|
132
|
-
const { code, redirectUri } = await new Promise((
|
|
132
|
+
const { code, redirectUri } = await new Promise((resolve2, reject) => {
|
|
133
133
|
const server = createServer((req, res) => {
|
|
134
134
|
const u = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
135
135
|
if (u.pathname !== "/cb") {
|
|
@@ -140,7 +140,7 @@ async function login() {
|
|
|
140
140
|
res.writeHead(ok ? 200 : 400, { "content-type": "text/html; charset=utf-8" }).end(CLOSE_PAGE(ok, base));
|
|
141
141
|
clearTimeout(timer);
|
|
142
142
|
server.close();
|
|
143
|
-
if (ok)
|
|
143
|
+
if (ok) resolve2({ code: u.searchParams.get("code"), redirectUri: redirectUri2 });
|
|
144
144
|
else reject(new CliError(`Login was not completed (${u.searchParams.get("error") ?? "state mismatch"}).`, EXIT.AUTH, "Run: ghosty login"));
|
|
145
145
|
});
|
|
146
146
|
let redirectUri2 = "";
|
|
@@ -278,11 +278,11 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
278
278
|
const token = await currentToken();
|
|
279
279
|
const httpsRequest = base.startsWith("https:") ? (await import("node:https")).request : (await import("node:http")).request;
|
|
280
280
|
const data = JSON.stringify(body);
|
|
281
|
-
return new Promise((
|
|
281
|
+
return new Promise((resolve2) => {
|
|
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.
|
|
285
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.10.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
286
286
|
}, (res) => {
|
|
287
287
|
let buf = "";
|
|
288
288
|
res.on("data", (c) => buf += c);
|
|
@@ -293,11 +293,11 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
293
293
|
} catch {
|
|
294
294
|
parsed = { error: buf.slice(0, 300) };
|
|
295
295
|
}
|
|
296
|
-
|
|
296
|
+
resolve2({ status: res.statusCode ?? 0, retryAfter: retryAfterSeconds(res.headers["retry-after"]), body: parsed });
|
|
297
297
|
});
|
|
298
298
|
});
|
|
299
299
|
req.on("timeout", () => req.destroy(new Error(`no answer in ${Math.round(timeoutMs / 6e4)} min`)));
|
|
300
|
-
req.on("error", (e) =>
|
|
300
|
+
req.on("error", (e) => resolve2({ status: 0, retryAfter: null, body: { error: String(e) } }));
|
|
301
301
|
req.end(data);
|
|
302
302
|
});
|
|
303
303
|
}
|
|
@@ -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.
|
|
327
|
+
"user-agent": `ghosty-cli/${"0.10.0"}`,
|
|
328
328
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
329
329
|
...opts.headers
|
|
330
330
|
},
|
|
@@ -358,7 +358,7 @@ async function toError(res) {
|
|
|
358
358
|
// src/commands/agents.ts
|
|
359
359
|
import { mkdir as mkdir3, readdir, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
360
360
|
import { homedir as homedir2 } from "node:os";
|
|
361
|
-
import { dirname as dirname2, join as join3, relative } from "node:path";
|
|
361
|
+
import { dirname as dirname2, join as join3, relative, resolve } from "node:path";
|
|
362
362
|
|
|
363
363
|
// src/prompt.ts
|
|
364
364
|
import { createInterface } from "node:readline/promises";
|
|
@@ -672,7 +672,7 @@ async function readSkillDir(dir) {
|
|
|
672
672
|
}
|
|
673
673
|
var trashDir = (id, slug) => join3(process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config"), "ghosty", "trash", id, `${slug}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
|
|
674
674
|
async function skillsCmd(sub, id, slug, v) {
|
|
675
|
-
const ex = "ghosty skills ls <agent-id> | get <agent-id> <slug> | add <agent-id> <slug> [--file SKILL.md | --dir FOLDER] | rm <agent-id> <slug>";
|
|
675
|
+
const ex = "ghosty skills ls <agent-id> | get <agent-id> <slug> [--out DIR] | templates <agent-id> | add <agent-id> <slug> [--file SKILL.md | --dir FOLDER] | rm <agent-id> <slug>";
|
|
676
676
|
id = need(id, "agent id", ex);
|
|
677
677
|
switch (sub ?? "ls") {
|
|
678
678
|
case "ls":
|
|
@@ -691,7 +691,13 @@ ${dim(`${more} more in the community catalog: ghosty skills add <agent-id> <slug
|
|
|
691
691
|
case "get": {
|
|
692
692
|
slug = need(slug, "skill slug", "ghosty skills get <agent-id> <slug> [--out DIR]");
|
|
693
693
|
const sk = await api(agentPath(id, `/skills/${enc2(slug)}`));
|
|
694
|
-
|
|
694
|
+
if (!v.out) {
|
|
695
|
+
const files = sk.files.map((f) => ({ path: f.path, bytes: Buffer.from(f.contentBase64, "base64").length }));
|
|
696
|
+
out({ slug, files, saved: null }, () => `${table(files, ["path", "bytes"])}
|
|
697
|
+
${dim(`Download: ghosty skills get ${id} ${slug} --out DIR`)}`);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const dir = resolve(v.out);
|
|
695
701
|
const bytes = await writeSkill(dir, sk);
|
|
696
702
|
out({ slug, saved: dir, files: sk.files.map((f) => f.path), bytes }, () => `${lavender("\u2713")} ${slug} \u2192 ${dir}/ ${dim(`(${sk.files.length} file(s), ${bytes} bytes)`)}`);
|
|
697
703
|
return;
|
|
@@ -704,6 +710,13 @@ ${dim(`${more} more in the community catalog: ghosty skills add <agent-id> <slug
|
|
|
704
710
|
out(r, () => `${lavender("\u2713")} Skill ${slug} installed.${r?.nota ? dim(` (${r.nota})`) : ""}`);
|
|
705
711
|
return;
|
|
706
712
|
}
|
|
713
|
+
case "templates": {
|
|
714
|
+
const r = await api(agentPath(id, "/templates"));
|
|
715
|
+
const rows = r.templates.map((t) => ({ name: t.name, source: t.source, used: t.active ? "yes" : "no (shadowed)", description: (t.description ?? "").replace(/\s+/g, " ").slice(0, 60) }));
|
|
716
|
+
out(r, () => `${table(rows, ["name", "source", "used", "description"])}
|
|
717
|
+
${dim(`pdf-doc reads from: ${r.active ?? "(not found)"}`)}`);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
707
720
|
case "rm":
|
|
708
721
|
case "remove": {
|
|
709
722
|
slug = need(slug, "skill slug", ex);
|
|
@@ -744,7 +757,7 @@ async function agentsDemo(id, v) {
|
|
|
744
757
|
if (v.on) body.enabled = true;
|
|
745
758
|
if (v.off) body.enabled = false;
|
|
746
759
|
if (v.turns) body.maxTurns = Number(v.turns);
|
|
747
|
-
if (v.
|
|
760
|
+
if (v.vence !== void 0) body.venceEl = v.vence === "no" ? null : new Date(v.vence).toISOString();
|
|
748
761
|
if (v["per-ip"]) body.perIpDay = Number(v["per-ip"]);
|
|
749
762
|
if (v.welcome !== void 0) body.welcome = v.welcome;
|
|
750
763
|
if (v.chips !== void 0) body.chips = v.chips.split("|").map((c) => c.trim()).filter(Boolean);
|
|
@@ -759,7 +772,7 @@ async function agentsDemo(id, v) {
|
|
|
759
772
|
d,
|
|
760
773
|
() => [
|
|
761
774
|
`${d.enabled ? lavender("\u25CF on") : dim("\u25CB off")} ${d.url ?? dim("(no link yet: --slug NAME)")}`,
|
|
762
|
-
dim(`
|
|
775
|
+
dim(`expires: ${d.venceEl ? new Date(d.venceEl).toLocaleString("es-MX") : "never"} \xB7 cap ${d.maxTurns} messages per visitor \xB7 ${d.perIpDay} per IP per day`),
|
|
763
776
|
d.welcome ? `welcome: ${d.welcome}` : dim("welcome: (default greeting)"),
|
|
764
777
|
d.starter ? `starter: \u2728 ${d.starter}` : dim("starter: (none)"),
|
|
765
778
|
d.chips.length ? `chips: ${d.chips.join(" | ")}` : dim("chips: (default)"),
|
|
@@ -768,6 +781,28 @@ async function agentsDemo(id, v) {
|
|
|
768
781
|
].join("\n")
|
|
769
782
|
);
|
|
770
783
|
}
|
|
784
|
+
async function agentsBox(id, v) {
|
|
785
|
+
id = need(id, "agent id", "ghosty agents box <agent-id> [--check /opt/claude-worker/skills/pdf-doc/templates/calendario.html] [--recycle]");
|
|
786
|
+
if (v.recycle) {
|
|
787
|
+
const r2 = await api(agentPath(id, "/box"), { method: "POST", body: { recycle: true } });
|
|
788
|
+
out(r2, () => `${lavender("\u2713")} Recycled. ${dim(r2?.note ?? "")}
|
|
789
|
+
${dim(`Verify after one turn: ghosty agents box ${id} --check <path>`)}`);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const q = new URLSearchParams();
|
|
793
|
+
for (const c of v.check ?? []) q.append("check", c);
|
|
794
|
+
for (const c of v.contains ?? []) q.append("contains", c);
|
|
795
|
+
const r = await api(agentPath(id, `/box?${q}`));
|
|
796
|
+
out(r, () => {
|
|
797
|
+
if (!r.boxes.length) return dim(r.note ?? "No live box.");
|
|
798
|
+
const lines = r.boxes.flatMap((b) => [
|
|
799
|
+
`${bold(b.sandboxId)} ${dim(b.lastMessageAt ? `last message ${b.lastMessageAt}` : r.kind)}${b.error ? ` ${red(b.error)}` : ""}`,
|
|
800
|
+
...b.checks.map((c) => ` ${c.ok ? lavender("\u2713") : red("\u2717")} ${c.path}${c.contains !== void 0 ? ` \u220B ${c.contains}` : ""}`)
|
|
801
|
+
]);
|
|
802
|
+
const verdict = !(v.check?.length || v.contains?.length) ? dim("Alive. Add --check PATH to confirm the new image.") : r.stale ? red(`${r.stale} box(es) on an OLD image: ghosty agents box ${id} --recycle`) : `${lavender("\u2713")} Serving with the expected image.`;
|
|
803
|
+
return [...lines, verdict].join("\n");
|
|
804
|
+
});
|
|
805
|
+
}
|
|
771
806
|
|
|
772
807
|
// src/commands/chat.ts
|
|
773
808
|
import { randomUUID } from "node:crypto";
|
|
@@ -1069,7 +1104,7 @@ ${bold("Options")}
|
|
|
1069
1104
|
--json Machine-readable output
|
|
1070
1105
|
|
|
1071
1106
|
${bold("Server credential mode")} GHOSTY_PARTNER_KEY=gpk_\u2026 GHOSTY_PARTNER_SECRET=gps_\u2026 ghosty partner agent get
|
|
1072
|
-
${dim("Docs: https://www.ghosty.studio/docs/cli")}`;
|
|
1107
|
+
${dim("Docs: https://www.ghosty.studio/docs/cli/partners")}`;
|
|
1073
1108
|
var enc5 = encodeURIComponent;
|
|
1074
1109
|
function partnerCreds() {
|
|
1075
1110
|
const key = process.env.GHOSTY_PARTNER_KEY;
|
|
@@ -1090,7 +1125,7 @@ async function call(path, v, init = {}) {
|
|
|
1090
1125
|
"x-ghosty-ts": ts,
|
|
1091
1126
|
"x-ghosty-sig": sig,
|
|
1092
1127
|
"content-type": "application/json",
|
|
1093
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1128
|
+
"user-agent": `ghosty-cli/${"0.10.0"}`
|
|
1094
1129
|
},
|
|
1095
1130
|
body: method === "GET" ? void 0 : raw
|
|
1096
1131
|
});
|
|
@@ -1364,8 +1399,9 @@ async function agentDocsCmd(sub, id, doc, v) {
|
|
|
1364
1399
|
throw usage(`Unknown agents docs subcommand: ${sub}.`, "Try: ghosty agents docs ls | grant | revoke");
|
|
1365
1400
|
}
|
|
1366
1401
|
}
|
|
1367
|
-
async function turnsCmd(sub, id, v) {
|
|
1368
|
-
if (sub
|
|
1402
|
+
async function turnsCmd(sub, id, turnId, v) {
|
|
1403
|
+
if (sub === "show") return turnShow(id, turnId);
|
|
1404
|
+
if (sub && sub !== "ls" && sub !== "list") throw usage(`Unknown turns subcommand: ${sub}.`, "Try: ghosty turns ls <agent-id> --errors --since 1h \xB7 ghosty turns show <agent-id> [turn-id|last]");
|
|
1369
1405
|
id = need2(id, "agent id", "ghosty turns ls <agent-id> --errors --since 1h");
|
|
1370
1406
|
const q = new URLSearchParams();
|
|
1371
1407
|
const status = v.errors ? "error" : v.status;
|
|
@@ -1375,6 +1411,7 @@ async function turnsCmd(sub, id, v) {
|
|
|
1375
1411
|
const r = await api(agentPath2(id, `/turns?${q}`));
|
|
1376
1412
|
const when = (s) => s.slice(0, 16).replace("T", " ");
|
|
1377
1413
|
const rows = r.turns.map((t) => ({
|
|
1414
|
+
id: t.id,
|
|
1378
1415
|
started: when(t.startedAt),
|
|
1379
1416
|
state: t.state === "error" ? red(t.state) : t.state,
|
|
1380
1417
|
secs: t.ms == null ? "" : Math.round(t.ms / 1e3),
|
|
@@ -1383,8 +1420,24 @@ async function turnsCmd(sub, id, v) {
|
|
|
1383
1420
|
error: (t.error ?? "").replace(/\s+/g, " ").slice(0, 90)
|
|
1384
1421
|
}));
|
|
1385
1422
|
const tally = Object.entries(r.counts).map(([k, n]) => `${k} ${n}`).join(" \xB7 ");
|
|
1386
|
-
out(r, () => rows.length ? `${table(rows, ["started", "state", "secs", "from", "session", "error"])}
|
|
1387
|
-
${dim(`${v.since ? `since ${v.since}: ` : ""}${tally}
|
|
1423
|
+
out(r, () => rows.length ? `${table(rows, ["id", "started", "state", "secs", "from", "session", "error"])}
|
|
1424
|
+
${dim(`${v.since ? `since ${v.since}: ` : ""}${tally} \xB7 where the time went: ghosty turns show ${id} <turn-id>`)}` : dim(`No ${status ?? ""} turns${v.since ? ` since ${v.since}` : ""}.`));
|
|
1425
|
+
}
|
|
1426
|
+
async function turnShow(id, turnId) {
|
|
1427
|
+
id = need2(id, "agent id", "ghosty turns show <agent-id> [turn-id|last]");
|
|
1428
|
+
const r = await api(agentPath2(id, `/turns/${enc6(turnId ?? "last")}`));
|
|
1429
|
+
out(r, () => {
|
|
1430
|
+
const t = r.turn;
|
|
1431
|
+
const head = `${bold(t.id)} ${t.state === "error" ? red(t.state) : t.state} ${t.secs ?? "?"} s ${dim(`${r.totals.model ?? ""} \xB7 ${r.totals.modelCalls} model calls \xB7 ${r.totals.outputTokens} output tokens \xB7 ${r.totals.toolSecs} s in tools`)}`;
|
|
1432
|
+
const line = (s) => {
|
|
1433
|
+
const at = `+${s.at.toFixed(1)}s`.padStart(8);
|
|
1434
|
+
if (s.kind === "tool") return `${at} ${bold(s.tool ?? "?")} ${dim(s.input ?? "")} ${s.secs === void 0 ? red("(no result)") : `${s.secs} s`}${s.error ? red(" error") : ""}`;
|
|
1435
|
+
const tok = s.outTokens ? dim(` [${s.outTokens} out]`) : "";
|
|
1436
|
+
return s.kind === "thinking" ? `${at} ${dim("thinking")}${tok}` : `${at} ${s.text}${tok}`;
|
|
1437
|
+
};
|
|
1438
|
+
const top = r.byTool.slice(0, 5).map((b) => `${b.tool} ${b.secs} s (${b.calls})`).join(" \xB7 ");
|
|
1439
|
+
return [head, ...r.steps.map(line), dim(`slowest: ${top || "\u2014"}`)].join("\n");
|
|
1440
|
+
});
|
|
1388
1441
|
}
|
|
1389
1442
|
async function agentsDoctor(id, v) {
|
|
1390
1443
|
id = need2(id, "agent id", "ghosty agents doctor <agent-id>");
|
|
@@ -1416,7 +1469,7 @@ async function hiddenPrompt(label) {
|
|
|
1416
1469
|
stdin.setRawMode(true);
|
|
1417
1470
|
stdin.resume();
|
|
1418
1471
|
stdin.setEncoding("utf8");
|
|
1419
|
-
return new Promise((
|
|
1472
|
+
return new Promise((resolve2, reject) => {
|
|
1420
1473
|
let value = "";
|
|
1421
1474
|
const onData = (chunk) => {
|
|
1422
1475
|
for (const ch of chunk) {
|
|
@@ -1425,7 +1478,7 @@ async function hiddenPrompt(label) {
|
|
|
1425
1478
|
stdin.pause();
|
|
1426
1479
|
stdin.off("data", onData);
|
|
1427
1480
|
process.stderr.write("\n");
|
|
1428
|
-
return
|
|
1481
|
+
return resolve2(value);
|
|
1429
1482
|
}
|
|
1430
1483
|
if (ch === "") {
|
|
1431
1484
|
stdin.setRawMode(false);
|
|
@@ -1537,6 +1590,7 @@ Archive ${plan.matched} card(s) on \xAB${plan.board}\xBB? (reversible: a new mes
|
|
|
1537
1590
|
|
|
1538
1591
|
// src/help.ts
|
|
1539
1592
|
var DOCS = "https://www.ghosty.studio/docs/cli";
|
|
1593
|
+
var REF = `${DOCS}/comandos`;
|
|
1540
1594
|
var HELPS = {
|
|
1541
1595
|
agents: {
|
|
1542
1596
|
summary: "Create, inspect, change, check and delete agents.",
|
|
@@ -1548,7 +1602,8 @@ var HELPS = {
|
|
|
1548
1602
|
"ghosty agents doctor <agent-id> [--verbose]",
|
|
1549
1603
|
"ghosty agents docs ls|grant|revoke <agent-id> [doc] [--write] [--no-deliver]",
|
|
1550
1604
|
"ghosty agents restart <agent-id>",
|
|
1551
|
-
"ghosty agents
|
|
1605
|
+
"ghosty agents box <agent-id> [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]",
|
|
1606
|
+
'ghosty agents demo <agent-id> [--slug NAME] [--on | --off] [--vence "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--starter TEXT] [--chips "a|b|c"] [--sponsor on|off] [--rotate]',
|
|
1552
1607
|
"ghosty agents rm <agent-id> [--yes]"
|
|
1553
1608
|
],
|
|
1554
1609
|
flags: [
|
|
@@ -1559,28 +1614,44 @@ var HELPS = {
|
|
|
1559
1614
|
["--verbose", "doctor: also list the checks that passed"],
|
|
1560
1615
|
["--write", "docs grant: the agent may replace the document (default: read only)"],
|
|
1561
1616
|
["--no-deliver", "docs grant: it can read it but never send it to whoever writes"],
|
|
1562
|
-
["--yes", "rm: skip the confirmation (required without a terminal)"]
|
|
1617
|
+
["--yes", "rm: skip the confirmation (required without a terminal)"],
|
|
1618
|
+
["--slug NAME", "demo: link name; a 6-char code is appended (/p/NAME-CODE)"],
|
|
1619
|
+
["--on | --off", "demo: turn the public link on or off"],
|
|
1620
|
+
["--vence DATE|no", `demo: fixed expiry in your machine's time ("2026-10-15T20:00"); no = never`],
|
|
1621
|
+
["--turns N", "demo: messages per visitor (default 150)"],
|
|
1622
|
+
["--per-ip N", "demo: messages per IP per day (default 30)"],
|
|
1623
|
+
["--welcome TEXT", "demo: chat greeting (empty = default)"],
|
|
1624
|
+
["--starter TEXT", 'demo: highlighted first suggestion ("" removes it)'],
|
|
1625
|
+
['--chips "a|b|c"', "demo: suggestions, separated by |"],
|
|
1626
|
+
["--sponsor on|off", "demo: copies people keep run on YOUR engine key"],
|
|
1627
|
+
["--rotate", "demo: new code; the old link stops working"],
|
|
1628
|
+
["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
|
|
1629
|
+
["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
|
|
1630
|
+
["--recycle", "box: replace its machines (not mid-turn); the new one starts on the next turn. ACP: use restart"]
|
|
1563
1631
|
],
|
|
1564
1632
|
examples: [
|
|
1565
1633
|
"ghosty agents doctor cmu7pgbyr0001gee3ruk172mm",
|
|
1566
1634
|
"ghosty agents set <id> --prompt-file PROMPT.md",
|
|
1567
1635
|
"ghosty agents set <id> --voice elevenlabs:21m00Tcm4TlvDq8ikWAM --voice-replies auto --auto-stage on",
|
|
1568
1636
|
'ghosty agents docs grant <id> "Cat\xE1logo 2026.pdf"',
|
|
1569
|
-
"ghosty agents docs grant <id> precios.xlsx --write --no-deliver"
|
|
1637
|
+
"ghosty agents docs grant <id> precios.xlsx --write --no-deliver",
|
|
1638
|
+
'ghosty agents demo <id> --slug guardias --on --vence "2026-10-15T20:00" --starter "Ens\xE9\xF1ame c\xF3mo empezar"',
|
|
1639
|
+
"ghosty agents box <id> --check /opt/claude-worker/skills/pdf-doc/templates/calendario.html"
|
|
1570
1640
|
],
|
|
1571
|
-
notes: ["doctor exits 1 when it finds a problem, so it can gate a script or CI."],
|
|
1572
|
-
learn: `${
|
|
1641
|
+
notes: ["doctor exits 1 when it finds a problem, so it can gate a script or CI.", "demo with no flags only prints its status: link, expiry, caps and numbers."],
|
|
1642
|
+
learn: `${REF}#agentes`
|
|
1573
1643
|
},
|
|
1574
1644
|
skills: {
|
|
1575
1645
|
summary: "Skills of an agent (agentskills.io format: a folder with SKILL.md).",
|
|
1576
1646
|
usage: [
|
|
1577
1647
|
"ghosty skills ls <agent-id>",
|
|
1578
1648
|
"ghosty skills get <agent-id> <slug> [--out DIR]",
|
|
1649
|
+
"ghosty skills templates <agent-id>",
|
|
1579
1650
|
"ghosty skills add <agent-id> <slug> [--file SKILL.md | --dir FOLDER]",
|
|
1580
1651
|
"ghosty skills rm <agent-id> <slug> [--yes]"
|
|
1581
1652
|
],
|
|
1582
1653
|
flags: [
|
|
1583
|
-
["--out DIR", "get: folder to write into
|
|
1654
|
+
["--out DIR", "get: folder to write into; without it, get only lists the files"],
|
|
1584
1655
|
["--file FILE", "add: upload one SKILL.md"],
|
|
1585
1656
|
["--dir FOLDER", "add: upload a whole skill folder (SKILL.md + assets); restores a backup"],
|
|
1586
1657
|
["--yes", "rm: skip the confirmation (required without a terminal)"]
|
|
@@ -1591,8 +1662,11 @@ var HELPS = {
|
|
|
1591
1662
|
"ghosty skills add <id> pdf-doc # from the community catalog",
|
|
1592
1663
|
"ghosty skills rm <id> cotizacion # keeps a local copy first"
|
|
1593
1664
|
],
|
|
1594
|
-
notes: [
|
|
1595
|
-
|
|
1665
|
+
notes: [
|
|
1666
|
+
"rm saves a copy under ~/.config/ghosty/trash/ and prints the command that restores it.",
|
|
1667
|
+
"templates reads a live machine (run one turn first). pdf-doc uses the FIRST folder with print.css: a full skill of your own shadows Ghosty's templates."
|
|
1668
|
+
],
|
|
1669
|
+
learn: `${REF}#skills`
|
|
1596
1670
|
},
|
|
1597
1671
|
dbs: {
|
|
1598
1672
|
summary: "Databases an agent can use, and what it may write from customer channels.",
|
|
@@ -1621,31 +1695,32 @@ var HELPS = {
|
|
|
1621
1695
|
"ghosty dbs import <id> totequim --from easybits:6a10c84c\u2026 --dry-run # reads $EASYBITS_API_KEY"
|
|
1622
1696
|
],
|
|
1623
1697
|
notes: ["<db> is the database id or its name. From owner channels the agent reads and writes everything granted."],
|
|
1624
|
-
learn: `${
|
|
1698
|
+
learn: `${REF}#bases-de-datos`
|
|
1625
1699
|
},
|
|
1626
1700
|
turns: {
|
|
1627
|
-
summary: "An agent's recent turns: what answered, what failed and why.",
|
|
1628
|
-
usage: ["ghosty turns ls <agent-id> [--errors | --status STATE] [--since 1h] [--limit N]"],
|
|
1701
|
+
summary: "An agent's recent turns: what answered, what failed and why, and where one turn's time went.",
|
|
1702
|
+
usage: ["ghosty turns ls <agent-id> [--errors | --status STATE] [--since 1h] [--limit N]", "ghosty turns show <agent-id> [turn-id | last]"],
|
|
1629
1703
|
flags: [
|
|
1630
1704
|
["--errors", "only failed turns (same as --status error)"],
|
|
1631
1705
|
["--status S", "running | done | stopped | error"],
|
|
1632
1706
|
["--since T", "30m, 1h, 2d or an ISO date"],
|
|
1633
1707
|
["--limit N", "max rows (default 20, max 500)"]
|
|
1634
1708
|
],
|
|
1635
|
-
examples: ["ghosty turns ls <id> --errors --since 1h", "ghosty turns ls <id> --json --limit 100"],
|
|
1636
|
-
|
|
1709
|
+
examples: ["ghosty turns ls <id> --errors --since 1h", "ghosty turns ls <id> --json --limit 100", "ghosty turns show <id> # last turn: each tool, its seconds, output tokens"],
|
|
1710
|
+
notes: ["show reads the worker's transcript: pool engines only (not ACP/goose)."],
|
|
1711
|
+
learn: `${REF}#turnos`
|
|
1637
1712
|
},
|
|
1638
1713
|
files: {
|
|
1639
1714
|
summary: "Files in the agent's machine.",
|
|
1640
1715
|
usage: ["ghosty files ls <agent-id> [dir]", "ghosty files get <agent-id> <path> [--out LOCAL]", "ghosty files put <agent-id> <path> [--file LOCAL | < stdin]", "ghosty files rm <agent-id> <path> [--yes]"],
|
|
1641
1716
|
examples: ["ghosty files put <id> knowledge/precios.md --file precios.md", "cat notas.md | ghosty files put <id> notas.md"],
|
|
1642
|
-
learn: `${
|
|
1717
|
+
learn: `${REF}#archivos`
|
|
1643
1718
|
},
|
|
1644
1719
|
mcp: {
|
|
1645
1720
|
summary: "MCP servers of an agent. set REPLACES the whole list.",
|
|
1646
1721
|
usage: ["ghosty mcp get <agent-id>", "ghosty mcp set <agent-id> --file servers.json"],
|
|
1647
1722
|
examples: ["ghosty mcp get <id> > servers.json && $EDITOR servers.json && ghosty mcp set <id> --file servers.json"],
|
|
1648
|
-
learn: `${
|
|
1723
|
+
learn: `${REF}#servidores-mcp`
|
|
1649
1724
|
},
|
|
1650
1725
|
try: {
|
|
1651
1726
|
summary: "Test turns: plain, through the real WhatsApp path (nothing is sent), or a replay of real conversations.",
|
|
@@ -1676,25 +1751,25 @@ var HELPS = {
|
|
|
1676
1751
|
"Replays survive Ghosty deploys: a 503 is retried after its Retry-After; the report lives on your disk.",
|
|
1677
1752
|
"--whatsapp is now an alias of --waba."
|
|
1678
1753
|
],
|
|
1679
|
-
learn: `${
|
|
1754
|
+
learn: `${REF}#turnos-de-prueba`
|
|
1680
1755
|
},
|
|
1681
1756
|
chat: {
|
|
1682
1757
|
summary: "Stream a conversation; with no message it is interactive.",
|
|
1683
1758
|
usage: ['ghosty chat <agent-id> ["message"] [--conversation ID]', "ghosty conversations ls <agent-id>"],
|
|
1684
1759
|
examples: ['ghosty chat <id> "hola"', "ghosty chat <id>"],
|
|
1685
|
-
learn: `${
|
|
1760
|
+
learn: `${REF}#conversaciones`
|
|
1686
1761
|
},
|
|
1687
1762
|
schedule: {
|
|
1688
1763
|
summary: "Turns that run later, on the agent's own clock.",
|
|
1689
1764
|
usage: ['ghosty schedule add <agent-id> "what to do" --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]', "ghosty schedule ls <agent-id>", "ghosty schedule rm <agent-id> <schedule-id>"],
|
|
1690
1765
|
examples: ['ghosty schedule add <id> "revisa pedidos pendientes" --every 12h --in 1h'],
|
|
1691
|
-
learn: `${
|
|
1766
|
+
learn: `${REF}#programar-turnos`
|
|
1692
1767
|
},
|
|
1693
1768
|
me: {
|
|
1694
1769
|
summary: "Your account: files and usage.",
|
|
1695
1770
|
usage: ["ghosty me files ls|upload|rm [--kind image|video|audio|document|other]", "ghosty usage"],
|
|
1696
1771
|
examples: ["ghosty me files ls --kind document"],
|
|
1697
|
-
learn: `${
|
|
1772
|
+
learn: `${REF}#tu-cuenta`
|
|
1698
1773
|
}
|
|
1699
1774
|
};
|
|
1700
1775
|
HELPS.credentials = {
|
|
@@ -1710,7 +1785,7 @@ HELPS.credentials = {
|
|
|
1710
1785
|
"op read op://vault/claude/token | ghosty credentials set anthropic-oauth"
|
|
1711
1786
|
],
|
|
1712
1787
|
notes: ["The key is never accepted as an argument: it would stay in your shell history and in `ps`."],
|
|
1713
|
-
learn: `${
|
|
1788
|
+
learn: `${REF}#credenciales`
|
|
1714
1789
|
};
|
|
1715
1790
|
HELPS.creds = HELPS.credentials;
|
|
1716
1791
|
HELPS.channels = {
|
|
@@ -1722,7 +1797,7 @@ HELPS.channels = {
|
|
|
1722
1797
|
],
|
|
1723
1798
|
examples: ["ghosty channels whatsapp ls <id>", "ghosty channels whatsapp enable <id> 5512345678 --dry-run", "ghosty channels whatsapp disable <id> 5512345678"],
|
|
1724
1799
|
notes: ["<number> is its id, its label or its last digits. One number, one agent: enabling it takes it from the other agent.", "Disabling keeps logging messages; nobody answers them."],
|
|
1725
|
-
learn: `${
|
|
1800
|
+
learn: `${REF}#canales-de-whatsapp`
|
|
1726
1801
|
};
|
|
1727
1802
|
HELPS.board = {
|
|
1728
1803
|
summary: "The agent's sales board. It belongs to the WORKSPACE: you must be a member, a shared agent is not enough.",
|
|
@@ -1736,10 +1811,10 @@ HELPS.board = {
|
|
|
1736
1811
|
],
|
|
1737
1812
|
examples: ["ghosty board archive <id> --integration prueba --dry-run", "ghosty board archive <id> --column Perdido --before 30d --yes"],
|
|
1738
1813
|
notes: ["Archiving is reversible: when the customer writes again the card comes back. At least one filter is required."],
|
|
1739
|
-
learn: `${
|
|
1814
|
+
learn: `${REF}#tablero`
|
|
1740
1815
|
};
|
|
1741
1816
|
HELPS.conversations = HELPS.chat;
|
|
1742
|
-
HELPS.usage = HELPS.me;
|
|
1817
|
+
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` };
|
|
1743
1818
|
function helpText(cmd) {
|
|
1744
1819
|
const h = HELPS[cmd];
|
|
1745
1820
|
if (!h) return void 0;
|
|
@@ -1758,6 +1833,43 @@ function helpText(cmd) {
|
|
|
1758
1833
|
].join("\n");
|
|
1759
1834
|
}
|
|
1760
1835
|
|
|
1836
|
+
// src/update.ts
|
|
1837
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
|
|
1838
|
+
import { homedir as homedir3 } from "node:os";
|
|
1839
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1840
|
+
var FILE = join4(process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config"), "ghosty", "update.json");
|
|
1841
|
+
var DAY = 24 * 60 * 60 * 1e3;
|
|
1842
|
+
var UPGRADE = "npm i -g @ghostystudio/cli@latest";
|
|
1843
|
+
function newer(a, b) {
|
|
1844
|
+
const pa = a.split(".").map(Number);
|
|
1845
|
+
const pb = b.split(".").map(Number);
|
|
1846
|
+
for (let i = 0; i < 3; i++) if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) > (pb[i] ?? 0);
|
|
1847
|
+
return false;
|
|
1848
|
+
}
|
|
1849
|
+
async function latestVersion() {
|
|
1850
|
+
if (process.env.GHOSTY_NO_UPDATE_CHECK || process.env.CI) return null;
|
|
1851
|
+
let cache = {};
|
|
1852
|
+
try {
|
|
1853
|
+
cache = JSON.parse(await readFile6(FILE, "utf8"));
|
|
1854
|
+
} catch {
|
|
1855
|
+
}
|
|
1856
|
+
if (!cache.checkedAt || Date.now() - cache.checkedAt > DAY) {
|
|
1857
|
+
try {
|
|
1858
|
+
const r = await fetch("https://registry.npmjs.org/@ghostystudio/cli/latest", { signal: AbortSignal.timeout(1500) });
|
|
1859
|
+
const latest = (await r.json()).version;
|
|
1860
|
+
if (latest) cache = { latest, checkedAt: Date.now() };
|
|
1861
|
+
await mkdir4(dirname3(FILE), { recursive: true });
|
|
1862
|
+
await writeFile4(FILE, JSON.stringify(cache));
|
|
1863
|
+
} catch {
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
return cache.latest ?? null;
|
|
1867
|
+
}
|
|
1868
|
+
async function updateNotice(current) {
|
|
1869
|
+
const latest = await latestVersion();
|
|
1870
|
+
return latest && newer(latest, current) ? `Update available: ${current} \u2192 ${latest}. Run: ${UPGRADE}` : null;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1761
1873
|
// src/index.ts
|
|
1762
1874
|
var HELP = `${bold("Usage:")} ghosty <command> [options]
|
|
1763
1875
|
|
|
@@ -1779,12 +1891,14 @@ ${bold("Agents")}
|
|
|
1779
1891
|
agents docs ls|grant|revoke <id> [doc] Documents it reads and may send [--write] [--no-deliver]
|
|
1780
1892
|
agents rm <id> Delete an agent (owner only; asks you to type its name, or --yes)
|
|
1781
1893
|
agents restart <id> Restart an ACP agent (needsRestart) so it loads new skills
|
|
1894
|
+
agents box <id> The machine serving it: is it on the new image? [--check PATH]\u2026 [--contains PATH=TEXT]\u2026 [--recycle]
|
|
1782
1895
|
agents demo <id> Public demo link /p/<slug>: anyone tries it without an account, "Keep it" clones it to theirs
|
|
1783
|
-
[--slug NAME] [--on | --off] [--
|
|
1896
|
+
[--slug NAME] [--on | --off] [--vence "YYYY-MM-DDTHH:MM"|no] [--turns N] [--per-ip N] [--welcome TEXT] [--chips "a|b|c"] [--starter TEXT] [--sponsor on|off] [--rotate]
|
|
1784
1897
|
|
|
1785
1898
|
${bold("Configure an agent")}
|
|
1786
1899
|
files ls|get|put|rm <id> [path] Files in the agent's machine [--file LOCAL] [--out LOCAL]
|
|
1787
|
-
skills ls|get|add|rm <id> [slug] Skills (add from catalog, --file SKILL.md or --dir FOLDER; rm keeps a copy)
|
|
1900
|
+
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)
|
|
1901
|
+
skills templates <id> pdf-doc templates it sees: its own and Ghosty's, and which folder wins
|
|
1788
1902
|
dbs ls|tables|query|import|grant|revoke <id> [db] Databases it uses [--write] [--external-write t1,t2] [--from easybits:ID --dry-run]
|
|
1789
1903
|
mcp get|set <id> MCP servers [--file servers.json]
|
|
1790
1904
|
channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
|
|
@@ -1797,6 +1911,7 @@ ${bold("Talk to an agent")}
|
|
|
1797
1911
|
chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID]
|
|
1798
1912
|
conversations ls <id> List conversations
|
|
1799
1913
|
turns ls <id> Recent turns and why they failed [--errors] [--since 1h] [--limit N]
|
|
1914
|
+
turns show <id> [turn] Where a turn's time went: each tool with its seconds, output tokens (default: last)
|
|
1800
1915
|
|
|
1801
1916
|
${bold("Schedule turns")} ${dim("(each run lands in a conversation and notifies your phone; an answer of exactly OK stays silent)")}
|
|
1802
1917
|
schedule ls <id> Pending turns and recent runs
|
|
@@ -1823,7 +1938,7 @@ ${bold("Exit codes")} 0 ok \xB7 1 API error \xB7 2 usage error \xB7 3 not signe
|
|
|
1823
1938
|
${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.")}
|
|
1824
1939
|
${dim("Docs: https://www.ghosty.studio/docs/cli")}`;
|
|
1825
1940
|
var COMMANDS = ["login", "logout", "whoami", "usage", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "credentials", "docs", "help"];
|
|
1826
|
-
var AGENTS_SUBS = ["ls", "create", "get", "set", "doctor", "docs", "rm", "restart", "demo"];
|
|
1941
|
+
var AGENTS_SUBS = ["ls", "create", "get", "set", "doctor", "docs", "rm", "restart", "demo", "box"];
|
|
1827
1942
|
function unknown(what, input, known, helpCmd) {
|
|
1828
1943
|
const s = suggest(input, known);
|
|
1829
1944
|
return usage(`Unknown command: ${what}.`, s ? `Did you mean "${s}"? Run: ${helpCmd}` : `Run: ${helpCmd}`);
|
|
@@ -1881,7 +1996,7 @@ async function main() {
|
|
|
1881
1996
|
on: { type: "boolean" },
|
|
1882
1997
|
off: { type: "boolean" },
|
|
1883
1998
|
turns: { type: "string" },
|
|
1884
|
-
|
|
1999
|
+
vence: { type: "string" },
|
|
1885
2000
|
"per-ip": { type: "string" },
|
|
1886
2001
|
welcome: { type: "string" },
|
|
1887
2002
|
chips: { type: "string" },
|
|
@@ -1910,14 +2025,17 @@ async function main() {
|
|
|
1910
2025
|
tables: { type: "string" },
|
|
1911
2026
|
append: { type: "boolean" },
|
|
1912
2027
|
column: { type: "string" },
|
|
1913
|
-
before: { type: "string" }
|
|
2028
|
+
before: { type: "string" },
|
|
2029
|
+
recycle: { type: "boolean" },
|
|
2030
|
+
check: { type: "string", multiple: true },
|
|
2031
|
+
contains: { type: "string", multiple: true }
|
|
1914
2032
|
}
|
|
1915
2033
|
});
|
|
1916
2034
|
globals.json = Boolean(v.json);
|
|
1917
2035
|
const tokens = splitTokens(pos, v);
|
|
1918
2036
|
globals.token = tokens.session;
|
|
1919
2037
|
globals.profile = v.profile;
|
|
1920
|
-
if (v.version) return out({ version: "0.
|
|
2038
|
+
if (v.version) return out({ version: "0.10.0" }, () => "0.10.0");
|
|
1921
2039
|
const [cmd, sub, a1, a2] = pos;
|
|
1922
2040
|
if (cmd === "partner" && (v.help || !sub || sub === "help")) return out({ help: PARTNER_HELP }, () => PARTNER_HELP);
|
|
1923
2041
|
const topic = cmd === "help" ? sub : v.help ? cmd : void 0;
|
|
@@ -1953,6 +2071,7 @@ async function main() {
|
|
|
1953
2071
|
if (sub === "docs" || sub === "documents") return agentDocsCmd(a1, a2, pos[4], v);
|
|
1954
2072
|
if (sub === "restart") return agentsRestart(a1);
|
|
1955
2073
|
if (sub === "demo") return agentsDemo(a1, v);
|
|
2074
|
+
if (sub === "box") return agentsBox(a1, v);
|
|
1956
2075
|
throw unknown(`agents ${sub}`, sub, AGENTS_SUBS, "ghosty agents --help");
|
|
1957
2076
|
case "files":
|
|
1958
2077
|
return filesCmd(sub, a1, a2, v);
|
|
@@ -1964,7 +2083,7 @@ async function main() {
|
|
|
1964
2083
|
case "db":
|
|
1965
2084
|
return dbsCmd(sub, a1, a2, pos.slice(4).filter((x) => x !== void 0), v);
|
|
1966
2085
|
case "turns":
|
|
1967
|
-
return turnsCmd(sub, a1, v);
|
|
2086
|
+
return turnsCmd(sub, a1, a2, v);
|
|
1968
2087
|
case "board":
|
|
1969
2088
|
return boardCmd(sub, a1, v);
|
|
1970
2089
|
case "channels":
|
|
@@ -1999,11 +2118,18 @@ async function main() {
|
|
|
1999
2118
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
2000
2119
|
}
|
|
2001
2120
|
}
|
|
2121
|
+
var notice = updateNotice("0.10.0").catch(() => null);
|
|
2002
2122
|
main().then(
|
|
2003
|
-
() =>
|
|
2004
|
-
|
|
2123
|
+
async () => {
|
|
2124
|
+
const n = await notice;
|
|
2125
|
+
if (n) info(dim(n));
|
|
2126
|
+
process.exit(EXIT.OK);
|
|
2127
|
+
},
|
|
2128
|
+
async (e) => {
|
|
2005
2129
|
if (e && typeof e === "object" && "code" in e && String(e.code).startsWith("ERR_PARSE_ARGS")) {
|
|
2006
|
-
|
|
2130
|
+
const n = await notice;
|
|
2131
|
+
fail(usage(e.message, n ? `${n}
|
|
2132
|
+
This option may exist in the new version.` : `Run: ghosty --help (or update: ${UPGRADE})`));
|
|
2007
2133
|
}
|
|
2008
2134
|
fail(e);
|
|
2009
2135
|
}
|
package/package.json
CHANGED