@1claw/cli 0.59.10 → 0.60.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 +12 -0
- package/dist/src/commands/approval.d.ts.map +1 -1
- package/dist/src/commands/approval.js +35 -16
- package/dist/src/commands/approval.js.map +1 -1
- package/dist/src/commands/chart.d.ts +6 -0
- package/dist/src/commands/chart.d.ts.map +1 -0
- package/dist/src/commands/chart.js +190 -0
- package/dist/src/commands/chart.js.map +1 -0
- package/dist/src/commands/connector.d.ts +3 -0
- package/dist/src/commands/connector.d.ts.map +1 -0
- package/dist/src/commands/connector.js +136 -0
- package/dist/src/commands/connector.js.map +1 -0
- package/dist/src/commands/notify.d.ts +3 -0
- package/dist/src/commands/notify.d.ts.map +1 -0
- package/dist/src/commands/notify.js +155 -0
- package/dist/src/commands/notify.js.map +1 -0
- package/dist/src/commands/platform.js +231 -0
- package/dist/src/commands/platform.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { api } from "../client.js";
|
|
4
|
+
import { requireToken, handleError } from "../middleware.js";
|
|
5
|
+
import { printTable, printSuccess, printInfo, printJson } from "../output.js";
|
|
6
|
+
export const notifyCommand = new Command("notify").description("Manage where approvals reach you — SMS, webhook, email, push");
|
|
7
|
+
notifyCommand
|
|
8
|
+
.command("list")
|
|
9
|
+
.alias("ls")
|
|
10
|
+
.description("List your notification targets")
|
|
11
|
+
.option("--json", "Output as JSON")
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
try {
|
|
14
|
+
requireToken();
|
|
15
|
+
const result = await api("/notification-targets");
|
|
16
|
+
if (opts.json) {
|
|
17
|
+
printJson(result);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if ((result.targets ?? []).length === 0) {
|
|
21
|
+
printInfo("No notification targets. Add one with `1claw notify add`.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
printTable(result.targets.map((t) => ({
|
|
25
|
+
id: t.id,
|
|
26
|
+
type: t.target_type,
|
|
27
|
+
destination: destinationOf(t),
|
|
28
|
+
status: statusOf(t),
|
|
29
|
+
events: t.events.length === 0 ? chalk.dim("all") : t.events.join(", "),
|
|
30
|
+
})), [
|
|
31
|
+
{ key: "id", header: "ID", width: 38 },
|
|
32
|
+
{ key: "type", header: "Type", width: 10 },
|
|
33
|
+
{ key: "destination", header: "Destination", width: 30 },
|
|
34
|
+
{ key: "status", header: "Status", width: 22 },
|
|
35
|
+
{ key: "events", header: "Events", width: 24 },
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
handleError(e);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
notifyCommand
|
|
43
|
+
.command("add <type> <destination>")
|
|
44
|
+
.description("Add a target: sms +14155550123 | webhook https://… | email a@b.co")
|
|
45
|
+
.option("--agent <agent-id>", "Agent whose SMS channel sends to this number")
|
|
46
|
+
.option("--events <events>", "Comma-separated event names (default: all)")
|
|
47
|
+
.option("--json", "Output as JSON")
|
|
48
|
+
.action(async (type, destination, opts) => {
|
|
49
|
+
try {
|
|
50
|
+
requireToken();
|
|
51
|
+
const config = {};
|
|
52
|
+
switch (type) {
|
|
53
|
+
case "sms":
|
|
54
|
+
config.phone_number = destination;
|
|
55
|
+
break;
|
|
56
|
+
case "webhook":
|
|
57
|
+
config.url = destination;
|
|
58
|
+
break;
|
|
59
|
+
case "email":
|
|
60
|
+
config.email = destination;
|
|
61
|
+
break;
|
|
62
|
+
case "expo":
|
|
63
|
+
config.push_token = destination;
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
console.error(chalk.red("Type must be one of: sms, webhook, email, expo"));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const body = { target_type: type, config };
|
|
70
|
+
if (opts.agent)
|
|
71
|
+
body.agent_id = opts.agent;
|
|
72
|
+
if (opts.events) {
|
|
73
|
+
body.events = String(opts.events)
|
|
74
|
+
.split(",")
|
|
75
|
+
.map((s) => s.trim())
|
|
76
|
+
.filter(Boolean);
|
|
77
|
+
}
|
|
78
|
+
const target = await api("/notification-targets", {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body,
|
|
81
|
+
});
|
|
82
|
+
if (opts.json) {
|
|
83
|
+
printJson(target);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
printSuccess(`Added ${type} target ${target.id}`);
|
|
87
|
+
// Saying "added" and stopping would imply it can already approve
|
|
88
|
+
// things. It cannot until the number is proved.
|
|
89
|
+
if (type === "sms") {
|
|
90
|
+
console.log("");
|
|
91
|
+
printInfo("It will receive notifications now, but cannot approve anything until verified:");
|
|
92
|
+
console.log(chalk.cyan(` 1claw notify verify ${target.id}`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
handleError(e);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
notifyCommand
|
|
100
|
+
.command("verify <id>")
|
|
101
|
+
.description("Verify an SMS target (texts a code, then asks for it)")
|
|
102
|
+
.option("--code <code>", "Submit a code you already received")
|
|
103
|
+
.action(async (id, opts) => {
|
|
104
|
+
try {
|
|
105
|
+
requireToken();
|
|
106
|
+
if (opts.code) {
|
|
107
|
+
await api(`/notification-targets/${id}/verify`, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
body: { code: String(opts.code).trim() },
|
|
110
|
+
});
|
|
111
|
+
printSuccess("Verified — this number can now approve tier-1 requests by reply.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const started = await api(`/notification-targets/${id}/verify/start`, { method: "POST", body: {} });
|
|
115
|
+
printSuccess(`Code sent. It expires in ${Math.round((started.expires_in_seconds ?? 600) / 60)} minutes.`);
|
|
116
|
+
console.log("");
|
|
117
|
+
printInfo("Then run:");
|
|
118
|
+
console.log(chalk.cyan(` 1claw notify verify ${id} --code 123456`));
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
handleError(e);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
notifyCommand
|
|
125
|
+
.command("remove <id>")
|
|
126
|
+
.alias("rm")
|
|
127
|
+
.description("Remove a notification target")
|
|
128
|
+
.action(async (id) => {
|
|
129
|
+
try {
|
|
130
|
+
requireToken();
|
|
131
|
+
await api(`/notification-targets/${id}`, { method: "DELETE" });
|
|
132
|
+
printSuccess(`Removed ${id}`);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
handleError(e);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
function destinationOf(t) {
|
|
139
|
+
const c = t.config ?? {};
|
|
140
|
+
return String(c.phone_number ?? c.url ?? c.email ?? c.push_token ?? chalk.dim("—"));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Verified and unverified are genuinely different states for SMS, and the
|
|
144
|
+
* difference is what the number is allowed to do.
|
|
145
|
+
*/
|
|
146
|
+
function statusOf(t) {
|
|
147
|
+
if (!t.is_active)
|
|
148
|
+
return chalk.dim("inactive");
|
|
149
|
+
if (t.target_type !== "sms")
|
|
150
|
+
return chalk.green("active");
|
|
151
|
+
return t.verified
|
|
152
|
+
? chalk.green("verified")
|
|
153
|
+
: chalk.yellow("unverified (cannot approve)");
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../../src/commands/notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -1564,4 +1564,235 @@ platformCommand
|
|
|
1564
1564
|
handleError(err);
|
|
1565
1565
|
}
|
|
1566
1566
|
});
|
|
1567
|
+
async function confirmOrExit(message, skip) {
|
|
1568
|
+
if (skip)
|
|
1569
|
+
return true;
|
|
1570
|
+
const inquirer = await import("inquirer");
|
|
1571
|
+
const { confirm } = await inquirer.default.prompt([
|
|
1572
|
+
{ type: "confirm", name: "confirm", message, default: false },
|
|
1573
|
+
]);
|
|
1574
|
+
return confirm;
|
|
1575
|
+
}
|
|
1576
|
+
const fleetCommand = platformCommand
|
|
1577
|
+
.command("fleet")
|
|
1578
|
+
.description("Manage every agent a template provisioned, as one cohort");
|
|
1579
|
+
fleetCommand
|
|
1580
|
+
.command("status <appId> <templateId>")
|
|
1581
|
+
.description("Show version skew and drift for a template's fleet")
|
|
1582
|
+
.option("--json", "Output as JSON")
|
|
1583
|
+
.action(async (appId, templateId, opts) => {
|
|
1584
|
+
try {
|
|
1585
|
+
requireToken();
|
|
1586
|
+
const f = await api(`/platform/apps/${appId}/fleets/${templateId}`);
|
|
1587
|
+
if (opts.json) {
|
|
1588
|
+
printJson(f);
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
printKeyValue([
|
|
1592
|
+
["Template", `${f.template_name} (${f.template_id})`],
|
|
1593
|
+
["Current version", String(f.current_version)],
|
|
1594
|
+
["Spec hash", f.spec_hash ? f.spec_hash.slice(0, 16) : chalk.dim("(none)")],
|
|
1595
|
+
["Agents", String(f.total_agents)],
|
|
1596
|
+
["On current version", String(f.agents_on_current_version)],
|
|
1597
|
+
[
|
|
1598
|
+
"Behind",
|
|
1599
|
+
f.agents_behind > 0
|
|
1600
|
+
? chalk.yellow(String(f.agents_behind))
|
|
1601
|
+
: String(f.agents_behind),
|
|
1602
|
+
],
|
|
1603
|
+
[
|
|
1604
|
+
"Drifted",
|
|
1605
|
+
f.drifted_agents > 0
|
|
1606
|
+
? chalk.yellow(`${f.drifted_agents} (hand-edited, rollout skips these)`)
|
|
1607
|
+
: "0",
|
|
1608
|
+
],
|
|
1609
|
+
]);
|
|
1610
|
+
if (f.version_skew.length) {
|
|
1611
|
+
console.log("");
|
|
1612
|
+
printTable(f.version_skew.map((b) => ({
|
|
1613
|
+
version: b.template_version === null ? "(unstamped)" : String(b.template_version),
|
|
1614
|
+
agents: String(b.agents),
|
|
1615
|
+
current: b.template_version === f.current_version ? chalk.green("✓") : "",
|
|
1616
|
+
})), [
|
|
1617
|
+
{ key: "version", header: "Provisioned from" },
|
|
1618
|
+
{ key: "agents", header: "Agents" },
|
|
1619
|
+
{ key: "current", header: "Current" },
|
|
1620
|
+
]);
|
|
1621
|
+
}
|
|
1622
|
+
console.log("");
|
|
1623
|
+
console.log(chalk.dim(`Bulk-patchable: ${f.bulk_patchable_fields.join(", ")}`));
|
|
1624
|
+
console.log(chalk.dim("Guardrails and capability flags are not on that list — they stay per-agent."));
|
|
1625
|
+
}
|
|
1626
|
+
catch (err) {
|
|
1627
|
+
handleError(err);
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
fleetCommand
|
|
1631
|
+
.command("agents <appId> <templateId>")
|
|
1632
|
+
.description("List the agents in a fleet")
|
|
1633
|
+
.option("--limit <n>", "Page size (max 500)", "50")
|
|
1634
|
+
.option("--offset <n>", "Offset", "0")
|
|
1635
|
+
.option("--drifted", "Show only agents a rollout has skipped")
|
|
1636
|
+
.option("--json", "Output as JSON")
|
|
1637
|
+
.action(async (appId, templateId, opts) => {
|
|
1638
|
+
try {
|
|
1639
|
+
requireToken();
|
|
1640
|
+
const res = await api(`/platform/apps/${appId}/fleets/${templateId}/agents`, {
|
|
1641
|
+
query: { limit: opts.limit, offset: opts.offset },
|
|
1642
|
+
});
|
|
1643
|
+
let agents = res.agents ?? [];
|
|
1644
|
+
if (opts.drifted)
|
|
1645
|
+
agents = agents.filter((a) => a.drift_fields.length > 0);
|
|
1646
|
+
if (opts.json) {
|
|
1647
|
+
printJson(agents);
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
if (!agents.length) {
|
|
1651
|
+
console.log(chalk.dim("No agents in this fleet."));
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
printTable(agents.map((a) => ({
|
|
1655
|
+
id: a.agent_id,
|
|
1656
|
+
name: a.name,
|
|
1657
|
+
version: a.provisioned_from_version === null
|
|
1658
|
+
? chalk.dim("—")
|
|
1659
|
+
: String(a.provisioned_from_version),
|
|
1660
|
+
current: a.is_current ? chalk.green("✓") : chalk.yellow("behind"),
|
|
1661
|
+
drift: a.drift_fields.length ? chalk.yellow(a.drift_fields.join(",")) : "",
|
|
1662
|
+
active: a.is_active ? "✓" : chalk.dim("paused"),
|
|
1663
|
+
})), [
|
|
1664
|
+
{ key: "id", header: "Agent", width: 36 },
|
|
1665
|
+
{ key: "name", header: "Name", width: 20 },
|
|
1666
|
+
{ key: "version", header: "Ver" },
|
|
1667
|
+
{ key: "current", header: "State" },
|
|
1668
|
+
{ key: "drift", header: "Drift" },
|
|
1669
|
+
{ key: "active", header: "Active" },
|
|
1670
|
+
]);
|
|
1671
|
+
}
|
|
1672
|
+
catch (err) {
|
|
1673
|
+
handleError(err);
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
fleetCommand
|
|
1677
|
+
.command("patch <appId> <templateId>")
|
|
1678
|
+
.description("Set a field on every agent in the fleet")
|
|
1679
|
+
.requiredOption("--set <key=value...>", "Field to set, repeatable. Guardrails and capability flags are refused.")
|
|
1680
|
+
.option("-y, --yes", "Skip confirmation")
|
|
1681
|
+
.option("--json", "Output as JSON")
|
|
1682
|
+
.action(async (appId, templateId, opts) => {
|
|
1683
|
+
try {
|
|
1684
|
+
requireToken();
|
|
1685
|
+
const patch = {};
|
|
1686
|
+
for (const pair of opts.set) {
|
|
1687
|
+
const eq = pair.indexOf("=");
|
|
1688
|
+
if (eq < 1) {
|
|
1689
|
+
console.error(chalk.red(`Not a key=value pair: ${pair}`));
|
|
1690
|
+
process.exitCode = 1;
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
const key = pair.slice(0, eq);
|
|
1694
|
+
const raw = pair.slice(eq + 1);
|
|
1695
|
+
// Only booleans are coerced; everything else stays a string so a
|
|
1696
|
+
// prompt that happens to look like a number is not silently
|
|
1697
|
+
// retyped on its way to a thousand agents.
|
|
1698
|
+
patch[key] = raw === "true" ? true : raw === "false" ? false : raw;
|
|
1699
|
+
}
|
|
1700
|
+
// Say how many agents this touches before asking, not after.
|
|
1701
|
+
const f = await api(`/platform/apps/${appId}/fleets/${templateId}`);
|
|
1702
|
+
const ok = await confirmOrExit(`Set ${Object.keys(patch).join(", ")} on all ${f.total_agents} agent(s) in "${f.template_name}"?`, opts.yes);
|
|
1703
|
+
if (!ok)
|
|
1704
|
+
return;
|
|
1705
|
+
const res = await api(`/platform/apps/${appId}/fleets/${templateId}/bulk-patch`, {
|
|
1706
|
+
method: "POST",
|
|
1707
|
+
body: { patch },
|
|
1708
|
+
});
|
|
1709
|
+
if (opts.json) {
|
|
1710
|
+
printJson(res);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
printSuccess(`Set ${res.fields_applied.join(", ")} on ${res.agents_updated} of ${res.agents_matched} agent(s).`);
|
|
1714
|
+
}
|
|
1715
|
+
catch (err) {
|
|
1716
|
+
handleError(err);
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
fleetCommand
|
|
1720
|
+
.command("rollout <appId> <templateId>")
|
|
1721
|
+
.description("Bring the fleet up to the template's current version")
|
|
1722
|
+
.option("--dry-run", "Report the plan without applying it")
|
|
1723
|
+
.option("--force", "Overwrite hand edits. Still cannot carry a guardrail or capability flag.")
|
|
1724
|
+
.option("-y, --yes", "Skip confirmation")
|
|
1725
|
+
.option("--json", "Output as JSON")
|
|
1726
|
+
.action(async (appId, templateId, opts) => {
|
|
1727
|
+
try {
|
|
1728
|
+
requireToken();
|
|
1729
|
+
if (!opts.dryRun) {
|
|
1730
|
+
const f = await api(`/platform/apps/${appId}/fleets/${templateId}`);
|
|
1731
|
+
const forceNote = opts.force
|
|
1732
|
+
? ` This will OVERWRITE ${f.drifted_agents} hand-edited agent(s).`
|
|
1733
|
+
: "";
|
|
1734
|
+
const ok = await confirmOrExit(`Roll "${f.template_name}" v${f.current_version} out to ${f.total_agents} agent(s)?${forceNote}`, opts.yes);
|
|
1735
|
+
if (!ok)
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
const res = await api(`/platform/apps/${appId}/fleets/${templateId}/rollout`, {
|
|
1739
|
+
method: "POST",
|
|
1740
|
+
body: { force: !!opts.force, dry_run: !!opts.dryRun },
|
|
1741
|
+
});
|
|
1742
|
+
if (opts.json) {
|
|
1743
|
+
printJson(res);
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
if (res.dry_run) {
|
|
1747
|
+
console.log(chalk.cyan("Dry run — nothing was changed."));
|
|
1748
|
+
}
|
|
1749
|
+
printKeyValue([
|
|
1750
|
+
["To version", String(res.to_version)],
|
|
1751
|
+
["Agents", String(res.total_agents)],
|
|
1752
|
+
["Synced", String(res.synced)],
|
|
1753
|
+
["Already current", String(res.already_current)],
|
|
1754
|
+
[
|
|
1755
|
+
"Skipped (drifted)",
|
|
1756
|
+
res.skipped_drifted > 0
|
|
1757
|
+
? chalk.yellow(String(res.skipped_drifted))
|
|
1758
|
+
: "0",
|
|
1759
|
+
],
|
|
1760
|
+
["Job", res.job_id ?? chalk.dim("(none — dry run)")],
|
|
1761
|
+
]);
|
|
1762
|
+
const skipped = res.outcomes.filter((o) => o.outcome === "skipped_drifted");
|
|
1763
|
+
if (skipped.length) {
|
|
1764
|
+
console.log("");
|
|
1765
|
+
console.log(chalk.yellow("Skipped because they were changed outside fleet control:"));
|
|
1766
|
+
for (const s of skipped.slice(0, 20)) {
|
|
1767
|
+
console.log(` ${s.agent_id} ${chalk.dim((s.drift_fields ?? []).join(", "))}`);
|
|
1768
|
+
}
|
|
1769
|
+
if (skipped.length > 20) {
|
|
1770
|
+
console.log(chalk.dim(` … and ${skipped.length - 20} more`));
|
|
1771
|
+
}
|
|
1772
|
+
console.log("");
|
|
1773
|
+
console.log(chalk.dim("Re-run with --force to overwrite them."));
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
catch (err) {
|
|
1777
|
+
handleError(err);
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
fleetCommand
|
|
1781
|
+
.command("pause <appId> <templateId>")
|
|
1782
|
+
.description("Deactivate every agent in the fleet")
|
|
1783
|
+
.option("-y, --yes", "Skip confirmation")
|
|
1784
|
+
.action(async (appId, templateId, opts) => {
|
|
1785
|
+
try {
|
|
1786
|
+
requireToken();
|
|
1787
|
+
const f = await api(`/platform/apps/${appId}/fleets/${templateId}`);
|
|
1788
|
+
const ok = await confirmOrExit(`Deactivate all ${f.total_agents} agent(s) in "${f.template_name}"? They stop immediately.`, opts.yes);
|
|
1789
|
+
if (!ok)
|
|
1790
|
+
return;
|
|
1791
|
+
const res = await api(`/platform/apps/${appId}/fleets/${templateId}/pause`, { method: "POST", body: {} });
|
|
1792
|
+
printSuccess(`Paused ${res.agents_paused} agent(s).`);
|
|
1793
|
+
}
|
|
1794
|
+
catch (err) {
|
|
1795
|
+
handleError(err);
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1567
1798
|
//# sourceMappingURL=platform.js.map
|