feat: add system cli
This commit is contained in:
@@ -8,6 +8,7 @@ Docs: https://docs.clawd.bot
|
||||
- Plugins: add optional llm-task JSON-only tool for workflows. (#1498) Thanks @vignesh07.
|
||||
- CLI: restart the gateway by default after `clawdbot update`; add `--no-restart` to skip it.
|
||||
- CLI: add live auth probes to `clawdbot models status` for per-profile verification.
|
||||
- CLI: add `clawdbot system` for system events + heartbeat controls; remove standalone `wake`.
|
||||
- Agents: add Bedrock auto-discovery defaults + config overrides. (#1553) Thanks @fal3.
|
||||
- Docs: add cron vs heartbeat decision guide (with Lobster workflow notes). (#1533) Thanks @JustYannicc.
|
||||
- Markdown: add per-channel table conversion (bullets for Signal/WhatsApp, code blocks elsewhere). (#1495) Thanks @odysseus0.
|
||||
|
||||
@@ -263,15 +263,15 @@ Run history:
|
||||
clawdbot cron runs --id <jobId> --limit 50
|
||||
```
|
||||
|
||||
Immediate wake without creating a job:
|
||||
Immediate system event without creating a job:
|
||||
```bash
|
||||
clawdbot wake --mode now --text "Next heartbeat: check battery."
|
||||
clawdbot system event --mode now --text "Next heartbeat: check battery."
|
||||
```
|
||||
|
||||
## Gateway API surface
|
||||
- `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove`
|
||||
- `cron.run` (force or due), `cron.runs`
|
||||
- `wake` (enqueue system event + optional heartbeat)
|
||||
For immediate system events without a job, use [`clawdbot system event`](/cli/system).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -271,4 +271,4 @@ clawdbot cron add \
|
||||
|
||||
- [Heartbeat](/gateway/heartbeat) - full heartbeat configuration
|
||||
- [Cron jobs](/automation/cron-jobs) - full cron CLI and API reference
|
||||
- [Wake](/cli/wake) - manual wake command
|
||||
- [System](/cli/system) - system events + heartbeat controls
|
||||
|
||||
@@ -29,6 +29,7 @@ This page describes the current CLI behavior. If commands change, update this do
|
||||
- [`sessions`](/cli/sessions)
|
||||
- [`gateway`](/cli/gateway)
|
||||
- [`logs`](/cli/logs)
|
||||
- [`system`](/cli/system)
|
||||
- [`models`](/cli/models)
|
||||
- [`memory`](/cli/memory)
|
||||
- [`nodes`](/cli/nodes)
|
||||
@@ -38,7 +39,6 @@ This page describes the current CLI behavior. If commands change, update this do
|
||||
- [`sandbox`](/cli/sandbox)
|
||||
- [`tui`](/cli/tui)
|
||||
- [`browser`](/cli/browser)
|
||||
- [`wake`](/cli/wake)
|
||||
- [`cron`](/cli/cron)
|
||||
- [`dns`](/cli/dns)
|
||||
- [`docs`](/cli/docs)
|
||||
@@ -145,6 +145,10 @@ clawdbot [--dev] [--profile <name>] <command>
|
||||
restart
|
||||
run
|
||||
logs
|
||||
system
|
||||
event
|
||||
heartbeat last|enable|disable
|
||||
presence
|
||||
models
|
||||
list
|
||||
status
|
||||
@@ -160,7 +164,6 @@ clawdbot [--dev] [--profile <name>] <command>
|
||||
list
|
||||
recreate
|
||||
explain
|
||||
wake
|
||||
cron
|
||||
status
|
||||
list
|
||||
@@ -763,9 +766,9 @@ Options:
|
||||
- `set`: `--provider <name>`, `--agent <id>`, `<profileIds...>`
|
||||
- `clear`: `--provider <name>`, `--agent <id>`
|
||||
|
||||
## Cron + wake
|
||||
## System
|
||||
|
||||
### `wake`
|
||||
### `system event`
|
||||
Enqueue a system event and optionally trigger a heartbeat (Gateway RPC).
|
||||
|
||||
Required:
|
||||
@@ -776,7 +779,21 @@ Options:
|
||||
- `--json`
|
||||
- `--url`, `--token`, `--timeout`, `--expect-final`
|
||||
|
||||
### `cron`
|
||||
### `system heartbeat last|enable|disable`
|
||||
Heartbeat controls (Gateway RPC).
|
||||
|
||||
Options:
|
||||
- `--json`
|
||||
- `--url`, `--token`, `--timeout`, `--expect-final`
|
||||
|
||||
### `system presence`
|
||||
List system presence entries (Gateway RPC).
|
||||
|
||||
Options:
|
||||
- `--json`
|
||||
- `--url`, `--token`, `--timeout`, `--expect-final`
|
||||
|
||||
## Cron
|
||||
Manage scheduled jobs (Gateway RPC). See [/automation/cron-jobs](/automation/cron-jobs).
|
||||
|
||||
Subcommands:
|
||||
|
||||
55
docs/cli/system.md
Normal file
55
docs/cli/system.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
summary: "CLI reference for `clawdbot system` (system events, heartbeat, presence)"
|
||||
read_when:
|
||||
- You want to enqueue a system event without creating a cron job
|
||||
- You need to enable or disable heartbeats
|
||||
- You want to inspect system presence entries
|
||||
---
|
||||
|
||||
# `clawdbot system`
|
||||
|
||||
System-level helpers for the Gateway: enqueue system events, control heartbeats,
|
||||
and view presence.
|
||||
|
||||
## Common commands
|
||||
|
||||
```bash
|
||||
clawdbot system event --text "Check for urgent follow-ups" --mode now
|
||||
clawdbot system heartbeat enable
|
||||
clawdbot system heartbeat last
|
||||
clawdbot system presence
|
||||
```
|
||||
|
||||
## `system event`
|
||||
|
||||
Enqueue a system event on the **main** session. The next heartbeat will inject
|
||||
it as a `System:` line in the prompt. Use `--mode now` to trigger the heartbeat
|
||||
immediately; `next-heartbeat` waits for the next scheduled tick.
|
||||
|
||||
Flags:
|
||||
- `--text <text>`: required system event text.
|
||||
- `--mode <mode>`: `now` or `next-heartbeat` (default).
|
||||
- `--json`: machine-readable output.
|
||||
|
||||
## `system heartbeat last|enable|disable`
|
||||
|
||||
Heartbeat controls:
|
||||
- `last`: show the last heartbeat event.
|
||||
- `enable`: turn heartbeats back on (use this if they were disabled).
|
||||
- `disable`: pause heartbeats.
|
||||
|
||||
Flags:
|
||||
- `--json`: machine-readable output.
|
||||
|
||||
## `system presence`
|
||||
|
||||
List the current system presence entries the Gateway knows about (nodes,
|
||||
instances, and similar status lines).
|
||||
|
||||
Flags:
|
||||
- `--json`: machine-readable output.
|
||||
|
||||
## Notes
|
||||
|
||||
- Requires a running Gateway reachable by your current config (local or remote).
|
||||
- System events are ephemeral and not persisted across restarts.
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
summary: "CLI reference for `clawdbot wake` (enqueue a system event and optionally trigger an immediate heartbeat)"
|
||||
read_when:
|
||||
- You want to “poke” a running Gateway to process a system event
|
||||
- You use `wake` with cron jobs or remote nodes
|
||||
---
|
||||
|
||||
# `clawdbot wake`
|
||||
|
||||
Enqueue a system event on the Gateway and optionally trigger an immediate heartbeat.
|
||||
|
||||
This is a lightweight “poke” for automation flows where you don’t want to run a full command, but you do want the Gateway to react quickly.
|
||||
|
||||
Related:
|
||||
- Cron jobs: [Cron](/cli/cron)
|
||||
- Gateway heartbeat: [Heartbeat](/gateway/heartbeat)
|
||||
|
||||
## Common commands
|
||||
|
||||
```bash
|
||||
clawdbot wake --text "sync"
|
||||
clawdbot wake --text "sync" --mode now
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
- `--text <text>`: system event text.
|
||||
- `--mode <mode>`: `now` or `next-heartbeat` (default).
|
||||
- `--json`: machine-readable output.
|
||||
|
||||
## Notes
|
||||
|
||||
- Requires a running Gateway reachable by your current config (local or remote).
|
||||
- If you’re using sandboxing, `wake` still targets the Gateway; sandboxing does not block the command itself.
|
||||
|
||||
@@ -850,12 +850,12 @@
|
||||
"cli/memory",
|
||||
"cli/models",
|
||||
"cli/logs",
|
||||
"cli/system",
|
||||
"cli/nodes",
|
||||
"cli/approvals",
|
||||
"cli/gateway",
|
||||
"cli/tui",
|
||||
"cli/voicecall",
|
||||
"cli/wake",
|
||||
"cli/cron",
|
||||
"cli/dns",
|
||||
"cli/docs",
|
||||
|
||||
@@ -195,7 +195,7 @@ Safety note: don’t put secrets (API keys, phone numbers, private tokens) into
|
||||
You can enqueue a system event and trigger an immediate heartbeat with:
|
||||
|
||||
```bash
|
||||
clawdbot wake --text "Check for urgent follow-ups" --mode now
|
||||
clawdbot system event --text "Check for urgent follow-ups" --mode now
|
||||
```
|
||||
|
||||
If multiple agents have `heartbeat` configured, a manual wake runs each of those
|
||||
|
||||
@@ -8,11 +8,8 @@ import {
|
||||
} from "./register.cron-add.js";
|
||||
import { registerCronEditCommand } from "./register.cron-edit.js";
|
||||
import { registerCronSimpleCommands } from "./register.cron-simple.js";
|
||||
import { registerWakeCommand } from "./register.wake.js";
|
||||
|
||||
export function registerCronCli(program: Command) {
|
||||
registerWakeCommand(program);
|
||||
|
||||
const cron = program
|
||||
.command("cron")
|
||||
.description("Manage cron jobs (via Gateway)")
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { Command } from "commander";
|
||||
import { danger } from "../../globals.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import type { GatewayRpcOpts } from "../gateway-rpc.js";
|
||||
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
|
||||
|
||||
export function registerWakeCommand(program: Command) {
|
||||
addGatewayClientOptions(
|
||||
program
|
||||
.command("wake")
|
||||
.description("Enqueue a system event and optionally trigger an immediate heartbeat")
|
||||
.requiredOption("--text <text>", "System event text")
|
||||
.option("--mode <mode>", "Wake mode (now|next-heartbeat)", "next-heartbeat")
|
||||
.option("--json", "Output JSON", false),
|
||||
)
|
||||
.addHelpText(
|
||||
"after",
|
||||
() => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/wake", "docs.clawd.bot/cli/wake")}\n`,
|
||||
)
|
||||
.action(async (opts: GatewayRpcOpts & { text?: string; mode?: string }) => {
|
||||
try {
|
||||
const result = await callGatewayFromCli(
|
||||
"wake",
|
||||
opts,
|
||||
{ mode: opts.mode, text: opts.text },
|
||||
{ expectFinal: false },
|
||||
);
|
||||
if (opts.json) defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
else defaultRuntime.log("ok");
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -60,6 +60,14 @@ const entries: SubCliEntry[] = [
|
||||
mod.registerLogsCli(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "system",
|
||||
description: "System events, heartbeat, and presence",
|
||||
register: async (program) => {
|
||||
const mod = await import("../system-cli.js");
|
||||
mod.registerSystemCli(program);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "models",
|
||||
description: "Model configuration",
|
||||
|
||||
125
src/cli/system-cli.ts
Normal file
125
src/cli/system-cli.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { Command } from "commander";
|
||||
|
||||
import { danger } from "../globals.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import type { GatewayRpcOpts } from "./gateway-rpc.js";
|
||||
import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js";
|
||||
|
||||
type SystemEventOpts = GatewayRpcOpts & { text?: string; mode?: string; json?: boolean };
|
||||
|
||||
const normalizeWakeMode = (raw: unknown) => {
|
||||
const mode = typeof raw === "string" ? raw.trim() : "";
|
||||
if (!mode) return "next-heartbeat" as const;
|
||||
if (mode === "now" || mode === "next-heartbeat") return mode;
|
||||
throw new Error("--mode must be now or next-heartbeat");
|
||||
};
|
||||
|
||||
export function registerSystemCli(program: Command) {
|
||||
const system = program
|
||||
.command("system")
|
||||
.description("System tools (events, heartbeat, presence)")
|
||||
.addHelpText(
|
||||
"after",
|
||||
() =>
|
||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/system", "docs.clawd.bot/cli/system")}\n`,
|
||||
);
|
||||
|
||||
addGatewayClientOptions(
|
||||
system
|
||||
.command("event")
|
||||
.description("Enqueue a system event and optionally trigger a heartbeat")
|
||||
.requiredOption("--text <text>", "System event text")
|
||||
.option("--mode <mode>", "Wake mode (now|next-heartbeat)", "next-heartbeat")
|
||||
.option("--json", "Output JSON", false),
|
||||
).action(async (opts: SystemEventOpts) => {
|
||||
try {
|
||||
const text = typeof opts.text === "string" ? opts.text.trim() : "";
|
||||
if (!text) throw new Error("--text is required");
|
||||
const mode = normalizeWakeMode(opts.mode);
|
||||
const result = await callGatewayFromCli("wake", opts, { mode, text }, { expectFinal: false });
|
||||
if (opts.json) defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
else defaultRuntime.log("ok");
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
const heartbeat = system.command("heartbeat").description("Heartbeat controls");
|
||||
|
||||
addGatewayClientOptions(
|
||||
heartbeat
|
||||
.command("last")
|
||||
.description("Show the last heartbeat event")
|
||||
.option("--json", "Output JSON", false),
|
||||
).action(async (opts: GatewayRpcOpts & { json?: boolean }) => {
|
||||
try {
|
||||
const result = await callGatewayFromCli("last-heartbeat", opts, undefined, {
|
||||
expectFinal: false,
|
||||
});
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
addGatewayClientOptions(
|
||||
heartbeat
|
||||
.command("enable")
|
||||
.description("Enable heartbeats")
|
||||
.option("--json", "Output JSON", false),
|
||||
).action(async (opts: GatewayRpcOpts & { json?: boolean }) => {
|
||||
try {
|
||||
const result = await callGatewayFromCli(
|
||||
"set-heartbeats",
|
||||
opts,
|
||||
{ enabled: true },
|
||||
{ expectFinal: false },
|
||||
);
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
addGatewayClientOptions(
|
||||
heartbeat
|
||||
.command("disable")
|
||||
.description("Disable heartbeats")
|
||||
.option("--json", "Output JSON", false),
|
||||
).action(async (opts: GatewayRpcOpts & { json?: boolean }) => {
|
||||
try {
|
||||
const result = await callGatewayFromCli(
|
||||
"set-heartbeats",
|
||||
opts,
|
||||
{ enabled: false },
|
||||
{ expectFinal: false },
|
||||
);
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
addGatewayClientOptions(
|
||||
system
|
||||
.command("presence")
|
||||
.description("List system presence entries")
|
||||
.option("--json", "Output JSON", false),
|
||||
).action(async (opts: GatewayRpcOpts & { json?: boolean }) => {
|
||||
try {
|
||||
const result = await callGatewayFromCli("system-presence", opts, undefined, {
|
||||
expectFinal: false,
|
||||
});
|
||||
defaultRuntime.log(JSON.stringify(result, null, 2));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user