patchcord 0.5.104 → 0.5.106
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/bin/patchcord.mjs +84 -45
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -797,7 +797,7 @@ if (cmd === "subscribe") {
|
|
|
797
797
|
// (docs/main-agent.md). The "main" is the managing agent of a team; its main
|
|
798
798
|
// token is a user-scoped provisioning credential stored at ~/.patchcord/main.json
|
|
799
799
|
// (legacy: master.json) or $PATCHCORD_MAIN_TOKEN (legacy: $PATCHCORD_MASTER_TOKEN).
|
|
800
|
-
if (cmd === "main" || cmd === "
|
|
800
|
+
if (cmd === "main" || cmd === "provision" || cmd === "team" || cmd === "schedule") {
|
|
801
801
|
const M = { cyan: "\x1b[36m", green: "\x1b[32m", dim: "\x1b[2m", rst: "\x1b[0m" };
|
|
802
802
|
const MAIN_CONFIG = join(HOME, ".patchcord", "main.json");
|
|
803
803
|
const LEGACY_CONFIG = join(HOME, ".patchcord", "master.json"); // pre-rename
|
|
@@ -865,7 +865,7 @@ if (cmd === "main" || cmd === "master" || cmd === "provision" || cmd === "team")
|
|
|
865
865
|
});
|
|
866
866
|
};
|
|
867
867
|
|
|
868
|
-
if (cmd === "main"
|
|
868
|
+
if (cmd === "main") {
|
|
869
869
|
const sub = process.argv[3];
|
|
870
870
|
if (sub === "connect") {
|
|
871
871
|
const harness = flagVal("harness", flagVal("tool", ""));
|
|
@@ -902,14 +902,7 @@ if (cmd === "main" || cmd === "master" || cmd === "provision" || cmd === "team")
|
|
|
902
902
|
console.log(`\n ${M.green}✓${M.rst} Main token saved: ${M.dim}${MAIN_CONFIG}${M.rst}`);
|
|
903
903
|
process.exit(0);
|
|
904
904
|
}
|
|
905
|
-
|
|
906
|
-
const m = requireMain();
|
|
907
|
-
const { status, json } = await _httpJSON("GET", `${m.baseUrl}/api/main/whoami`, m.token);
|
|
908
|
-
if (status !== "200" || !json) { console.error(`main whoami failed (HTTP ${status})`); process.exit(1); }
|
|
909
|
-
console.log(`main · user ${json.user_id} · ${json.agents}/${json.quota} agents`);
|
|
910
|
-
process.exit(0);
|
|
911
|
-
}
|
|
912
|
-
console.error("Usage: patchcord main <connect|whoami>");
|
|
905
|
+
console.error("Usage: patchcord main connect");
|
|
913
906
|
process.exit(1);
|
|
914
907
|
}
|
|
915
908
|
|
|
@@ -993,9 +986,20 @@ if (cmd === "main" || cmd === "master" || cmd === "provision" || cmd === "team")
|
|
|
993
986
|
const m = requireMain();
|
|
994
987
|
const { status, json } = await _httpJSON("GET", `${m.baseUrl}/api/provision/list`, m.token);
|
|
995
988
|
if (status !== "200") { console.error(`list failed (HTTP ${status})`); process.exit(1); }
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
989
|
+
// Dedup by namespace:agent — the server returns one row per token, so a
|
|
990
|
+
// re-provisioned agent shows up many times. Optional --namespace filter.
|
|
991
|
+
const filterNs = flagVal("namespace");
|
|
992
|
+
const seen = new Set();
|
|
993
|
+
let n = 0;
|
|
994
|
+
for (const a of (json?.agents || [])) {
|
|
995
|
+
if (filterNs && a.namespace_id !== filterNs) continue;
|
|
996
|
+
const key = `${a.namespace_id}:${a.agent_id}`;
|
|
997
|
+
if (seen.has(key)) continue;
|
|
998
|
+
seen.add(key);
|
|
999
|
+
console.log(` ${key} ${M.dim}${a.label || ""}${M.rst}`);
|
|
1000
|
+
n++;
|
|
1001
|
+
}
|
|
1002
|
+
if (!n) console.log(" (no agents)");
|
|
999
1003
|
process.exit(0);
|
|
1000
1004
|
}
|
|
1001
1005
|
if (sub === "status") {
|
|
@@ -1134,6 +1138,73 @@ if (cmd === "main" || cmd === "master" || cmd === "provision" || cmd === "team")
|
|
|
1134
1138
|
console.error("Usage: patchcord team <init|list|launch|status>");
|
|
1135
1139
|
process.exit(1);
|
|
1136
1140
|
}
|
|
1141
|
+
|
|
1142
|
+
if (cmd === "schedule") {
|
|
1143
|
+
// User-level scheduled / recurring messages (server Plan 041). Authed by
|
|
1144
|
+
// the main token — the user can manage schedules in any namespace they own.
|
|
1145
|
+
const m = requireMain();
|
|
1146
|
+
const sub = process.argv[3];
|
|
1147
|
+
const BASE = `${m.baseUrl}/api/dashboard/scheduled`;
|
|
1148
|
+
const when = (s) => s.cron_expr ? `cron ${s.cron_expr}` : s.interval_sec ? `every ${s.interval_sec}s` : s.fire_at ? `once @ ${s.fire_at}` : s.schedule_kind;
|
|
1149
|
+
const fmt = (s) => `${s.id} ${M.green}${s.namespace_id}:${s.to_agent}${M.rst} ${s.active ? "" : M.dim + "[paused] " + M.rst}${s.name} ${M.dim}${when(s)}${s.next_fire_at ? ` → ${s.next_fire_at}` : ""}${M.rst}`;
|
|
1150
|
+
|
|
1151
|
+
if (sub === "list") {
|
|
1152
|
+
const ns = flagVal("namespace");
|
|
1153
|
+
const url = ns ? `${BASE}?namespace=${encodeURIComponent(ns)}` : BASE;
|
|
1154
|
+
const { status, json } = await _httpJSON("GET", url, m.token);
|
|
1155
|
+
if (status !== "200") { console.error(`list failed (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1156
|
+
const items = json?.schedules || [];
|
|
1157
|
+
for (const s of items) console.log(" " + fmt(s));
|
|
1158
|
+
if (!items.length) console.log(" (no schedules)");
|
|
1159
|
+
process.exit(0);
|
|
1160
|
+
}
|
|
1161
|
+
if (sub === "create") {
|
|
1162
|
+
const name = process.argv[4];
|
|
1163
|
+
if (!name || name.startsWith("-")) { console.error('Usage: patchcord schedule create <name> --namespace <ns> --to <agent> --content "..." (--at <ISO> | --cron "<expr>" | --every <sec>) [--timezone <tz>] [--thread <slug>] [--max-runs N] [--expires <ISO>]'); process.exit(1); }
|
|
1164
|
+
const ns = flagVal("namespace"), to = flagVal("to"), content = flagVal("content");
|
|
1165
|
+
if (!ns || !to || !content) { console.error("--namespace, --to, and --content are required"); process.exit(1); }
|
|
1166
|
+
const at = flagVal("at"), cron = flagVal("cron"), every = flagVal("every");
|
|
1167
|
+
let schedule_kind, extra = {};
|
|
1168
|
+
if (cron) { schedule_kind = "cron"; extra.cron_expr = cron; }
|
|
1169
|
+
else if (every) { schedule_kind = "interval"; extra.interval_sec = parseInt(every, 10); }
|
|
1170
|
+
else if (at) { schedule_kind = "once"; extra.fire_at = at; }
|
|
1171
|
+
else { console.error('one of --at <ISO>, --cron "<expr>", or --every <seconds> is required'); process.exit(1); }
|
|
1172
|
+
const body = { namespace: ns, name, to_agent: to, content, schedule_kind, timezone: flagVal("timezone", "UTC"), ...extra };
|
|
1173
|
+
const thread = flagVal("thread"); if (thread) body.thread_slug = thread;
|
|
1174
|
+
const maxRuns = flagVal("max-runs"); if (maxRuns) body.max_runs = parseInt(maxRuns, 10);
|
|
1175
|
+
const expires = flagVal("expires"); if (expires) body.expires_at = expires;
|
|
1176
|
+
const { status, json } = await _httpJSON("POST", BASE, m.token, body);
|
|
1177
|
+
if (status !== "201" && status !== "200") { console.error(`create failed (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1178
|
+
console.log(`✓ scheduled ${M.green}${ns}:${to}${M.rst} [${schedule_kind}] ${M.dim}${json?.schedule?.id || ""}${M.rst}`);
|
|
1179
|
+
process.exit(0);
|
|
1180
|
+
}
|
|
1181
|
+
if (sub === "cancel" || sub === "delete" || sub === "rm") {
|
|
1182
|
+
const id = process.argv[4];
|
|
1183
|
+
if (!id) { console.error("Usage: patchcord schedule cancel <id>"); process.exit(1); }
|
|
1184
|
+
const { status, json } = await _httpJSON("DELETE", `${BASE}/${id}`, m.token);
|
|
1185
|
+
if (status !== "200") { console.error(`cancel failed (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1186
|
+
console.log(`✓ cancelled ${id}`);
|
|
1187
|
+
process.exit(0);
|
|
1188
|
+
}
|
|
1189
|
+
if (sub === "test" || sub === "fire") {
|
|
1190
|
+
const id = process.argv[4];
|
|
1191
|
+
if (!id) { console.error("Usage: patchcord schedule test <id>"); process.exit(1); }
|
|
1192
|
+
const { status, json } = await _httpJSON("POST", `${BASE}/${id}/test`, m.token, {});
|
|
1193
|
+
if (status !== "200") { console.error(`test failed (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1194
|
+
console.log(`✓ fired ${id} once (does not consume max-runs)`);
|
|
1195
|
+
process.exit(0);
|
|
1196
|
+
}
|
|
1197
|
+
if (sub === "pause" || sub === "resume") {
|
|
1198
|
+
const id = process.argv[4];
|
|
1199
|
+
if (!id) { console.error(`Usage: patchcord schedule ${sub} <id>`); process.exit(1); }
|
|
1200
|
+
const { status, json } = await _httpJSON("PATCH", `${BASE}/${id}`, m.token, { active: sub === "resume" });
|
|
1201
|
+
if (status !== "200") { console.error(`${sub} failed (HTTP ${status}): ${json?.error || ""}`); process.exit(1); }
|
|
1202
|
+
console.log(`✓ ${sub === "resume" ? "resumed" : "paused"} ${id}`);
|
|
1203
|
+
process.exit(0);
|
|
1204
|
+
}
|
|
1205
|
+
console.error("Usage: patchcord schedule <create|list|cancel|test|pause|resume>");
|
|
1206
|
+
process.exit(1);
|
|
1207
|
+
}
|
|
1137
1208
|
}
|
|
1138
1209
|
|
|
1139
1210
|
if (cmd === "update" || cmd === "--update") {
|
|
@@ -2701,38 +2772,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
2701
2772
|
}
|
|
2702
2773
|
|
|
2703
2774
|
// ── channel: spawn the channel MCP server (used by .mcp.json) ──
|
|
2704
|
-
if (cmd === "channel") {
|
|
2705
|
-
const channelScript = join(pluginRoot, "channel", "server.ts");
|
|
2706
|
-
if (!existsSync(channelScript)) {
|
|
2707
|
-
console.error("Channel server not found. Reinstall patchcord.");
|
|
2708
|
-
process.exit(1);
|
|
2709
|
-
}
|
|
2710
|
-
// Prefer bun, fall back to node (tsx)
|
|
2711
|
-
const hasBun = run("which bun");
|
|
2712
|
-
if (hasBun) {
|
|
2713
|
-
const { spawnSync } = await import("child_process");
|
|
2714
|
-
// Install deps if needed
|
|
2715
|
-
const channelDir = join(pluginRoot, "channel");
|
|
2716
|
-
if (!existsSync(join(channelDir, "node_modules"))) {
|
|
2717
|
-
spawnSync("bun", ["install", "--no-summary"], { cwd: channelDir, stdio: "inherit" });
|
|
2718
|
-
}
|
|
2719
|
-
const result = spawnSync("bun", ["run", channelScript], { stdio: "inherit", env: process.env });
|
|
2720
|
-
process.exit(result.status ?? 1);
|
|
2721
|
-
} else {
|
|
2722
|
-
console.error("Channel plugin requires bun. Install from https://bun.sh");
|
|
2723
|
-
process.exit(1);
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
|
|
2727
|
-
// ── back-compat: init → install + agent ───────────────────────
|
|
2728
|
-
if (cmd === "init") {
|
|
2729
|
-
console.log(`"patchcord init" is now two commands:
|
|
2730
|
-
|
|
2731
|
-
patchcord install One-time global setup (once)
|
|
2732
|
-
patchcord agent Set up MCP for this project (per project)`);
|
|
2733
|
-
process.exit(0);
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
2775
|
// ── skill: custom skill from web console ─────────────────────
|
|
2737
2776
|
if (cmd === "skill") {
|
|
2738
2777
|
const sub = process.argv[3];
|