refactor: normalize cli command hints
This commit is contained in:
@@ -117,8 +117,8 @@ Send these as standalone messages so they register.
|
||||
```
|
||||
|
||||
## Inspecting
|
||||
- `pnpm clawdbot status` — shows store path and recent sessions.
|
||||
- `pnpm clawdbot sessions --json` — dumps every entry (filter with `--active <minutes>`).
|
||||
- `clawdbot status` — shows store path and recent sessions.
|
||||
- `clawdbot sessions --json` — dumps every entry (filter with `--active <minutes>`).
|
||||
- `clawdbot gateway call sessions.list --params '{}'` — fetch sessions from the running gateway (use `--url`/`--token` for remote gateway access).
|
||||
- Send `/status` as a standalone message in chat to see whether the agent is reachable, how much of the session context is used, current thinking/verbose toggles, and when your WhatsApp web creds were last refreshed (helps spot relink needs).
|
||||
- Send `/context list` or `/context detail` to see what’s in the system prompt and injected workspace files (and the biggest context contributors).
|
||||
|
||||
@@ -48,7 +48,7 @@ node --import tsx scripts/repro/tsx-name-repro.ts
|
||||
|
||||
## Regression history
|
||||
- `2871657e` (2026-01-06): scripts changed from Bun to tsx to make Bun optional.
|
||||
- Before that (Bun path), `pnpm clawdbot status` and `gateway:watch` worked.
|
||||
- Before that (Bun path), `clawdbot status` and `gateway:watch` worked.
|
||||
|
||||
## Workarounds
|
||||
- Use Bun for dev scripts (current temporary revert).
|
||||
|
||||
@@ -59,9 +59,11 @@ Recommended flow (dev profile + dev bootstrap):
|
||||
|
||||
```bash
|
||||
pnpm gateway:dev
|
||||
CLAWDBOT_PROFILE=dev pnpm clawdbot tui
|
||||
CLAWDBOT_PROFILE=dev clawdbot tui
|
||||
```
|
||||
|
||||
If you don’t have a global install yet, run the CLI via `pnpm clawdbot ...`.
|
||||
|
||||
What this does:
|
||||
|
||||
1) **Profile isolation** (global `--dev`)
|
||||
@@ -89,7 +91,7 @@ Note: `--dev` is a **global** profile flag and gets eaten by some runners.
|
||||
If you need to spell it out, use the env var form:
|
||||
|
||||
```bash
|
||||
CLAWDBOT_PROFILE=dev pnpm clawdbot gateway --dev --reset
|
||||
CLAWDBOT_PROFILE=dev clawdbot gateway --dev --reset
|
||||
```
|
||||
|
||||
`--reset` wipes config, credentials, sessions, and the dev workspace (using
|
||||
|
||||
@@ -378,7 +378,7 @@ clawdbot channels login
|
||||
### Build errors on `main` — what’s the standard fix path?
|
||||
|
||||
1) `git pull origin main && pnpm install`
|
||||
2) `pnpm clawdbot doctor`
|
||||
2) `clawdbot doctor`
|
||||
3) Check GitHub issues or Discord
|
||||
4) Temporary workaround: check out an older commit
|
||||
|
||||
@@ -392,7 +392,7 @@ Typical recovery:
|
||||
git status # ensure you’re in the repo root
|
||||
pnpm install
|
||||
pnpm build
|
||||
pnpm clawdbot doctor
|
||||
clawdbot doctor
|
||||
clawdbot daemon restart
|
||||
```
|
||||
|
||||
|
||||
@@ -123,9 +123,11 @@ cd clawdbot
|
||||
pnpm install
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
pnpm build
|
||||
pnpm clawdbot onboard --install-daemon
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
|
||||
If you don’t have a global install yet, run the onboarding step via `pnpm clawdbot ...` from the repo.
|
||||
|
||||
Multi-instance quickstart (optional):
|
||||
|
||||
```bash
|
||||
|
||||
@@ -7,40 +7,43 @@ read_when:
|
||||
|
||||
# Install
|
||||
|
||||
Runtime baseline: **Node >=22**.
|
||||
Use the installer unless you have a reason not to. It sets up the CLI and runs onboarding.
|
||||
|
||||
If the installer says it succeeded but you later see `clawdbot: command not found`, it’s usually a Node/npm PATH issue (global npm bin dir not on PATH). See the section below.
|
||||
|
||||
## Node.js + npm (PATH sanity)
|
||||
|
||||
Quick diagnosis:
|
||||
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
npm bin -g
|
||||
echo "$PATH"
|
||||
```
|
||||
|
||||
If the output of `npm bin -g` is **not** present inside `echo "$PATH"`, your shell can’t find global npm binaries (including `clawdbot`).
|
||||
|
||||
Fix: add it to your shell startup file (zsh: `~/.zshrc`, bash: `~/.bashrc`):
|
||||
|
||||
```bash
|
||||
export PATH="/path/from/npm/bin/-g:$PATH"
|
||||
```
|
||||
|
||||
Then open a new terminal (or `rehash` in zsh / `hash -r` in bash).
|
||||
|
||||
## Recommended (installer script)
|
||||
## Quick install (recommended)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://clawd.bot/install.sh | bash
|
||||
```
|
||||
|
||||
This installs the `clawdbot` CLI globally via npm and then starts onboarding.
|
||||
Windows (PowerShell):
|
||||
|
||||
See installer flags:
|
||||
```powershell
|
||||
iwr -useb https://clawd.bot/install.ps1 | iex
|
||||
```
|
||||
|
||||
Next step (if you skipped onboarding):
|
||||
|
||||
```bash
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
|
||||
## System requirements
|
||||
|
||||
- **Node >=22**
|
||||
- macOS, Linux, or Windows via WSL2
|
||||
- `pnpm` only if you build from source
|
||||
|
||||
## Choose your install path
|
||||
|
||||
### 1) Installer script (recommended)
|
||||
|
||||
Installs `clawdbot` globally via npm and runs onboarding.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://clawd.bot/install.sh | bash
|
||||
```
|
||||
|
||||
Installer flags:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://clawd.bot/install.sh | bash -s -- --help
|
||||
@@ -54,7 +57,60 @@ Non-interactive (skip onboarding):
|
||||
curl -fsSL https://clawd.bot/install.sh | bash -s -- --no-onboard
|
||||
```
|
||||
|
||||
## Install method: npm vs git
|
||||
### 2) Global install (manual)
|
||||
|
||||
If you already have Node:
|
||||
|
||||
```bash
|
||||
npm install -g clawdbot@latest
|
||||
```
|
||||
|
||||
If you have libvips installed globally (common on macOS via Homebrew) and `sharp` fails to install, force prebuilt binaries:
|
||||
|
||||
```bash
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install -g clawdbot@latest
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```bash
|
||||
pnpm add -g clawdbot@latest
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
|
||||
### 3) From source (contributors/dev)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/clawdbot/clawdbot.git
|
||||
cd clawdbot
|
||||
pnpm install
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
pnpm build
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
|
||||
Tip: if you don’t have a global install yet, run repo commands via `pnpm clawdbot ...`.
|
||||
|
||||
### 4) Other install options
|
||||
|
||||
- Docker: [Docker](/install/docker)
|
||||
- Nix: [Nix](/install/nix)
|
||||
- Ansible: [Ansible](/install/ansible)
|
||||
- Bun (CLI only): [Bun](/install/bun)
|
||||
|
||||
## After install
|
||||
|
||||
- Run onboarding: `clawdbot onboard --install-daemon`
|
||||
- Quick check: `clawdbot doctor`
|
||||
- Check gateway health: `clawdbot status` + `clawdbot health`
|
||||
- Open the dashboard: `clawdbot dashboard`
|
||||
|
||||
## Install method: npm vs git (installer)
|
||||
|
||||
The installer supports two methods:
|
||||
|
||||
@@ -92,28 +148,28 @@ Equivalent env vars (useful for automation):
|
||||
- `CLAWDBOT_NO_ONBOARD=1`
|
||||
- `SHARP_IGNORE_GLOBAL_LIBVIPS=0|1` (default: `1`; avoids `sharp` building against system libvips)
|
||||
|
||||
## Global install (manual)
|
||||
## Troubleshooting: `clawdbot` not found (PATH)
|
||||
|
||||
If you already have Node:
|
||||
Quick diagnosis:
|
||||
|
||||
```bash
|
||||
npm install -g clawdbot@latest
|
||||
node -v
|
||||
npm -v
|
||||
npm bin -g
|
||||
echo "$PATH"
|
||||
```
|
||||
|
||||
If you have libvips installed globally (common on macOS via Homebrew) and `sharp` fails to install, force prebuilt binaries:
|
||||
If the output of `npm bin -g` is **not** present inside `echo "$PATH"`, your shell can’t find global npm binaries (including `clawdbot`).
|
||||
|
||||
Fix: add it to your shell startup file (zsh: `~/.zshrc`, bash: `~/.bashrc`):
|
||||
|
||||
```bash
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install -g clawdbot@latest
|
||||
export PATH="/path/from/npm/bin/-g:$PATH"
|
||||
```
|
||||
|
||||
Or:
|
||||
Then open a new terminal (or `rehash` in zsh / `hash -r` in bash).
|
||||
|
||||
```bash
|
||||
pnpm add -g clawdbot@latest
|
||||
```
|
||||
## Update / uninstall
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
- Updates: [Updating](/install/updating)
|
||||
- Uninstall: [Uninstall](/install/uninstall)
|
||||
|
||||
@@ -118,7 +118,7 @@ Remove it with `npm rm -g clawdbot` (or `pnpm remove -g` / `bun remove -g` if yo
|
||||
|
||||
### Source checkout (git clone)
|
||||
|
||||
If you run from a repo checkout (`git clone` + `pnpm clawdbot ...` / `bun run clawdbot ...`):
|
||||
If you run from a repo checkout (`git clone` + `clawdbot ...` / `bun run clawdbot ...`):
|
||||
|
||||
1) Uninstall the gateway service **before** deleting the repo (use the easy path above or manual service removal).
|
||||
2) Delete the repo directory.
|
||||
|
||||
@@ -119,12 +119,13 @@ git pull
|
||||
pnpm install
|
||||
pnpm build
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
pnpm clawdbot doctor
|
||||
pnpm clawdbot health
|
||||
clawdbot doctor
|
||||
clawdbot health
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `pnpm build` matters when you run the packaged `clawdbot` binary ([`dist/entry.js`](https://github.com/clawdbot/clawdbot/blob/main/dist/entry.js)) or use Node to run `dist/`.
|
||||
- If you run from a repo checkout without a global install, use `pnpm clawdbot ...` for CLI commands.
|
||||
- If you run directly from TypeScript (`pnpm clawdbot ...`), a rebuild is usually unnecessary, but **config migrations still apply** → run doctor.
|
||||
- Switching between global and git installs is easy: install the other flavor, then run `clawdbot doctor` so the gateway service entrypoint is rewritten to the current install.
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ Options:
|
||||
- `--timeout <ms>`: overall discovery window (default `2000`)
|
||||
- `--json`: structured output for diffing
|
||||
|
||||
Tip: compare against `pnpm clawdbot gateway discover --json` to see whether the
|
||||
Tip: compare against `clawdbot gateway discover --json` to see whether the
|
||||
macOS app’s discovery pipeline (NWBrowser + tailnet DNS‑SD fallback) differs from
|
||||
the Node CLI’s `dns-sd` based discovery.
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ cd clawdbot
|
||||
pnpm install
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
pnpm build
|
||||
pnpm clawdbot onboard
|
||||
clawdbot onboard
|
||||
```
|
||||
|
||||
Full guide: [Getting Started](/start/getting-started)
|
||||
|
||||
@@ -184,22 +184,25 @@ Clawdbot is a personal AI assistant you run on your own devices. It replies on t
|
||||
The repo recommends running from source and using the onboarding wizard:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/clawdbot/clawdbot.git
|
||||
cd clawdbot
|
||||
|
||||
pnpm install
|
||||
|
||||
# Optional if you want built output / global linking:
|
||||
pnpm build
|
||||
|
||||
# If the Control UI assets are missing or you want the dashboard:
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
|
||||
pnpm clawdbot onboard
|
||||
curl -fsSL https://clawd.bot/install.sh | bash
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
|
||||
The wizard can also build UI assets automatically. After onboarding, you typically run the Gateway on port **18789**.
|
||||
|
||||
From source (contributors/dev):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/clawdbot/clawdbot.git
|
||||
cd clawdbot
|
||||
pnpm install
|
||||
pnpm build
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
clawdbot onboard
|
||||
```
|
||||
|
||||
If you don’t have a global install yet, run it via `pnpm clawdbot onboard`.
|
||||
|
||||
### How do I open the dashboard after onboarding?
|
||||
|
||||
The wizard now opens your browser with a tokenized dashboard URL right after onboarding and also prints the full link (with token) in the summary. Keep that tab open; if it didn’t launch, copy/paste the printed URL on the same machine. Tokens stay local to your host—nothing is fetched from the browser.
|
||||
@@ -330,7 +333,7 @@ git clone https://github.com/clawdbot/clawdbot.git
|
||||
cd clawdbot
|
||||
pnpm install
|
||||
pnpm build
|
||||
pnpm clawdbot doctor
|
||||
clawdbot doctor
|
||||
clawdbot daemon restart
|
||||
```
|
||||
|
||||
|
||||
@@ -116,6 +116,13 @@ If a token is configured, paste it into the Control UI settings (stored as `conn
|
||||
⚠️ **Bun warning (WhatsApp + Telegram):** Bun has known issues with these
|
||||
channels. If you use WhatsApp or Telegram, run the Gateway with **Node**.
|
||||
|
||||
## 3.5) Quick verify (2 min)
|
||||
|
||||
```bash
|
||||
clawdbot status
|
||||
clawdbot health
|
||||
```
|
||||
|
||||
## 4) Pair + connect your first chat surface
|
||||
|
||||
### WhatsApp (QR login)
|
||||
@@ -158,9 +165,11 @@ cd clawdbot
|
||||
pnpm install
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
pnpm build
|
||||
pnpm clawdbot onboard --install-daemon
|
||||
clawdbot onboard --install-daemon
|
||||
```
|
||||
|
||||
If you don’t have a global install yet, run the onboarding step via `pnpm clawdbot ...` from the repo.
|
||||
|
||||
Gateway (from this repo):
|
||||
|
||||
```bash
|
||||
@@ -169,15 +178,13 @@ node dist/entry.js gateway --port 18789 --verbose
|
||||
|
||||
## 7) Verify end-to-end
|
||||
|
||||
In a new terminal:
|
||||
In a new terminal, send a test message:
|
||||
|
||||
```bash
|
||||
clawdbot status
|
||||
clawdbot health
|
||||
clawdbot message send --target +15555550123 --message "Hello from Clawdbot"
|
||||
```
|
||||
|
||||
If `health` shows “no auth configured”, go back to the wizard and set OAuth/key auth — the agent won’t be able to respond without it.
|
||||
If `clawdbot health` shows “no auth configured”, go back to the wizard and set OAuth/key auth — the agent won’t be able to respond without it.
|
||||
|
||||
Tip: `clawdbot status --all` is the best pasteable, read-only debug report.
|
||||
Health probes: `clawdbot health` (or `clawdbot status --deep`) asks the running gateway for a health snapshot.
|
||||
|
||||
@@ -35,9 +35,11 @@ clawdbot setup
|
||||
From inside this repo, use the local CLI entry:
|
||||
|
||||
```bash
|
||||
pnpm clawdbot setup
|
||||
clawdbot setup
|
||||
```
|
||||
|
||||
If you don’t have a global install yet, run it via `pnpm clawdbot setup`.
|
||||
|
||||
## Stable workflow (macOS app first)
|
||||
|
||||
1) Install + launch **Clawdbot.app** (menu bar).
|
||||
@@ -92,7 +94,7 @@ The app will attach to the running gateway on the configured port.
|
||||
- Or via CLI:
|
||||
|
||||
```bash
|
||||
pnpm clawdbot health
|
||||
clawdbot health
|
||||
```
|
||||
|
||||
### Common footguns
|
||||
|
||||
@@ -150,8 +150,8 @@ Live tests are split into two layers so we can isolate failures:
|
||||
Tip: to see what you can test on your machine (and the exact `provider/model` ids), run:
|
||||
|
||||
```bash
|
||||
pnpm clawdbot models list
|
||||
pnpm clawdbot models list --json
|
||||
clawdbot models list
|
||||
clawdbot models list --json
|
||||
```
|
||||
|
||||
## Live: Anthropic setup-token smoke
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { normalizeProviderId } from "../model-selection.js";
|
||||
import { listProfilesForProvider } from "./profiles.js";
|
||||
@@ -37,6 +38,6 @@ export function formatAuthDoctorHint(params: {
|
||||
}`,
|
||||
`- auth store oauth profiles: ${storeOauthProfiles || "(none)"}`,
|
||||
`- suggested profile: ${suggested}`,
|
||||
'Fix: run "clawdbot doctor --yes"',
|
||||
`Fix: run "${formatCliCommand("clawdbot doctor --yes")}"`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { type Api, getEnvApiKey, type Model } from "@mariozechner/pi-ai";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { ModelProviderConfig } from "../config/types.js";
|
||||
import { getShellEnvAppliedKeys } from "../infra/shell-env.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
type AuthProfileStore,
|
||||
ensureAuthProfileStore,
|
||||
@@ -103,7 +104,7 @@ export async function resolveApiKeyForProvider(params: {
|
||||
[
|
||||
`No API key found for provider "${provider}".`,
|
||||
`Auth store: ${authStorePath} (agentDir: ${resolvedAgentDir}).`,
|
||||
"Configure auth for this agent (clawdbot agents add <id>) or copy auth-profiles.json from the main agentDir.",
|
||||
`Configure auth for this agent (${formatCliCommand("clawdbot agents add <id>")}) or copy auth-profiles.json from the main agentDir.`,
|
||||
].join(" "),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { DEFAULT_SANDBOX_IMAGE, SANDBOX_AGENT_WORKSPACE_MOUNT } from "./constants.js";
|
||||
import { readRegistry, updateRegistry } from "./registry.js";
|
||||
import { computeSandboxConfigHash } from "./config-hash.js";
|
||||
@@ -214,13 +215,13 @@ async function readContainerConfigHash(containerName: string): Promise<string |
|
||||
|
||||
function formatSandboxRecreateHint(params: { scope: SandboxConfig["scope"]; sessionKey: string }) {
|
||||
if (params.scope === "session") {
|
||||
return `clawdbot sandbox recreate --session ${params.sessionKey}`;
|
||||
return formatCliCommand(`clawdbot sandbox recreate --session ${params.sessionKey}`);
|
||||
}
|
||||
if (params.scope === "agent") {
|
||||
const agentId = resolveSandboxAgentId(params.sessionKey) ?? "main";
|
||||
return `clawdbot sandbox recreate --agent ${agentId}`;
|
||||
return formatCliCommand(`clawdbot sandbox recreate --agent ${agentId}`);
|
||||
}
|
||||
return "clawdbot sandbox recreate --all";
|
||||
return formatCliCommand("clawdbot sandbox recreate --all");
|
||||
}
|
||||
|
||||
export async function ensureSandboxContainer(params: {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { canonicalizeMainSessionAlias, resolveAgentMainSessionKey } from "../../config/sessions.js";
|
||||
import { resolveSessionAgentId } from "../agent-scope.js";
|
||||
import { expandToolGroups } from "../tool-policy.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { resolveSandboxConfigForAgent } from "./config.js";
|
||||
import { resolveSandboxToolPolicyForAgent } from "./tool-policy.js";
|
||||
import type { SandboxConfig, SandboxToolPolicyResolved } from "./types.js";
|
||||
@@ -115,7 +116,9 @@ export function formatSandboxToolPolicyBlockedMessage(params: {
|
||||
if (runtime.mode === "non-main") {
|
||||
lines.push(`- Use main session key (direct): ${runtime.mainSessionKey}`);
|
||||
}
|
||||
lines.push(`- See: clawdbot sandbox explain --session ${runtime.sessionKey}`);
|
||||
lines.push(
|
||||
`- See: ${formatCliCommand(`clawdbot sandbox explain --session ${runtime.sessionKey}`)}`,
|
||||
);
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ReasoningLevel, ThinkLevel } from "../auto-reply/thinking.js";
|
||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { listDeliverableMessageChannels } from "../utils/message-channel.js";
|
||||
import type { ResolvedTimeFormat } from "./date-time.js";
|
||||
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
|
||||
@@ -124,7 +125,7 @@ function buildDocsSection(params: { docsPath?: string; isMinimal: boolean; readT
|
||||
"Community: https://discord.com/invite/clawd",
|
||||
"Find new skills: https://clawdhub.com",
|
||||
"For Clawdbot behavior, commands, config, or architecture: consult local docs first.",
|
||||
"When diagnosing issues, run `clawdbot status` yourself when possible; only ask the user if you lack access (e.g., sandboxed).",
|
||||
`When diagnosing issues, run \`${formatCliCommand("clawdbot status")}\` yourself when possible; only ask the user if you lack access (e.g., sandboxed).`,
|
||||
"",
|
||||
];
|
||||
}
|
||||
@@ -364,11 +365,11 @@ export function buildAgentSystemPrompt(params: {
|
||||
"## Clawdbot CLI Quick Reference",
|
||||
"Clawdbot is controlled via subcommands. Do not invent commands.",
|
||||
"To manage the Gateway daemon service (start/stop/restart):",
|
||||
"- clawdbot daemon status",
|
||||
"- clawdbot daemon start",
|
||||
"- clawdbot daemon stop",
|
||||
"- clawdbot daemon restart",
|
||||
"If unsure, ask the user to run `clawdbot help` (or `clawdbot daemon --help`) and paste the output.",
|
||||
`- ${formatCliCommand("clawdbot daemon status")}`,
|
||||
`- ${formatCliCommand("clawdbot daemon start")}`,
|
||||
`- ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`- ${formatCliCommand("clawdbot daemon restart")}`,
|
||||
`If unsure, ask the user to run \`${formatCliCommand("clawdbot help")}\` (or \`${formatCliCommand("clawdbot daemon --help")}\`) and paste the output.`,
|
||||
"",
|
||||
...skillsSection,
|
||||
...memorySection,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||
import {
|
||||
DOCTOR_NONINTERACTIVE_HINT,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
} from "../../infra/restart-sentinel.js";
|
||||
@@ -83,7 +83,7 @@ export function createGatewayTool(opts?: {
|
||||
ts: Date.now(),
|
||||
sessionKey,
|
||||
message: note ?? reason ?? null,
|
||||
doctorHint: DOCTOR_NONINTERACTIVE_HINT,
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: "gateway.restart",
|
||||
reason,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
|
||||
import {
|
||||
@@ -124,8 +125,7 @@ function missingSearchKeyPayload(provider: (typeof SEARCH_PROVIDERS)[number]) {
|
||||
}
|
||||
return {
|
||||
error: "missing_brave_api_key",
|
||||
message:
|
||||
"web_search needs a Brave Search API key. Run `clawdbot configure --section web` to store it, or set BRAVE_API_KEY in the Gateway environment.",
|
||||
message: `web_search needs a Brave Search API key. Run \`${formatCliCommand("clawdbot configure --section web")}\` to store it, or set BRAVE_API_KEY in the Gateway environment.`,
|
||||
docs: "https://docs.clawd.bot/tools/web",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { isSubagentSessionKey } from "../routing/session-key.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
|
||||
export function resolveDefaultAgentWorkspaceDir(
|
||||
@@ -135,7 +136,7 @@ After the user chooses, update:
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdbot/clawdbot.json
|
||||
Run: clawdbot agents set-identity --workspace "<this workspace>" --from-identity
|
||||
Run: ${formatCliCommand('clawdbot agents set-identity --workspace "<this workspace>" --from-identity')}
|
||||
If multiple agents share a host, add --agent <id>.
|
||||
|
||||
## Cleanup
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createExecTool } from "../../agents/bash-tools.js";
|
||||
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
|
||||
import { killProcessTree } from "../../agents/shell-utils.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { clampInt } from "../../utils.js";
|
||||
import type { MsgContext } from "../templating.js";
|
||||
@@ -167,7 +168,9 @@ function formatElevatedUnavailableMessage(params: {
|
||||
lines.push("- agents.list[].tools.elevated.enabled");
|
||||
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
||||
if (params.sessionKey) {
|
||||
lines.push(`See: clawdbot sandbox explain --session ${params.sessionKey}`);
|
||||
lines.push(
|
||||
`See: ${formatCliCommand(`clawdbot sandbox explain --session ${params.sessionKey}`)}`,
|
||||
);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import type { ElevatedLevel, ReasoningLevel } from "./directives.js";
|
||||
|
||||
export const SYSTEM_MARK = "⚙️";
|
||||
@@ -44,7 +45,9 @@ export function formatElevatedUnavailableText(params: {
|
||||
);
|
||||
}
|
||||
if (params.sessionKey) {
|
||||
lines.push(`See: clawdbot sandbox explain --session ${params.sessionKey}`);
|
||||
lines.push(
|
||||
`See: ${formatCliCommand(`clawdbot sandbox explain --session ${params.sessionKey}`)}`,
|
||||
);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { normalizeChannelId } from "../../channels/plugins/index.js";
|
||||
import { CHAT_CHANNEL_ORDER } from "../../channels/registry.js";
|
||||
import type { AgentElevatedAllowFromConfig, ClawdbotConfig } from "../../config/config.js";
|
||||
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import type { MsgContext } from "../templating.js";
|
||||
|
||||
function normalizeAllowToken(value?: string) {
|
||||
@@ -187,7 +188,9 @@ export function formatElevatedUnavailableMessage(params: {
|
||||
lines.push("- agents.list[].tools.elevated.enabled");
|
||||
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
||||
if (params.sessionKey) {
|
||||
lines.push(`See: clawdbot sandbox explain --session ${params.sessionKey}`);
|
||||
lines.push(
|
||||
`See: ${formatCliCommand(`clawdbot sandbox explain --session ${params.sessionKey}`)}`,
|
||||
);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { extractErrorCode, formatErrorMessage } from "../infra/errors.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveBrowserConfig } from "./config.js";
|
||||
|
||||
let cachedConfigToken: string | null | undefined = undefined;
|
||||
@@ -30,8 +31,7 @@ function enhanceBrowserFetchError(url: string, err: unknown, timeoutMs: number):
|
||||
const cause = unwrapCause(err);
|
||||
const code = extractErrorCode(cause) ?? extractErrorCode(err) ?? "";
|
||||
|
||||
const hint =
|
||||
"Start (or restart) the Clawdbot gateway (Clawdbot.app menubar, or `clawdbot gateway`) and try again.";
|
||||
const hint = `Start (or restart) the Clawdbot gateway (Clawdbot.app menubar, or \`${formatCliCommand("clawdbot gateway")}\`) and try again.`;
|
||||
|
||||
if (code === "ECONNREFUSED") {
|
||||
return new Error(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { ensurePageState, getPageForTargetId } from "./pw-session.js";
|
||||
import { normalizeTimeoutMs } from "./pw-tools-core.shared.js";
|
||||
|
||||
@@ -65,7 +66,7 @@ export async function responseBodyViaPlaywright(opts: {
|
||||
cleanup();
|
||||
reject(
|
||||
new Error(
|
||||
`Response not found for url pattern "${pattern}". Run 'clawdbot browser requests' to inspect recent network activity.`,
|
||||
`Response not found for url pattern "${pattern}". Run '${formatCliCommand("clawdbot browser requests")}' to inspect recent network activity.`,
|
||||
),
|
||||
);
|
||||
}, timeout);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
||||
import type { ChannelPlugin } from "./types.js";
|
||||
@@ -13,5 +14,7 @@ export function resolveChannelDefaultAccountId<ResolvedAccount>(params: {
|
||||
}
|
||||
|
||||
export function formatPairingApproveHint(channelId: string): string {
|
||||
return `Approve via: clawdbot pairing list ${channelId} / clawdbot pairing approve ${channelId} <code>`;
|
||||
const listCmd = formatCliCommand(`clawdbot pairing list ${channelId}`);
|
||||
const approveCmd = formatCliCommand(`clawdbot pairing approve ${channelId} <code>`);
|
||||
return `Approve via: ${listCmd} / ${approveCmd}`;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
resolveSignalAccount,
|
||||
} from "../../../signal/accounts.js";
|
||||
import { formatDocsLink } from "../../../terminal/links.js";
|
||||
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||
import { normalizeE164 } from "../../../utils.js";
|
||||
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
||||
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
||||
@@ -283,7 +284,7 @@ export const signalOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
[
|
||||
'Link device with: signal-cli link -n "Clawdbot"',
|
||||
"Scan QR in Signal → Linked Devices",
|
||||
"Then run: clawdbot gateway call channels.status --params '{\"probe\":true}'",
|
||||
`Then run: ${formatCliCommand("clawdbot gateway call channels.status --params '{\"probe\":true}'")}`,
|
||||
`Docs: ${formatDocsLink("/signal", "signal")}`,
|
||||
].join("\n"),
|
||||
"Signal next steps",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
resolveTelegramAccount,
|
||||
} from "../../../telegram/accounts.js";
|
||||
import { formatDocsLink } from "../../../terminal/links.js";
|
||||
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
||||
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
||||
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
|
||||
@@ -46,7 +47,7 @@ async function noteTelegramTokenHelp(prompter: WizardPrompter): Promise<void> {
|
||||
async function noteTelegramUserIdHelp(prompter: WizardPrompter): Promise<void> {
|
||||
await prompter.note(
|
||||
[
|
||||
"1) DM your bot, then read from.id in `clawdbot logs --follow` (safest)",
|
||||
`1) DM your bot, then read from.id in \`${formatCliCommand("clawdbot logs --follow")}\` (safest)`,
|
||||
"2) Or call https://api.telegram.org/bot<bot_token>/getUpdates and read message.from.id",
|
||||
"3) Third-party: DM @userinfobot or @getidsbot",
|
||||
`Docs: ${formatDocsLink("/telegram")}`,
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { DmPolicy } from "../../../config/types.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../../../runtime.js";
|
||||
import { formatDocsLink } from "../../../terminal/links.js";
|
||||
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||
import { normalizeE164 } from "../../../utils.js";
|
||||
import {
|
||||
listWhatsAppAccountIds,
|
||||
@@ -321,7 +322,10 @@ export const whatsappOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
await prompter.note(`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`, "WhatsApp help");
|
||||
}
|
||||
} else if (!linked) {
|
||||
await prompter.note("Run `clawdbot channels login` later to link WhatsApp.", "WhatsApp");
|
||||
await prompter.note(
|
||||
`Run \`${formatCliCommand("clawdbot channels login")}\` later to link WhatsApp.`,
|
||||
"WhatsApp",
|
||||
);
|
||||
}
|
||||
|
||||
next = await promptWhatsAppAllowFrom(next, runtime, prompter, {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||
import type { ChannelAccountSnapshot, ChannelStatusIssue } from "../types.js";
|
||||
import { asString, isRecord } from "./shared.js";
|
||||
|
||||
@@ -47,7 +48,7 @@ export function collectWhatsAppStatusIssues(
|
||||
accountId,
|
||||
kind: "auth",
|
||||
message: "Not linked (no WhatsApp Web session).",
|
||||
fix: "Run: clawdbot channels login (scan QR on the gateway host).",
|
||||
fix: `Run: ${formatCliCommand("clawdbot channels login")} (scan QR on the gateway host).`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -58,7 +59,7 @@ export function collectWhatsAppStatusIssues(
|
||||
accountId,
|
||||
kind: "runtime",
|
||||
message: `Linked but disconnected${reconnectAttempts != null ? ` (reconnectAttempts=${reconnectAttempts})` : ""}${lastError ? `: ${lastError}` : "."}`,
|
||||
fix: "Run: clawdbot doctor (or restart the gateway). If it persists, relink via channels login and check logs.",
|
||||
fix: `Run: ${formatCliCommand("clawdbot doctor")} (or restart the gateway). If it persists, relink via channels login and check logs.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { defaultRuntime } from "../runtime.js";
|
||||
import { movePathToTrash } from "../browser/trash.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
function bundledExtensionRootDir() {
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -103,7 +104,7 @@ export function registerBrowserExtensionCommands(
|
||||
defaultRuntime.error(
|
||||
danger(
|
||||
[
|
||||
'Chrome extension is not installed. Run: "clawdbot browser extension install"',
|
||||
`Chrome extension is not installed. Run: "${formatCliCommand("clawdbot browser extension install")}"`,
|
||||
`Docs: ${formatDocsLink("/tools/chrome-extension", "docs.clawd.bot/tools/chrome-extension")}`,
|
||||
].join("\n"),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,7 @@ import { danger } from "../globals.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { registerBrowserActionInputCommands } from "./browser-cli-actions-input.js";
|
||||
import { registerBrowserActionObserveCommands } from "./browser-cli-actions-observe.js";
|
||||
import { registerBrowserDebugCommands } from "./browser-cli-debug.js";
|
||||
@@ -32,7 +33,9 @@ export function registerBrowserCli(program: Command) {
|
||||
)
|
||||
.action(() => {
|
||||
browser.outputHelp();
|
||||
defaultRuntime.error(danger('Missing subcommand. Try: "clawdbot browser status"'));
|
||||
defaultRuntime.error(
|
||||
danger(`Missing subcommand. Try: "${formatCliCommand("clawdbot browser status")}"`),
|
||||
);
|
||||
defaultRuntime.exit(1);
|
||||
});
|
||||
|
||||
|
||||
16
src/cli/command-format.ts
Normal file
16
src/cli/command-format.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { normalizeProfileName } from "./profile-utils.js";
|
||||
|
||||
const CLI_PREFIX_RE = /^(?:pnpm|npm|bunx|npx)\s+clawdbot\b|^clawdbot\b/;
|
||||
const PROFILE_FLAG_RE = /\b--profile\b/;
|
||||
const DEV_FLAG_RE = /\b--dev\b/;
|
||||
|
||||
export function formatCliCommand(
|
||||
command: string,
|
||||
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
|
||||
): string {
|
||||
const profile = normalizeProfileName(env.CLAWDBOT_PROFILE);
|
||||
if (!profile) return command;
|
||||
if (!CLI_PREFIX_RE.test(command)) return command;
|
||||
if (PROFILE_FLAG_RE.test(command) || DEV_FLAG_RE.test(command)) return command;
|
||||
return command.replace(CLI_PREFIX_RE, (match) => `${match} --profile ${profile}`);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
||||
import { danger, info } from "../globals.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
|
||||
type PathSegment = string;
|
||||
@@ -171,7 +172,7 @@ async function loadValidConfig() {
|
||||
for (const issue of snapshot.issues) {
|
||||
defaultRuntime.error(`- ${issue.path || "<root>"}: ${issue.message}`);
|
||||
}
|
||||
defaultRuntime.error("Run `clawdbot doctor` to repair, then retry.");
|
||||
defaultRuntime.error(`Run \`${formatCliCommand("clawdbot doctor")}\` to repair, then retry.`);
|
||||
defaultRuntime.exit(1);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { loadConfig, resolveGatewayPort } from "../../config/config.js";
|
||||
import { resolveIsNixMode } from "../../config/paths.js";
|
||||
import { resolveGatewayService } from "../../daemon/service.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson } from "./response.js";
|
||||
import { parsePort } from "./shared.js";
|
||||
import type { DaemonInstallOptions } from "./types.js";
|
||||
@@ -82,7 +83,9 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
|
||||
defaultRuntime.log("Reinstall with: clawdbot daemon install --force");
|
||||
defaultRuntime.log(
|
||||
`Reinstall with: ${formatCliCommand("clawdbot daemon install --force")}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "../../daemon/constants.js";
|
||||
import { resolveGatewayLogPaths } from "../../daemon/launchd.js";
|
||||
import { getResolvedLoggerSettings } from "../../logging.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
|
||||
export function parsePort(raw: unknown): number | null {
|
||||
if (raw === undefined || raw === null) return null;
|
||||
@@ -122,7 +123,7 @@ export function renderRuntimeHints(
|
||||
}
|
||||
})();
|
||||
if (runtime.missingUnit) {
|
||||
hints.push("Service not installed. Run: clawdbot daemon install");
|
||||
hints.push(`Service not installed. Run: ${formatCliCommand("clawdbot daemon install", env)}`);
|
||||
if (fileLog) hints.push(`File logs: ${fileLog}`);
|
||||
return hints;
|
||||
}
|
||||
@@ -144,7 +145,10 @@ export function renderRuntimeHints(
|
||||
}
|
||||
|
||||
export function renderGatewayServiceStartHints(env: NodeJS.ProcessEnv = process.env): string[] {
|
||||
const base = ["clawdbot daemon install", "clawdbot gateway"];
|
||||
const base = [
|
||||
formatCliCommand("clawdbot daemon install", env),
|
||||
formatCliCommand("clawdbot gateway", env),
|
||||
];
|
||||
const profile = env.CLAWDBOT_PROFILE;
|
||||
switch (process.platform) {
|
||||
case "darwin": {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { isWSLEnv } from "../../infra/wsl.js";
|
||||
import { getResolvedLoggerSettings } from "../../logging.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { formatRuntimeStatus, renderRuntimeHints, safeDaemonEnv } from "./shared.js";
|
||||
import {
|
||||
type DaemonStatus,
|
||||
@@ -70,7 +71,9 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
defaultRuntime.error(`${warnText("Service config issue:")} ${issue.message}${detail}`);
|
||||
}
|
||||
defaultRuntime.error(
|
||||
warnText('Recommendation: run "clawdbot doctor" (or "clawdbot doctor --repair").'),
|
||||
warnText(
|
||||
`Recommendation: run "${formatCliCommand("clawdbot doctor")}" (or "${formatCliCommand("clawdbot doctor --repair")}").`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
);
|
||||
defaultRuntime.error(
|
||||
errorText(
|
||||
"Fix: rerun `clawdbot daemon install --force` from the same --profile / CLAWDBOT_STATE_DIR you expect.",
|
||||
`Fix: rerun \`${formatCliCommand("clawdbot daemon install --force")}\` from the same --profile / CLAWDBOT_STATE_DIR you expect.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -205,7 +208,9 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
`LaunchAgent label cached but plist missing. Clear with: launchctl bootout gui/$UID/${labelValue}`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.error(errorText("Then reinstall: clawdbot daemon install"));
|
||||
defaultRuntime.error(
|
||||
errorText(`Then reinstall: ${formatCliCommand("clawdbot daemon install")}`),
|
||||
);
|
||||
spacer();
|
||||
}
|
||||
|
||||
@@ -259,7 +264,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
for (const svc of legacyServices) {
|
||||
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.detail})`);
|
||||
}
|
||||
defaultRuntime.error(errorText("Cleanup: clawdbot doctor"));
|
||||
defaultRuntime.error(errorText(`Cleanup: ${formatCliCommand("clawdbot doctor")}`));
|
||||
spacer();
|
||||
}
|
||||
|
||||
@@ -288,6 +293,6 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
spacer();
|
||||
}
|
||||
|
||||
defaultRuntime.log(`${label("Troubles:")} run clawdbot status`);
|
||||
defaultRuntime.log(`${label("Troubles:")} run ${formatCliCommand("clawdbot status")}`);
|
||||
defaultRuntime.log(`${label("Troubleshooting:")} https://docs.clawd.bot/troubleshooting`);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { formatPortDiagnostics, inspectPortUsage } from "../../infra/ports.js";
|
||||
import { setConsoleSubsystemFilter, setConsoleTimestampPrefix } from "../../logging/console.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { forceFreePortAndWait } from "../ports.js";
|
||||
import { ensureDevGatewayConfig } from "./dev.js";
|
||||
import { runGatewayLoop } from "./run-loop.js";
|
||||
@@ -161,7 +162,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
||||
if (!opts.allowUnconfigured && mode !== "local") {
|
||||
if (!configExists) {
|
||||
defaultRuntime.error(
|
||||
"Missing config. Run `clawdbot setup` or set gateway.mode=local (or pass --allow-unconfigured).",
|
||||
`Missing config. Run \`${formatCliCommand("clawdbot setup")}\` or set gateway.mode=local (or pass --allow-unconfigured).`,
|
||||
);
|
||||
} else {
|
||||
defaultRuntime.error(
|
||||
@@ -277,7 +278,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
||||
) {
|
||||
const errMessage = describeUnknownError(err);
|
||||
defaultRuntime.error(
|
||||
`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: clawdbot daemon stop`,
|
||||
`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
);
|
||||
try {
|
||||
const diagnostics = await inspectPortUsage(port);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "../../daemon/constants.js";
|
||||
import { resolveGatewayService } from "../../daemon/service.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
|
||||
export function parsePort(raw: unknown): number | null {
|
||||
if (raw === undefined || raw === null) return null;
|
||||
@@ -67,21 +68,21 @@ export function renderGatewayServiceStopHints(env: NodeJS.ProcessEnv = process.e
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return [
|
||||
"Tip: clawdbot daemon stop",
|
||||
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`Or: launchctl bootout gui/$UID/${resolveGatewayLaunchAgentLabel(profile)}`,
|
||||
];
|
||||
case "linux":
|
||||
return [
|
||||
"Tip: clawdbot daemon stop",
|
||||
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`Or: systemctl --user stop ${resolveGatewaySystemdServiceName(profile)}.service`,
|
||||
];
|
||||
case "win32":
|
||||
return [
|
||||
"Tip: clawdbot daemon stop",
|
||||
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`Or: schtasks /End /TN "${resolveGatewayWindowsTaskName(profile)}"`,
|
||||
];
|
||||
default:
|
||||
return ["Tip: clawdbot daemon stop"];
|
||||
return [`Tip: ${formatCliCommand("clawdbot daemon stop")}`];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { buildPluginStatusReport } from "../plugins/status.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
|
||||
export type HooksListOptions = {
|
||||
@@ -150,7 +151,7 @@ export function formatHooksList(report: HookStatusReport, opts: HooksListOptions
|
||||
|
||||
if (hooks.length === 0) {
|
||||
const message = opts.eligible
|
||||
? "No eligible hooks found. Run `clawdbot hooks list` to see all hooks."
|
||||
? `No eligible hooks found. Run \`${formatCliCommand("clawdbot hooks list")}\` to see all hooks.`
|
||||
: "No hooks found.";
|
||||
return message;
|
||||
}
|
||||
@@ -194,7 +195,7 @@ export function formatHookInfo(
|
||||
if (opts.json) {
|
||||
return JSON.stringify({ error: "not found", hook: hookName }, null, 2);
|
||||
}
|
||||
return `Hook "${hookName}" not found. Run \`clawdbot hooks list\` to see available hooks.`;
|
||||
return `Hook "${hookName}" not found. Run \`${formatCliCommand("clawdbot hooks list")}\` to see available hooks.`;
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { parseLogLine } from "../logging/parse-log-line.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js";
|
||||
|
||||
type LogsTailPayload = {
|
||||
@@ -117,7 +118,7 @@ function emitGatewayError(
|
||||
) {
|
||||
const details = buildGatewayConnectionDetails({ url: opts.url });
|
||||
const message = "Gateway not reachable. Is it running and accessible?";
|
||||
const hint = "Hint: run `clawdbot doctor`.";
|
||||
const hint = `Hint: run \`${formatCliCommand("clawdbot doctor")}\`.`;
|
||||
const errorText = err instanceof Error ? err.message : String(err);
|
||||
|
||||
if (mode === "json") {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { isWSL } from "../../infra/wsl.js";
|
||||
import { loadNodeHostConfig } from "../../node-host/config.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import {
|
||||
buildDaemonServiceSnapshot,
|
||||
createNullWriter,
|
||||
@@ -46,7 +47,10 @@ type NodeDaemonStatusOptions = {
|
||||
};
|
||||
|
||||
function renderNodeServiceStartHints(): string[] {
|
||||
const base = ["clawdbot node service install", "clawdbot node start"];
|
||||
const base = [
|
||||
formatCliCommand("clawdbot node service install"),
|
||||
formatCliCommand("clawdbot node start"),
|
||||
];
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return [
|
||||
@@ -168,7 +172,9 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) {
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(`Node service already ${service.loadedText}.`);
|
||||
defaultRuntime.log("Reinstall with: clawdbot node service install --force");
|
||||
defaultRuntime.log(
|
||||
`Reinstall with: ${formatCliCommand("clawdbot node service install --force")}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "../pairing/pairing-store.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
/** Parse channel, allowing extension channels not in core registry. */
|
||||
function parseChannel(raw: unknown, channels: PairingChannel[]): PairingChannel {
|
||||
@@ -95,12 +96,12 @@ export function registerPairingCli(program: Command) {
|
||||
const resolvedCode = opts.channel ? codeOrChannel : code;
|
||||
if (!opts.channel && !code) {
|
||||
throw new Error(
|
||||
`Usage: clawdbot pairing approve <channel> <code> (or: clawdbot pairing approve --channel <channel> <code>)`,
|
||||
`Usage: ${formatCliCommand("clawdbot pairing approve <channel> <code>")} (or: ${formatCliCommand("clawdbot pairing approve --channel <channel> <code>")})`,
|
||||
);
|
||||
}
|
||||
if (opts.channel && code != null) {
|
||||
throw new Error(
|
||||
`Too many arguments. Use: clawdbot pairing approve --channel <channel> <code>`,
|
||||
`Too many arguments. Use: ${formatCliCommand("clawdbot pairing approve --channel <channel> <code>")}`,
|
||||
);
|
||||
}
|
||||
const channel = parseChannel(channelRaw, channels);
|
||||
|
||||
15
src/cli/profile-utils.ts
Normal file
15
src/cli/profile-utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
||||
|
||||
export function isValidProfileName(value: string): boolean {
|
||||
if (!value) return false;
|
||||
// Keep it path-safe + shell-friendly.
|
||||
return PROFILE_NAME_RE.test(value);
|
||||
}
|
||||
|
||||
export function normalizeProfileName(raw?: string | null): string | null {
|
||||
const profile = raw?.trim();
|
||||
if (!profile) return null;
|
||||
if (profile.toLowerCase() === "default") return null;
|
||||
if (!isValidProfileName(profile)) return null;
|
||||
return profile;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
|
||||
|
||||
describe("parseCliProfileArgs", () => {
|
||||
@@ -76,3 +77,63 @@ describe("applyCliProfileEnv", () => {
|
||||
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join("/custom", "clawdbot.json"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatCliCommand", () => {
|
||||
it("returns command unchanged when no profile is set", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", {})).toBe("clawdbot doctor --fix");
|
||||
});
|
||||
|
||||
it("returns command unchanged when profile is default", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "default" })).toBe(
|
||||
"clawdbot doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns command unchanged when profile is Default (case-insensitive)", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "Default" })).toBe(
|
||||
"clawdbot doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns command unchanged when profile is invalid", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "bad profile" })).toBe(
|
||||
"clawdbot doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns command unchanged when --profile is already present", () => {
|
||||
expect(
|
||||
formatCliCommand("clawdbot --profile work doctor --fix", { CLAWDBOT_PROFILE: "work" }),
|
||||
).toBe("clawdbot --profile work doctor --fix");
|
||||
});
|
||||
|
||||
it("returns command unchanged when --dev is already present", () => {
|
||||
expect(formatCliCommand("clawdbot --dev doctor", { CLAWDBOT_PROFILE: "dev" })).toBe(
|
||||
"clawdbot --dev doctor",
|
||||
);
|
||||
});
|
||||
|
||||
it("inserts --profile flag when profile is set", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "work" })).toBe(
|
||||
"clawdbot --profile work doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("trims whitespace from profile", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: " jbclawd " })).toBe(
|
||||
"clawdbot --profile jbclawd doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles command with no args after clawdbot", () => {
|
||||
expect(formatCliCommand("clawdbot", { CLAWDBOT_PROFILE: "test" })).toBe(
|
||||
"clawdbot --profile test",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles pnpm wrapper", () => {
|
||||
expect(formatCliCommand("pnpm clawdbot doctor", { CLAWDBOT_PROFILE: "work" })).toBe(
|
||||
"pnpm clawdbot --profile work doctor",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { isValidProfileName } from "./profile-utils.js";
|
||||
|
||||
export type CliProfileParseResult =
|
||||
| { ok: true; profile: string | null; argv: string[] }
|
||||
| { ok: false; error: string };
|
||||
@@ -21,12 +23,6 @@ function takeValue(
|
||||
return { value: trimmed || null, consumedNext: Boolean(next) };
|
||||
}
|
||||
|
||||
function isValidProfileName(value: string): boolean {
|
||||
if (!value) return false;
|
||||
// Keep it path-safe + shell-friendly.
|
||||
return /^[a-z0-9][a-z0-9_-]{0,63}$/i.test(value);
|
||||
}
|
||||
|
||||
export function parseCliProfileArgs(argv: string[]): CliProfileParseResult {
|
||||
if (argv.length < 2) return { ok: true, profile: null, argv };
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { loadAndMaybeMigrateDoctorConfig } from "../../commands/doctor-config-fl
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { loadClawdbotPlugins } from "../../plugins/loader.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
|
||||
const ALLOWED_INVALID_COMMANDS = new Set(["doctor", "logs", "health", "help", "status", "service"]);
|
||||
|
||||
@@ -72,7 +73,9 @@ export async function ensureConfigReady(params: {
|
||||
params.runtime.error(pluginIssues.map((issue) => ` ${error(issue)}`).join("\n"));
|
||||
}
|
||||
params.runtime.error("");
|
||||
params.runtime.error(`${muted("Run:")} ${commandText("clawdbot doctor --fix")}`);
|
||||
params.runtime.error(
|
||||
`${muted("Run:")} ${commandText(formatCliCommand("clawdbot doctor --fix"))}`,
|
||||
);
|
||||
if (!allowInvalid) {
|
||||
params.runtime.exit(1);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { runSecurityAudit } from "../security/audit.js";
|
||||
import { fixSecurityFootguns } from "../security/fix.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { isRich, theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
type SecurityAuditOptions = {
|
||||
json?: boolean;
|
||||
@@ -67,10 +68,10 @@ export function registerSecurityCli(program: Command) {
|
||||
const lines: string[] = [];
|
||||
lines.push(heading("Clawdbot security audit"));
|
||||
lines.push(muted(`Summary: ${formatSummary(report.summary)}`));
|
||||
lines.push(muted(`Run deeper: clawdbot security audit --deep`));
|
||||
lines.push(muted(`Run deeper: ${formatCliCommand("clawdbot security audit --deep")}`));
|
||||
|
||||
if (opts.fix) {
|
||||
lines.push(muted(`Fix: clawdbot security audit --fix`));
|
||||
lines.push(muted(`Fix: ${formatCliCommand("clawdbot security audit --fix")}`));
|
||||
if (!fixResult) {
|
||||
lines.push(muted("Fixes: failed to apply (unexpected error)"));
|
||||
} else if (
|
||||
|
||||
@@ -10,6 +10,7 @@ import { loadConfig } from "../config/config.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
export type SkillsListOptions = {
|
||||
json?: boolean;
|
||||
@@ -101,7 +102,7 @@ export function formatSkillsList(report: SkillStatusReport, opts: SkillsListOpti
|
||||
|
||||
if (skills.length === 0) {
|
||||
const message = opts.eligible
|
||||
? "No eligible skills found. Run `clawdbot skills list` to see all skills."
|
||||
? `No eligible skills found. Run \`${formatCliCommand("clawdbot skills list")}\` to see all skills.`
|
||||
: "No skills found.";
|
||||
return appendClawdHubHint(message, opts.json);
|
||||
}
|
||||
@@ -148,7 +149,7 @@ export function formatSkillInfo(
|
||||
return JSON.stringify({ error: "not found", skill: skillName }, null, 2);
|
||||
}
|
||||
return appendClawdHubHint(
|
||||
`Skill "${skillName}" not found. Run \`clawdbot skills list\` to see available skills.`,
|
||||
`Skill "${skillName}" not found. Run \`${formatCliCommand("clawdbot skills list")}\` to see available skills.`,
|
||||
opts.json,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "../infra/update-runner.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { stylePromptMessage } from "../terminal/prompt-style.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
|
||||
@@ -373,7 +374,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
if (result.reason === "not-git-install") {
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
"Skipped: this Clawdbot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run `clawdbot doctor` and `clawdbot daemon restart`.",
|
||||
`Skipped: this Clawdbot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run \`${formatCliCommand("clawdbot doctor")}\` and \`${formatCliCommand("clawdbot daemon restart")}\`.`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(
|
||||
@@ -410,7 +411,9 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
if (!opts.json) {
|
||||
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
|
||||
defaultRuntime.log(
|
||||
theme.muted("You may need to restart the daemon manually: clawdbot daemon restart"),
|
||||
theme.muted(
|
||||
`You may need to restart the daemon manually: ${formatCliCommand("clawdbot daemon restart")}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -419,12 +422,14 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
if (result.mode === "npm" || result.mode === "pnpm") {
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
"Tip: Run `clawdbot doctor`, then `clawdbot daemon restart` to apply updates to a running gateway.",
|
||||
`Tip: Run \`${formatCliCommand("clawdbot doctor")}\`, then \`${formatCliCommand("clawdbot daemon restart")}\` to apply updates to a running gateway.`,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
defaultRuntime.log(
|
||||
theme.muted("Tip: Run `clawdbot daemon restart` to apply updates to a running gateway."),
|
||||
theme.muted(
|
||||
`Tip: Run \`${formatCliCommand("clawdbot daemon restart")}\` to apply updates to a running gateway.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
|
||||
import { listAgentIds } from "../agents/agent-scope.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
GATEWAY_CLIENT_MODES,
|
||||
GATEWAY_CLIENT_NAMES,
|
||||
@@ -92,7 +93,7 @@ export async function agentViaGatewayCommand(opts: AgentCliOpts, runtime: Runtim
|
||||
const knownAgents = listAgentIds(cfg);
|
||||
if (!knownAgents.includes(agentId)) {
|
||||
throw new Error(
|
||||
`Unknown agent id "${agentIdRaw}". Use "clawdbot agents list" to see configured agents.`,
|
||||
`Unknown agent id "${agentIdRaw}". Use "${formatCliCommand("clawdbot agents list")}" to see configured agents.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
} from "../infra/agent-events.js";
|
||||
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { applyVerboseOverride } from "../sessions/level-overrides.js";
|
||||
import { resolveSendPolicy } from "../sessions/send-policy.js";
|
||||
import { resolveMessageChannel } from "../utils/message-channel.js";
|
||||
@@ -75,7 +76,7 @@ export async function agentCommand(
|
||||
const knownAgents = listAgentIds(cfg);
|
||||
if (!knownAgents.includes(agentIdOverride)) {
|
||||
throw new Error(
|
||||
`Unknown agent id "${agentIdOverrideRaw}". Use "clawdbot agents list" to see configured agents.`,
|
||||
`Unknown agent id "${agentIdOverrideRaw}". Use "${formatCliCommand("clawdbot agents list")}" to see configured agents.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { readConfigFileSnapshot } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
@@ -14,7 +15,7 @@ export async function requireValidConfig(runtime: RuntimeEnv): Promise<ClawdbotC
|
||||
? snapshot.issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n")
|
||||
: "Unknown validation issue.";
|
||||
runtime.error(`Config invalid:\n${issues}`);
|
||||
runtime.error("Fix the config or run clawdbot doctor.");
|
||||
runtime.error(`Fix the config or run ${formatCliCommand("clawdbot doctor")}.`);
|
||||
runtime.exit(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { AgentBinding } from "../config/types.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { describeBinding } from "./agents.bindings.js";
|
||||
import { requireValidConfig } from "./agents.command-shared.js";
|
||||
import type { AgentSummary } from "./agents.config.js";
|
||||
@@ -116,7 +117,7 @@ export async function agentsListCommand(
|
||||
const lines = ["Agents:", ...summaries.map(formatSummary)];
|
||||
lines.push("Routing rules map channel/account/peer to an agent. Use --bindings for full rules.");
|
||||
lines.push(
|
||||
"Channel status reflects local config/creds. For live health: clawdbot channels status --probe.",
|
||||
`Channel status reflects local config/creds. For live health: ${formatCliCommand("clawdbot channels status --probe")}.`,
|
||||
);
|
||||
runtime.log(lines.join("\n"));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type ChannelId, getChannelPlugin } from "../../channels/plugins/index.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { type ClawdbotConfig, readConfigFileSnapshot } from "../../config/config.js";
|
||||
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||
@@ -15,7 +16,7 @@ export async function requireValidConfig(
|
||||
? snapshot.issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n")
|
||||
: "Unknown validation issue.";
|
||||
runtime.error(`Config invalid:\n${issues}`);
|
||||
runtime.error("Fix the config or run clawdbot doctor.");
|
||||
runtime.error(`Fix the config or run ${formatCliCommand("clawdbot doctor")}.`);
|
||||
runtime.exit(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { formatAge } from "../../infra/channel-summary.js";
|
||||
import { collectChannelStatusIssues } from "../../infra/channels-status-issues.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { type ChatChannel, formatChannelAccountLabel, requireValidConfig } from "./shared.js";
|
||||
|
||||
@@ -142,7 +143,7 @@ export function formatGatewayChannelsStatusLines(payload: Record<string, unknown
|
||||
`- ${issue.channel} ${issue.accountId}: ${issue.message}${issue.fix ? ` (${issue.fix})` : ""}`,
|
||||
);
|
||||
}
|
||||
lines.push(`- Run: clawdbot doctor`);
|
||||
lines.push(`- Run: ${formatCliCommand("clawdbot doctor")}`);
|
||||
lines.push("");
|
||||
}
|
||||
lines.push(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { CONFIG_PATH_CLAWDBOT } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
@@ -23,7 +24,7 @@ export async function removeChannelConfigWizard(
|
||||
note(
|
||||
[
|
||||
"No channel config found in clawdbot.json.",
|
||||
"Tip: `clawdbot channels status` shows what is configured and enabled.",
|
||||
`Tip: \`${formatCliCommand("clawdbot channels status")}\` shows what is configured and enabled.`,
|
||||
].join("\n"),
|
||||
"Remove channel",
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
@@ -198,7 +199,9 @@ export async function runConfigureWizard(
|
||||
);
|
||||
}
|
||||
if (!snapshot.valid) {
|
||||
outro("Config invalid. Run `clawdbot doctor` to repair it, then re-run configure.");
|
||||
outro(
|
||||
`Config invalid. Run \`${formatCliCommand("clawdbot doctor")}\` to repair it, then re-run configure.`,
|
||||
);
|
||||
runtime.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
resolveSystemNodeInfo,
|
||||
} from "../daemon/runtime-paths.js";
|
||||
import { buildServiceEnvironment } from "../daemon/service-env.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
|
||||
|
||||
type WarnFn = (message: string, title?: string) => void;
|
||||
@@ -65,5 +66,5 @@ export async function buildGatewayInstallPlan(params: {
|
||||
export function gatewayInstallErrorHint(platform = process.platform): string {
|
||||
return platform === "win32"
|
||||
? "Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip daemon install."
|
||||
: "Tip: rerun `clawdbot daemon install` after fixing the error.";
|
||||
: `Tip: rerun \`${formatCliCommand("clawdbot daemon install")}\` after fixing the error.`;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { isRich, theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
||||
const SEARCH_TOOL = "https://docs.clawd.bot/mcp.SearchClawdbot";
|
||||
const SEARCH_TIMEOUT_MS = 30_000;
|
||||
@@ -150,10 +151,10 @@ export async function docsSearchCommand(queryParts: string[], runtime: RuntimeEn
|
||||
const docs = formatDocsLink("/", "docs.clawd.bot");
|
||||
if (isRich()) {
|
||||
runtime.log(`${theme.muted("Docs:")} ${docs}`);
|
||||
runtime.log(`${theme.muted("Search:")} clawdbot docs "your query"`);
|
||||
runtime.log(`${theme.muted("Search:")} ${formatCliCommand('clawdbot docs "your query"')}`);
|
||||
} else {
|
||||
runtime.log("Docs: https://docs.clawd.bot/");
|
||||
runtime.log('Search: clawdbot docs "your query"');
|
||||
runtime.log(`Search: ${formatCliCommand('clawdbot docs "your query"')}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "../agents/auth-profiles.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { DoctorPrompter } from "./doctor-prompter.js";
|
||||
|
||||
export async function maybeRepairAnthropicOAuthProfileId(
|
||||
@@ -49,9 +50,9 @@ function formatAuthIssueHint(issue: AuthIssue): string | null {
|
||||
return "Run `claude setup-token` on the gateway host.";
|
||||
}
|
||||
if (issue.provider === "openai-codex" && issue.profileId === CODEX_CLI_PROFILE_ID) {
|
||||
return "Run `codex login` (or `clawdbot configure` → OpenAI Codex OAuth).";
|
||||
return `Run \`codex login\` (or \`${formatCliCommand("clawdbot configure")}\` → OpenAI Codex OAuth).`;
|
||||
}
|
||||
return "Re-auth via `clawdbot configure` or `clawdbot onboard`.";
|
||||
return `Re-auth via \`${formatCliCommand("clawdbot configure")}\` or \`${formatCliCommand("clawdbot onboard")}\`.`;
|
||||
}
|
||||
|
||||
function formatAuthIssueLine(issue: AuthIssue): string {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
readConfigFileSnapshot,
|
||||
} from "../config/config.js";
|
||||
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
import { normalizeLegacyConfigValues } from "./doctor-legacy-config.js";
|
||||
import type { DoctorOptions } from "./doctor-prompter.js";
|
||||
@@ -139,7 +140,10 @@ export async function loadAndMaybeMigrateDoctorConfig(params: {
|
||||
if (changes.length > 0) note(changes.join("\n"), "Doctor changes");
|
||||
if (migrated) cfg = migrated;
|
||||
} else {
|
||||
note('Run "clawdbot doctor --fix" to apply legacy migrations.', "Doctor");
|
||||
note(
|
||||
`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply legacy migrations.`,
|
||||
"Doctor",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@ export async function loadAndMaybeMigrateDoctorConfig(params: {
|
||||
if (shouldRepair) {
|
||||
cfg = normalized.config;
|
||||
} else {
|
||||
note('Run "clawdbot doctor --fix" to apply these changes.', "Doctor");
|
||||
note(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply these changes.`, "Doctor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +163,7 @@ export async function loadAndMaybeMigrateDoctorConfig(params: {
|
||||
if (shouldRepair) {
|
||||
cfg = autoEnable.config;
|
||||
} else {
|
||||
note('Run "clawdbot doctor --fix" to apply these changes.', "Doctor");
|
||||
note(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply these changes.`, "Doctor");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
isSystemdUnavailableDetail,
|
||||
renderSystemdUnavailableHints,
|
||||
} from "../daemon/systemd-hints.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { isWSLEnv } from "../infra/wsl.js";
|
||||
import type { GatewayServiceRuntime } from "../daemon/service-runtime.js";
|
||||
import { getResolvedLoggerSettings } from "../logging.js";
|
||||
@@ -69,10 +70,10 @@ export function buildGatewayRuntimeHints(
|
||||
hints.push(
|
||||
`LaunchAgent label cached but plist missing. Clear with: launchctl bootout gui/$UID/${label}`,
|
||||
);
|
||||
hints.push("Then reinstall: clawdbot daemon install");
|
||||
hints.push(`Then reinstall: ${formatCliCommand("clawdbot daemon install", env)}`);
|
||||
}
|
||||
if (runtime.missingUnit) {
|
||||
hints.push("Service not installed. Run: clawdbot daemon install");
|
||||
hints.push(`Service not installed. Run: ${formatCliCommand("clawdbot daemon install", env)}`);
|
||||
if (fileLog) hints.push(`File logs: ${fileLog}`);
|
||||
return hints;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { renderSystemdUnavailableHints } from "../daemon/systemd-hints.js";
|
||||
import { formatPortDiagnostics, inspectPortUsage } from "../infra/ports.js";
|
||||
import { isWSL } from "../infra/wsl.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
import { sleep } from "../utils.js";
|
||||
import {
|
||||
@@ -201,7 +202,7 @@ export async function maybeRepairGatewayDaemon(params: {
|
||||
if (process.platform === "darwin") {
|
||||
const label = resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE);
|
||||
note(
|
||||
`LaunchAgent loaded; stopping requires "clawdbot daemon stop" or launchctl bootout gui/$UID/${label}.`,
|
||||
`LaunchAgent loaded; stopping requires "${formatCliCommand("clawdbot daemon stop")}" or launchctl bootout gui/$UID/${label}.`,
|
||||
"Gateway",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ import type { ChannelId } from "../channels/plugins/types.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
||||
export async function noteSecurityWarnings(cfg: ClawdbotConfig) {
|
||||
const warnings: string[] = [];
|
||||
const auditHint = `- Run: clawdbot security audit --deep`;
|
||||
const auditHint = `- Run: ${formatCliCommand("clawdbot security audit --deep")}`;
|
||||
|
||||
const warnDmPolicy = async (params: {
|
||||
label: string;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { isTruthyEnvValue } from "../infra/env.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { note } from "../terminal/note.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { DoctorOptions } from "./doctor-prompter.js";
|
||||
|
||||
async function detectClawdbotGitCheckout(root: string): Promise<"git" | "not-git" | "unknown"> {
|
||||
@@ -71,7 +72,7 @@ export async function maybeOfferUpdateBeforeDoctor(params: {
|
||||
note(
|
||||
[
|
||||
"This install is not a git checkout.",
|
||||
"Run `clawdbot update` to update via your package manager (npm/pnpm), then rerun doctor.",
|
||||
`Run \`${formatCliCommand("clawdbot update")}\` to update via your package manager (npm/pnpm), then rerun doctor.`,
|
||||
].join("\n"),
|
||||
"Update",
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
resolveConfiguredModelRef,
|
||||
resolveHooksGmailModel,
|
||||
} from "../agents/model-selection.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
||||
import { resolveGatewayService } from "../daemon/service.js";
|
||||
@@ -258,7 +259,7 @@ export async function doctorCommand(
|
||||
runtime.log(`Backup: ${backupPath}`);
|
||||
}
|
||||
} else {
|
||||
runtime.log('Run "clawdbot doctor --fix" to apply changes.');
|
||||
runtime.log(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply changes.`);
|
||||
}
|
||||
|
||||
if (options.workspaceSuggestions !== false) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "../../agents/agent-scope.js";
|
||||
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
readConfigFileSnapshot,
|
||||
@@ -340,7 +341,9 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim
|
||||
|
||||
const providers = resolvePluginProviders({ config, workspaceDir });
|
||||
if (providers.length === 0) {
|
||||
throw new Error("No provider plugins found. Install one via `clawdbot plugins install`.");
|
||||
throw new Error(
|
||||
`No provider plugins found. Install one via \`${formatCliCommand("clawdbot plugins install")}\`.`,
|
||||
);
|
||||
}
|
||||
|
||||
const prompter = createClackPrompter();
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "../../infra/provider-usage.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { colorize, theme } from "../../terminal/theme.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { shortenHomePath } from "../../utils.js";
|
||||
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
||||
import { isRich } from "./list.format.js";
|
||||
@@ -395,8 +396,8 @@ export async function modelsStatusCommand(
|
||||
for (const provider of missingProvidersInUse) {
|
||||
const hint =
|
||||
provider === "anthropic"
|
||||
? "Run `claude setup-token` or `clawdbot configure`."
|
||||
: "Run `clawdbot configure` or set an API key env var.";
|
||||
? `Run \`claude setup-token\` or \`${formatCliCommand("clawdbot configure")}\`.`
|
||||
: `Run \`${formatCliCommand("clawdbot configure")}\` or set an API key env var.`;
|
||||
runtime.log(`- ${theme.heading(provider)} ${hint}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { enablePluginInConfig } from "../plugins/enable.js";
|
||||
import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js";
|
||||
import type { ChannelChoice } from "./onboard-types.js";
|
||||
@@ -186,7 +187,7 @@ async function noteChannelPrimer(
|
||||
await prompter.note(
|
||||
[
|
||||
"DM security: default is pairing; unknown DMs get a pairing code.",
|
||||
"Approve with: clawdbot pairing approve <channel> <code>",
|
||||
`Approve with: ${formatCliCommand("clawdbot pairing approve <channel> <code>")}`,
|
||||
'Public DMs require dmPolicy="open" + allowFrom=["*"].',
|
||||
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
||||
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
||||
@@ -233,7 +234,7 @@ async function maybeConfigureDmPolicies(params: {
|
||||
await prompter.note(
|
||||
[
|
||||
"Default: pairing (unknown DMs get a pairing code).",
|
||||
`Approve: clawdbot pairing approve ${policy.channel} <code>`,
|
||||
`Approve: ${formatCliCommand(`clawdbot pairing approve ${policy.channel} <code>`)}`,
|
||||
`Allowlist DMs: ${policy.policyKey}="allowlist" + ${policy.allowFromKey} entries.`,
|
||||
`Public DMs: ${policy.policyKey}="open" + ${policy.allowFromKey} includes "*".`,
|
||||
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
||||
@@ -581,7 +582,7 @@ export async function setupChannels(
|
||||
{
|
||||
value: "__skip__",
|
||||
label: "Skip for now",
|
||||
hint: "You can add channels later via `clawdbot channels add`",
|
||||
hint: `You can add channels later via \`${formatCliCommand("clawdbot channels add")}\``,
|
||||
},
|
||||
],
|
||||
initialValue: quickstartDefault,
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { RuntimeEnv } from "../runtime.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import { buildWorkspaceHookStatus } from "../hooks/hooks-status.js";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
||||
export async function setupInternalHooks(
|
||||
cfg: ClawdbotConfig,
|
||||
@@ -73,9 +74,9 @@ export async function setupInternalHooks(
|
||||
`Enabled ${selected.length} hook${selected.length > 1 ? "s" : ""}: ${selected.join(", ")}`,
|
||||
"",
|
||||
"You can manage hooks later with:",
|
||||
" clawdbot hooks list",
|
||||
" clawdbot hooks enable <name>",
|
||||
" clawdbot hooks disable <name>",
|
||||
` ${formatCliCommand("clawdbot hooks list")}`,
|
||||
` ${formatCliCommand("clawdbot hooks enable <name>")}`,
|
||||
` ${formatCliCommand("clawdbot hooks disable <name>")}`,
|
||||
].join("\n"),
|
||||
"Hooks Configured",
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { readConfigFileSnapshot } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
@@ -12,7 +13,9 @@ export async function runNonInteractiveOnboarding(
|
||||
) {
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
if (snapshot.exists && !snapshot.valid) {
|
||||
runtime.error("Config invalid. Run `clawdbot doctor` to repair it, then re-run onboarding.");
|
||||
runtime.error(
|
||||
`Config invalid. Run \`${formatCliCommand("clawdbot doctor")}\` to repair it, then re-run onboarding.`,
|
||||
);
|
||||
runtime.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, resolveGatewayPort, writeConfigFile } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { DEFAULT_GATEWAY_DAEMON_RUNTIME } from "../daemon-runtime.js";
|
||||
import { healthCommand } from "../health.js";
|
||||
import {
|
||||
@@ -123,7 +124,7 @@ export async function runNonInteractiveOnboardingLocal(params: {
|
||||
|
||||
if (!opts.json) {
|
||||
runtime.log(
|
||||
"Tip: run `clawdbot configure --section web` to store your Brave API key for web_search. Docs: https://docs.clawd.bot/tools/web",
|
||||
`Tip: run \`${formatCliCommand("clawdbot configure --section web")}\` to store your Brave API key for web_search. Docs: https://docs.clawd.bot/tools/web`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../../config/config.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { applyWizardMetadata } from "../onboard-helpers.js";
|
||||
import type { OnboardOptions } from "../onboard-types.js";
|
||||
|
||||
@@ -45,7 +46,7 @@ export async function runNonInteractiveOnboardingRemote(params: {
|
||||
runtime.log(`Remote gateway: ${remoteUrl}`);
|
||||
runtime.log(`Auth: ${payload.auth}`);
|
||||
runtime.log(
|
||||
"Tip: run `clawdbot configure --section web` to store your Brave API key for web_search. Docs: https://docs.clawd.bot/tools/web",
|
||||
`Tip: run \`${formatCliCommand("clawdbot configure --section web")}\` to store your Brave API key for web_search. Docs: https://docs.clawd.bot/tools/web`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { installSkill } from "../agents/skills-install.js";
|
||||
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
@@ -152,7 +153,9 @@ export async function setupSkills(
|
||||
spin.stop(`Install failed: ${name}${code}${detail ? ` — ${detail}` : ""}`);
|
||||
if (result.stderr) runtime.log(result.stderr.trim());
|
||||
else if (result.stdout) runtime.log(result.stdout.trim());
|
||||
runtime.log("Tip: run `clawdbot doctor` to review skills + requirements.");
|
||||
runtime.log(
|
||||
`Tip: run \`${formatCliCommand("clawdbot doctor")}\` to review skills + requirements.`,
|
||||
);
|
||||
runtime.log("Docs: https://docs.clawd.bot/skills");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { resolveUserPath } from "../utils.js";
|
||||
import { DEFAULT_WORKSPACE, handleReset } from "./onboard-helpers.js";
|
||||
import { runInteractiveOnboarding } from "./onboard-interactive.js";
|
||||
import { runNonInteractiveOnboarding } from "./onboard-non-interactive.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { OnboardOptions } from "./onboard-types.js";
|
||||
|
||||
export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv = defaultRuntime) {
|
||||
@@ -18,7 +19,7 @@ export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv =
|
||||
[
|
||||
"Non-interactive onboarding requires explicit risk acknowledgement.",
|
||||
"Read: https://docs.clawd.bot/security",
|
||||
"Re-run with: clawdbot onboard --non-interactive --accept-risk ...",
|
||||
`Re-run with: ${formatCliCommand("clawdbot onboard --non-interactive --accept-risk ...")}`,
|
||||
].join("\n"),
|
||||
);
|
||||
runtime.exit(1);
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { resolveGatewayService } from "../daemon/service.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
collectWorkspaceDirs,
|
||||
isPathWithin,
|
||||
@@ -143,7 +144,7 @@ export async function resetCommand(runtime: RuntimeEnv, opts: ResetOptions) {
|
||||
for (const dir of sessionDirs) {
|
||||
await removePath(dir, runtime, { dryRun, label: dir });
|
||||
}
|
||||
runtime.log("Next: clawdbot onboard --install-daemon");
|
||||
runtime.log(`Next: ${formatCliCommand("clawdbot onboard --install-daemon")}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,7 +159,7 @@ export async function resetCommand(runtime: RuntimeEnv, opts: ResetOptions) {
|
||||
for (const workspace of workspaceDirs) {
|
||||
await removePath(workspace, runtime, { dryRun, label: workspace });
|
||||
}
|
||||
runtime.log("Next: clawdbot onboard --install-daemon");
|
||||
runtime.log(`Next: ${formatCliCommand("clawdbot onboard --install-daemon")}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import type { SandboxBrowserInfo, SandboxContainerInfo } from "../agents/sandbox.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import {
|
||||
formatAge,
|
||||
@@ -88,7 +89,9 @@ export function displaySummary(
|
||||
|
||||
if (mismatchCount > 0) {
|
||||
runtime.log(`\n⚠️ ${mismatchCount} container(s) with image mismatch detected.`);
|
||||
runtime.log(` Run 'clawdbot sandbox recreate --all' to update all containers.`);
|
||||
runtime.log(
|
||||
` Run '${formatCliCommand("clawdbot sandbox recreate --all")}' to update all containers.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||
import { withProgress } from "../cli/progress.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { loadConfig, readConfigFileSnapshot, resolveGatewayPort } from "../config/config.js";
|
||||
import { readLastGatewayErrorLine } from "../daemon/diagnostics.js";
|
||||
import type { GatewayService } from "../daemon/service.js";
|
||||
@@ -337,7 +338,7 @@ export async function statusAllCommand(
|
||||
Item: "Gateway",
|
||||
Value: `${gatewayMode}${remoteUrlMissing ? " (remote.url missing)" : ""} · ${gatewayTarget} (${connection.urlSource}) · ${gatewayStatus}${gatewayAuth}`,
|
||||
},
|
||||
{ Item: "Security", Value: "Run: clawdbot security audit --deep" },
|
||||
{ Item: "Security", Value: `Run: ${formatCliCommand("clawdbot security audit --deep")}` },
|
||||
gatewaySelfLine
|
||||
? { Item: "Gateway self", Value: gatewaySelfLine }
|
||||
: { Item: "Gateway self", Value: "unknown" },
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { RuntimeEnv } from "../runtime.js";
|
||||
import { runSecurityAudit } from "../security/audit.js";
|
||||
import { renderTable } from "../terminal/table.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
resolveMemoryCacheSummary,
|
||||
resolveMemoryFtsState,
|
||||
@@ -374,8 +375,8 @@ export async function statusCommand(
|
||||
runtime.log(theme.muted(`… +${sorted.length - shown.length} more`));
|
||||
}
|
||||
}
|
||||
runtime.log(theme.muted("Full report: clawdbot security audit"));
|
||||
runtime.log(theme.muted("Deep probe: clawdbot security audit --deep"));
|
||||
runtime.log(theme.muted(`Full report: ${formatCliCommand("clawdbot security audit")}`));
|
||||
runtime.log(theme.muted(`Deep probe: ${formatCliCommand("clawdbot security audit --deep")}`));
|
||||
|
||||
runtime.log("");
|
||||
runtime.log(theme.heading("Channels"));
|
||||
@@ -531,11 +532,11 @@ export async function statusCommand(
|
||||
runtime.log("");
|
||||
}
|
||||
runtime.log("Next steps:");
|
||||
runtime.log(" Need to share? clawdbot status --all");
|
||||
runtime.log(" Need to debug live? clawdbot logs --follow");
|
||||
runtime.log(` Need to share? ${formatCliCommand("clawdbot status --all")}`);
|
||||
runtime.log(` Need to debug live? ${formatCliCommand("clawdbot logs --follow")}`);
|
||||
if (gatewayReachable) {
|
||||
runtime.log(" Need to test channels? clawdbot status --deep");
|
||||
runtime.log(` Need to test channels? ${formatCliCommand("clawdbot status --deep")}`);
|
||||
} else {
|
||||
runtime.log(" Fix reachability first: clawdbot gateway status");
|
||||
runtime.log(` Fix reachability first: ${formatCliCommand("clawdbot gateway status")}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
compareSemverStrings,
|
||||
type UpdateCheckResult,
|
||||
} from "../infra/update-check.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { VERSION } from "../version.js";
|
||||
|
||||
export async function getUpdateCheckResult(params: {
|
||||
@@ -63,7 +64,7 @@ export function formatUpdateAvailableHint(update: UpdateCheckResult): string | n
|
||||
details.push(`npm ${availability.latestVersion}`);
|
||||
}
|
||||
const suffix = details.length > 0 ? ` (${details.join(" · ")})` : "";
|
||||
return `Update available${suffix}. Run: clawdbot update`;
|
||||
return `Update available${suffix}. Run: ${formatCliCommand("clawdbot update")}`;
|
||||
}
|
||||
|
||||
export function formatUpdateOneLiner(update: UpdateCheckResult): string {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
||||
export function isSystemdUnavailableDetail(detail?: string): boolean {
|
||||
if (!detail) return false;
|
||||
const normalized = detail.toLowerCase();
|
||||
@@ -20,6 +22,6 @@ export function renderSystemdUnavailableHints(options: { wsl?: boolean } = {}):
|
||||
}
|
||||
return [
|
||||
"systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.",
|
||||
"If you're in a container, run the gateway in the foreground instead of `clawdbot daemon`.",
|
||||
`If you're in a container, run the gateway in the foreground instead of \`${formatCliCommand("clawdbot daemon")}\`.`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { applyMergePatch } from "../../config/merge-patch.js";
|
||||
import { buildConfigSchema } from "../../config/schema.js";
|
||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||
import {
|
||||
DOCTOR_NONINTERACTIVE_HINT,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
} from "../../infra/restart-sentinel.js";
|
||||
@@ -336,7 +336,7 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
ts: Date.now(),
|
||||
sessionKey,
|
||||
message: note ?? null,
|
||||
doctorHint: DOCTOR_NONINTERACTIVE_HINT,
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: "config.apply",
|
||||
root: CONFIG_PATH_CLAWDBOT,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { resolveClawdbotPackageRoot } from "../../infra/clawdbot-root.js";
|
||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||
import {
|
||||
DOCTOR_NONINTERACTIVE_HINT,
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
writeRestartSentinel,
|
||||
} from "../../infra/restart-sentinel.js";
|
||||
@@ -75,7 +75,7 @@ export const updateHandlers: GatewayRequestHandlers = {
|
||||
ts: Date.now(),
|
||||
sessionKey,
|
||||
message: note ?? null,
|
||||
doctorHint: DOCTOR_NONINTERACTIVE_HINT,
|
||||
doctorHint: formatDoctorNonInteractiveHint(),
|
||||
stats: {
|
||||
mode: result.mode,
|
||||
root: result.root ?? undefined,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { registerSkillsChangeListener } from "../agents/skills/refresh.js";
|
||||
import type { CanvasHostServer } from "../canvas-host/server.js";
|
||||
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
|
||||
import { createDefaultDeps } from "../cli/deps.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
isNixMode,
|
||||
@@ -155,7 +156,7 @@ export async function startGatewayServer(
|
||||
const { config: migrated, changes } = migrateLegacyConfig(configSnapshot.parsed);
|
||||
if (!migrated) {
|
||||
throw new Error(
|
||||
'Legacy config entries detected but auto-migration failed. Run "clawdbot doctor" to migrate.',
|
||||
`Legacy config entries detected but auto-migration failed. Run "${formatCliCommand("clawdbot doctor")}" to migrate.`,
|
||||
);
|
||||
}
|
||||
await writeConfigFile(migrated);
|
||||
@@ -177,7 +178,7 @@ export async function startGatewayServer(
|
||||
.join("\n")
|
||||
: "Unknown validation issue.";
|
||||
throw new Error(
|
||||
`Invalid config at ${configSnapshot.path}.\n${issues}\nRun "clawdbot doctor" to repair, then retry.`,
|
||||
`Invalid config at ${configSnapshot.path}.\n${issues}\nRun "${formatCliCommand("clawdbot doctor")}" to repair, then retry.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "../config/config.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
buildDefaultHookUrl,
|
||||
buildGogWatchServeArgs,
|
||||
@@ -276,7 +277,7 @@ export async function runGmailSetup(opts: GmailSetupOptions) {
|
||||
defaultRuntime.log(`- push endpoint: ${pushEndpoint}`);
|
||||
defaultRuntime.log(`- hook url: ${hookUrl}`);
|
||||
defaultRuntime.log(`- config: ${CONFIG_PATH_CLAWDBOT}`);
|
||||
defaultRuntime.log("Next: clawdbot webhooks gmail run");
|
||||
defaultRuntime.log(`Next: ${formatCliCommand("clawdbot webhooks gmail run")}`);
|
||||
}
|
||||
|
||||
export async function runGmailService(opts: GmailRunOptions) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import type { ChannelId, ChannelOutboundTargetMode } from "../../channels/plugins/types.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
@@ -109,7 +110,7 @@ export function resolveOutboundTarget(params: {
|
||||
return {
|
||||
ok: false,
|
||||
error: new Error(
|
||||
"Delivering to WebChat is not supported via `clawdbot agent`; use WhatsApp/Telegram or run with --deliver=false.",
|
||||
`Delivering to WebChat is not supported via \`${formatCliCommand("clawdbot agent")}\`; use WhatsApp/Telegram or run with --deliver=false.`,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js";
|
||||
|
||||
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
|
||||
@@ -20,7 +21,7 @@ export function buildPortHints(listeners: PortListener[], port: number): string[
|
||||
const hints: string[] = [];
|
||||
if (kinds.has("gateway")) {
|
||||
hints.push(
|
||||
"Gateway already running locally. Stop it (clawdbot daemon stop) or use a different port.",
|
||||
`Gateway already running locally. Stop it (${formatCliCommand("clawdbot daemon stop")}) or use a different port.`,
|
||||
);
|
||||
}
|
||||
if (kinds.has("ssh")) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
|
||||
export type RestartSentinelLog = {
|
||||
@@ -44,7 +45,11 @@ export type RestartSentinel = {
|
||||
|
||||
const SENTINEL_FILENAME = "restart-sentinel.json";
|
||||
|
||||
export const DOCTOR_NONINTERACTIVE_HINT = "Run: clawdbot doctor --non-interactive";
|
||||
export function formatDoctorNonInteractiveHint(
|
||||
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
|
||||
): string {
|
||||
return `Run: ${formatCliCommand("clawdbot doctor --non-interactive", env)}`;
|
||||
}
|
||||
|
||||
export function resolveRestartSentinelPath(env: NodeJS.ProcessEnv = process.env): string {
|
||||
return path.join(resolveStateDir(env), SENTINEL_FILENAME);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { danger, info, logVerbose, shouldLogVerbose, warn } from "../globals.js"
|
||||
import { runExec } from "../process/exec.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { ensureBinary } from "./binaries.js";
|
||||
|
||||
function parsePossiblyNoisyJsonObject(stdout: string): Record<string, unknown> {
|
||||
@@ -268,7 +269,7 @@ export async function ensureFunnel(
|
||||
runtime.error("Failed to enable Tailscale Funnel. Is it allowed on your tailnet?");
|
||||
runtime.error(
|
||||
info(
|
||||
"Tip: Funnel is optional for CLAWDBOT. You can keep running the web gateway without it: `pnpm clawdbot gateway`",
|
||||
`Tip: Funnel is optional for CLAWDBOT. You can keep running the web gateway without it: \`${formatCliCommand("clawdbot gateway")}\``,
|
||||
),
|
||||
);
|
||||
if (shouldLogVerbose()) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { resolveStateDir } from "../config/paths.js";
|
||||
import { resolveClawdbotPackageRoot } from "./clawdbot-root.js";
|
||||
import { compareSemverStrings, fetchNpmTagVersion, checkUpdateStatus } from "./update-check.js";
|
||||
import { VERSION } from "../version.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
||||
type UpdateCheckState = {
|
||||
lastCheckedAt?: string;
|
||||
@@ -102,7 +103,7 @@ export async function runGatewayUpdateCheck(params: {
|
||||
state.lastNotifiedVersion !== tagStatus.version || state.lastNotifiedTag !== tag;
|
||||
if (shouldNotify) {
|
||||
params.log.info(
|
||||
`update available (${tag}): v${tagStatus.version} (current v${VERSION}). Run: clawdbot update`,
|
||||
`update available (${tag}): v${tagStatus.version} (current v${VERSION}). Run: ${formatCliCommand("clawdbot update")}`,
|
||||
);
|
||||
nextState.lastNotifiedVersion = tagStatus.version;
|
||||
nextState.lastNotifiedTag = tag;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ensurePortAvailable, PortInUseError } from "../infra/ports.js";
|
||||
import { getTailnetHostname } from "../infra/tailscale.js";
|
||||
import { logInfo } from "../logger.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { startMediaServer } from "./server.js";
|
||||
import { saveMediaSource } from "./store.js";
|
||||
|
||||
@@ -36,7 +37,7 @@ export async function ensureMediaHosted(
|
||||
if (needsServerStart && !opts.startServer) {
|
||||
await fs.rm(saved.path).catch(() => {});
|
||||
throw new Error(
|
||||
"Media hosting requires the webhook/Funnel server. Start `clawdbot webhook`/`clawdbot up` or re-run with --serve-media.",
|
||||
`Media hosting requires the webhook/Funnel server. Start \`${formatCliCommand("clawdbot webhook")}\`/\`${formatCliCommand("clawdbot up")}\` or re-run with --serve-media.`,
|
||||
);
|
||||
}
|
||||
if (needsServerStart && opts.startServer) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { PairingChannel } from "./pairing-store.js";
|
||||
|
||||
export function buildPairingReply(params: {
|
||||
@@ -14,6 +15,6 @@ export function buildPairingReply(params: {
|
||||
`Pairing code: ${code}`,
|
||||
"",
|
||||
"Ask the bot owner to approve with:",
|
||||
`clawdbot pairing approve ${channel} <code>`,
|
||||
formatCliCommand(`clawdbot pairing approve ${channel} <code>`),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
|
||||
const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
|
||||
const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`;
|
||||
@@ -28,7 +29,7 @@ export async function refreshQwenPortalCredentials(
|
||||
const text = await response.text();
|
||||
if (response.status === 400) {
|
||||
throw new Error(
|
||||
"Qwen OAuth refresh token expired or invalid. Re-authenticate with `clawdbot models auth login --provider qwen-portal`.",
|
||||
`Qwen OAuth refresh token expired or invalid. Re-authenticate with \`${formatCliCommand("clawdbot models auth login --provider qwen-portal")}\`.`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Qwen OAuth refresh failed: ${text || response.statusText}`);
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { ClawdbotConfig, ConfigFileSnapshot } from "../config/config.js";
|
||||
import { createConfigIO } from "../config/config.js";
|
||||
import { resolveNativeSkillsEnabled } from "../config/commands.js";
|
||||
import { resolveOAuthDir } from "../config/paths.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { INCLUDE_KEY, MAX_INCLUDE_DEPTH } from "../config/includes.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
@@ -105,7 +106,7 @@ export function collectSyncedFolderFindings(params: {
|
||||
severity: "warn",
|
||||
title: "State/config path looks like a synced folder",
|
||||
detail: `stateDir=${params.stateDir}, configPath=${params.configPath}. Synced folders (iCloud/Dropbox/OneDrive/Google Drive) can leak tokens and transcripts onto other devices.`,
|
||||
remediation: `Keep CLAWDBOT_STATE_DIR on a local-only volume and re-run "clawdbot security audit --fix".`,
|
||||
remediation: `Keep CLAWDBOT_STATE_DIR on a local-only volume and re-run "${formatCliCommand("clawdbot security audit --fix")}".`,
|
||||
});
|
||||
}
|
||||
return findings;
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { resolveBrowserConfig } from "../browser/config.js";
|
||||
import { resolveConfigPath, resolveStateDir } from "../config/paths.js";
|
||||
import { resolveGatewayAuth } from "../gateway/auth.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { buildGatewayConnectionDetails } from "../gateway/call.js";
|
||||
import { probeGateway } from "../gateway/probe.js";
|
||||
import {
|
||||
@@ -264,7 +265,7 @@ function collectBrowserControlFindings(cfg: ClawdbotConfig): SecurityAuditFindin
|
||||
severity: "warn",
|
||||
title: "Browser control config looks invalid",
|
||||
detail: String(err),
|
||||
remediation: `Fix browser.controlUrl/browser.cdpUrl in ${resolveConfigPath()} and re-run "clawdbot security audit --deep".`,
|
||||
remediation: `Fix browser.controlUrl/browser.cdpUrl in ${resolveConfigPath()} and re-run "${formatCliCommand("clawdbot security audit --deep")}".`,
|
||||
});
|
||||
return findings;
|
||||
}
|
||||
@@ -840,7 +841,7 @@ export async function runSecurityAudit(opts: SecurityAuditOptions): Promise<Secu
|
||||
severity: "warn",
|
||||
title: "Gateway probe failed (deep)",
|
||||
detail: deep.gateway.error ?? "gateway unreachable",
|
||||
remediation: `Run "clawdbot status --all" to debug connectivity/auth, then re-run "clawdbot security audit --deep".`,
|
||||
remediation: `Run "${formatCliCommand("clawdbot status --all")}" to debug connectivity/auth, then re-run "${formatCliCommand("clawdbot security audit --deep")}".`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js";
|
||||
import { buildMentionRegexes, matchesMentionPatterns } from "../auto-reply/reply/mentions.js";
|
||||
import { formatLocationText, toLocationContext } from "../channels/location.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import {
|
||||
readSessionUpdatedAt,
|
||||
recordSessionMetaFromInbound,
|
||||
@@ -234,7 +235,7 @@ export const buildTelegramMessageContext = async ({
|
||||
`Pairing code: ${code}`,
|
||||
"",
|
||||
"Ask the bot owner to approve with:",
|
||||
"clawdbot pairing approve telegram <code>",
|
||||
formatCliCommand("clawdbot pairing approve telegram <code>"),
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { PollInput } from "../polls.js";
|
||||
import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js";
|
||||
|
||||
@@ -42,7 +43,7 @@ export function requireActiveWebListener(accountId?: string | null): {
|
||||
const listener = listeners.get(id) ?? null;
|
||||
if (!listener) {
|
||||
throw new Error(
|
||||
`No active WhatsApp Web listener (account: ${id}). Start the gateway, then link WhatsApp with: clawdbot channels login --channel whatsapp --account ${id}.`,
|
||||
`No active WhatsApp Web listener (account: ${id}). Start the gateway, then link WhatsApp with: ${formatCliCommand(`clawdbot channels login --channel whatsapp --account ${id}`)}.`,
|
||||
);
|
||||
}
|
||||
return { accountId: id, listener };
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user