@ghostystudio/cli 0.1.0 → 0.2.1
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 +4 -0
- package/dist/index.js +100 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,10 @@ ghosty try <id> "message" [--session] [--reset] [--whatsapp]
|
|
|
27
27
|
ghosty chat <id> ["message"] [--conversation ID]
|
|
28
28
|
ghosty conversations ls <id>
|
|
29
29
|
|
|
30
|
+
ghosty schedule add <id> "what to do" --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]
|
|
31
|
+
ghosty schedule ls <id>
|
|
32
|
+
ghosty schedule rm <id> <schedule-id>
|
|
33
|
+
|
|
30
34
|
ghosty me files ls|upload|rm [--kind image|video|audio|document|other]
|
|
31
35
|
ghosty docs
|
|
32
36
|
```
|
package/dist/index.js
CHANGED
|
@@ -275,7 +275,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
275
275
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
276
276
|
headers: {
|
|
277
277
|
authorization: `Bearer ${token}`,
|
|
278
|
-
"user-agent": `ghosty-cli/${"0.1
|
|
278
|
+
"user-agent": `ghosty-cli/${"0.2.1"}`,
|
|
279
279
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
280
280
|
...opts.headers
|
|
281
281
|
},
|
|
@@ -296,7 +296,7 @@ async function toError(res) {
|
|
|
296
296
|
body = JSON.parse(text);
|
|
297
297
|
} catch {
|
|
298
298
|
}
|
|
299
|
-
const detail = body.message || body.error_description || body.error || text.slice(0, 300) || res.statusText;
|
|
299
|
+
const detail = body.message || body.error_description || (body.detalle && body.error ? `${body.error}: ${body.detalle}` : body.error) || text.slice(0, 300) || res.statusText;
|
|
300
300
|
if (res.status === 401) return new CliError("Your session is not valid anymore.", EXIT.AUTH, "Run: ghosty login");
|
|
301
301
|
if (res.status === 403) return new CliError(`Not allowed: ${detail}`, EXIT.API, "Run: ghosty login (to refresh your permissions)");
|
|
302
302
|
if (res.status === 404) return new CliError("Not found.", EXIT.API, "Check the agent id (ghosty agents ls) or that your token belongs to that agent.");
|
|
@@ -604,6 +604,90 @@ ${dim(`${(r.storage.usedBytes / 1e9).toFixed(2)} GB of ${r.storage.limitGb} GB u
|
|
|
604
604
|
}
|
|
605
605
|
}
|
|
606
606
|
|
|
607
|
+
// src/commands/schedule.ts
|
|
608
|
+
var enc3 = encodeURIComponent;
|
|
609
|
+
function minutesOf(s, flag) {
|
|
610
|
+
const m = /^(\d+(?:\.\d+)?)\s*(m|min|h|d)?$/i.exec(s.trim());
|
|
611
|
+
if (!m) throw usage(`Invalid ${flag}: ${s}.`, "Use minutes, hours or days: 30m, 12h, 1d");
|
|
612
|
+
const n = Number(m[1]);
|
|
613
|
+
const unit = (m[2] ?? "m").toLowerCase();
|
|
614
|
+
return Math.round(unit === "d" ? n * 1440 : unit === "h" ? n * 60 : n);
|
|
615
|
+
}
|
|
616
|
+
function isoOf(s, flag) {
|
|
617
|
+
const d = new Date(s);
|
|
618
|
+
if (Number.isNaN(d.getTime())) throw usage(`Invalid ${flag}: ${s}.`, "Use an ISO date, e.g. 2026-10-01T09:00-06:00");
|
|
619
|
+
return d.toISOString();
|
|
620
|
+
}
|
|
621
|
+
var every = (min) => min == null ? "once" : min % 1440 === 0 ? `${min / 1440}d` : min % 60 === 0 ? `${min / 60}h` : `${min}m`;
|
|
622
|
+
var short = (s, n = 60) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
623
|
+
async function agentSchedule(agent) {
|
|
624
|
+
return api(`/api/v2/me/agents/${enc3(agent)}/schedule`);
|
|
625
|
+
}
|
|
626
|
+
async function scheduleCmd(sub, agent, arg, v) {
|
|
627
|
+
if (sub === "ls" || sub === "list") {
|
|
628
|
+
if (!agent) throw usage("Missing agent id.", "Example: ghosty schedule ls <agent-id>");
|
|
629
|
+
const r = await agentSchedule(agent);
|
|
630
|
+
if (globals.json) return out({ pending: r.pendientes, runs: r.corridas });
|
|
631
|
+
const pending = r.pendientes.map((p) => ({
|
|
632
|
+
id: p.id,
|
|
633
|
+
due: p.dueAt,
|
|
634
|
+
every: every(p.repeatMin),
|
|
635
|
+
until: p.until ?? "",
|
|
636
|
+
conversation: p.sessionId,
|
|
637
|
+
prompt: short(p.prompt)
|
|
638
|
+
}));
|
|
639
|
+
const runs = r.corridas.slice(0, 10).map((p) => ({ fired: p.firedAt ?? "", conversation: p.sessionId, answer: short(p.answer ?? "", 70) }));
|
|
640
|
+
return out(
|
|
641
|
+
null,
|
|
642
|
+
() => [
|
|
643
|
+
pending.length ? table(pending, ["id", "due", "every", "until", "conversation", "prompt"]) : dim("No scheduled turns."),
|
|
644
|
+
...runs.length ? ["", lavender("Recent runs"), table(runs, ["fired", "conversation", "answer"])] : []
|
|
645
|
+
].join("\n")
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
if (sub === "add") {
|
|
649
|
+
if (!agent || !arg) throw usage("Missing agent id or prompt.", 'Example: ghosty schedule add <agent-id> "Check the campaign" --in 30m');
|
|
650
|
+
if (!v.at && !v.in) throw usage("Say when.", "Use --in 30m or --at 2026-10-01T09:00-06:00");
|
|
651
|
+
if (v.at && v.in) throw usage("Use --at or --in, not both.");
|
|
652
|
+
const body = {
|
|
653
|
+
prompt: arg,
|
|
654
|
+
...v.in ? { inMinutes: minutesOf(v.in, "--in") } : { dueAt: isoOf(v.at, "--at") },
|
|
655
|
+
repeatMin: v.every ? minutesOf(v.every, "--every") : null,
|
|
656
|
+
until: v.until ? isoOf(v.until, "--until") : null
|
|
657
|
+
};
|
|
658
|
+
if (body.repeatMin && !body.until) throw usage("A repeating turn needs --until.", "Example: --every 12h --until 2026-10-25");
|
|
659
|
+
let sid = v.conversation;
|
|
660
|
+
const created = !sid;
|
|
661
|
+
if (!sid) {
|
|
662
|
+
sid = (await api(convPath(agent), { method: "POST", body: {} })).id;
|
|
663
|
+
if (!globals.json) info(dim(`new conversation ${sid}`));
|
|
664
|
+
}
|
|
665
|
+
let r;
|
|
666
|
+
try {
|
|
667
|
+
r = await api(convPath(agent, `/${enc3(sid)}/schedule`), { body });
|
|
668
|
+
} catch (e) {
|
|
669
|
+
if (created) await api(convPath(agent, `/${enc3(sid)}`), { method: "DELETE" }).catch(() => {
|
|
670
|
+
});
|
|
671
|
+
throw e;
|
|
672
|
+
}
|
|
673
|
+
if (v.title && created) await api(convPath(agent, `/${enc3(sid)}`), { method: "PATCH", body: { title: v.title } });
|
|
674
|
+
const p = r.programado;
|
|
675
|
+
return out(
|
|
676
|
+
{ scheduled: p },
|
|
677
|
+
() => `${lavender("\u2713")} Scheduled ${p.id} \xB7 ${p.dueAt}${p.repeatMin ? ` \xB7 every ${every(p.repeatMin)} until ${p.until}` : ""}
|
|
678
|
+
${dim(`conversation ${p.sessionId}`)}`
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
if (sub === "rm" || sub === "cancel") {
|
|
682
|
+
if (!agent || !arg) throw usage("Missing agent id or schedule id.", "Example: ghosty schedule rm <agent-id> <schedule-id> (ids: ghosty schedule ls <agent-id>)");
|
|
683
|
+
const row = (await agentSchedule(agent)).pendientes.find((p) => p.id === arg);
|
|
684
|
+
if (!row) throw usage(`No pending scheduled turn ${arg}.`, "See them with: ghosty schedule ls <agent-id>");
|
|
685
|
+
await api(convPath(agent, `/${enc3(row.sessionId)}/schedule/${enc3(arg)}`), { method: "DELETE" });
|
|
686
|
+
return out({ canceled: arg }, () => `${lavender("\u2713")} Canceled ${arg}`);
|
|
687
|
+
}
|
|
688
|
+
throw usage(`Unknown schedule subcommand: ${sub ?? "(none)"}.`, "Try: ghosty schedule ls | add | rm");
|
|
689
|
+
}
|
|
690
|
+
|
|
607
691
|
// src/index.ts
|
|
608
692
|
var HELP = `${bold("Usage:")} ghosty <command> [options]
|
|
609
693
|
|
|
@@ -629,6 +713,11 @@ ${bold("Talk to an agent")}
|
|
|
629
713
|
chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID]
|
|
630
714
|
conversations ls <id> List conversations
|
|
631
715
|
|
|
716
|
+
${bold("Schedule turns")} ${dim("(each run lands in a conversation and notifies your phone; an answer of exactly OK stays silent)")}
|
|
717
|
+
schedule ls <id> Pending turns and recent runs
|
|
718
|
+
schedule add <id> "what to do" Schedule a turn --in 30m | --at ISO [--every 12h --until ISO] [--conversation ID] [--title T]
|
|
719
|
+
schedule rm <id> <schedule-id> Cancel a pending turn
|
|
720
|
+
|
|
632
721
|
${bold("Your account")}
|
|
633
722
|
me files ls|upload|rm Files in your account [--kind image|video|audio|document|other]
|
|
634
723
|
docs [topic] Open the docs
|
|
@@ -666,12 +755,17 @@ async function main() {
|
|
|
666
755
|
reset: { type: "boolean" },
|
|
667
756
|
whatsapp: { type: "boolean" },
|
|
668
757
|
conversation: { type: "string" },
|
|
669
|
-
kind: { type: "string" }
|
|
758
|
+
kind: { type: "string" },
|
|
759
|
+
at: { type: "string" },
|
|
760
|
+
in: { type: "string" },
|
|
761
|
+
every: { type: "string" },
|
|
762
|
+
until: { type: "string" },
|
|
763
|
+
title: { type: "string" }
|
|
670
764
|
}
|
|
671
765
|
});
|
|
672
766
|
globals.json = Boolean(v.json);
|
|
673
767
|
globals.token = v.token;
|
|
674
|
-
if (v.version) return out({ version: "0.1
|
|
768
|
+
if (v.version) return out({ version: "0.2.1" }, () => "0.2.1");
|
|
675
769
|
const [cmd, sub, a1, a2] = pos;
|
|
676
770
|
if (!cmd || v.help || cmd === "help") return showHelp();
|
|
677
771
|
switch (cmd) {
|
|
@@ -712,6 +806,8 @@ async function main() {
|
|
|
712
806
|
case "conversations":
|
|
713
807
|
if (sub === "ls" || sub === "list") return conversationsLs(a1);
|
|
714
808
|
throw usage(`Unknown conversations subcommand: ${sub ?? "(none)"}.`, "Try: ghosty conversations ls <agent-id>");
|
|
809
|
+
case "schedule":
|
|
810
|
+
return scheduleCmd(sub, a1, a2, v);
|
|
715
811
|
case "me":
|
|
716
812
|
if (sub === "files") return meFiles(a1, a2, v);
|
|
717
813
|
throw usage(`Unknown me subcommand: ${sub ?? "(none)"}.`, "Try: ghosty me files ls");
|
package/package.json
CHANGED