refactor: normalize cli command hints
This commit is contained in:
parent
11b9b6dba5
commit
6d5195c890
@ -117,8 +117,8 @@ Send these as standalone messages so they register.
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Inspecting
|
## Inspecting
|
||||||
- `pnpm clawdbot status` — shows store path and recent sessions.
|
- `clawdbot status` — shows store path and recent sessions.
|
||||||
- `pnpm clawdbot sessions --json` — dumps every entry (filter with `--active <minutes>`).
|
- `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).
|
- `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 `/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).
|
- 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
|
## Regression history
|
||||||
- `2871657e` (2026-01-06): scripts changed from Bun to tsx to make Bun optional.
|
- `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
|
## Workarounds
|
||||||
- Use Bun for dev scripts (current temporary revert).
|
- Use Bun for dev scripts (current temporary revert).
|
||||||
|
|||||||
@ -59,9 +59,11 @@ Recommended flow (dev profile + dev bootstrap):
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm gateway:dev
|
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:
|
What this does:
|
||||||
|
|
||||||
1) **Profile isolation** (global `--dev`)
|
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:
|
If you need to spell it out, use the env var form:
|
||||||
|
|
||||||
```bash
|
```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
|
`--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?
|
### Build errors on `main` — what’s the standard fix path?
|
||||||
|
|
||||||
1) `git pull origin main && pnpm install`
|
1) `git pull origin main && pnpm install`
|
||||||
2) `pnpm clawdbot doctor`
|
2) `clawdbot doctor`
|
||||||
3) Check GitHub issues or Discord
|
3) Check GitHub issues or Discord
|
||||||
4) Temporary workaround: check out an older commit
|
4) Temporary workaround: check out an older commit
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ Typical recovery:
|
|||||||
git status # ensure you’re in the repo root
|
git status # ensure you’re in the repo root
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm clawdbot doctor
|
clawdbot doctor
|
||||||
clawdbot daemon restart
|
clawdbot daemon restart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -123,9 +123,11 @@ cd clawdbot
|
|||||||
pnpm install
|
pnpm install
|
||||||
pnpm ui:build # auto-installs UI deps on first run
|
pnpm ui:build # auto-installs UI deps on first run
|
||||||
pnpm build
|
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):
|
Multi-instance quickstart (optional):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -7,40 +7,43 @@ read_when:
|
|||||||
|
|
||||||
# Install
|
# 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.
|
## Quick install (recommended)
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://clawd.bot/install.sh | 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
|
```bash
|
||||||
curl -fsSL https://clawd.bot/install.sh | bash -s -- --help
|
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
|
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:
|
The installer supports two methods:
|
||||||
|
|
||||||
@ -92,28 +148,28 @@ Equivalent env vars (useful for automation):
|
|||||||
- `CLAWDBOT_NO_ONBOARD=1`
|
- `CLAWDBOT_NO_ONBOARD=1`
|
||||||
- `SHARP_IGNORE_GLOBAL_LIBVIPS=0|1` (default: `1`; avoids `sharp` building against system libvips)
|
- `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
|
```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
|
```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
|
## Update / uninstall
|
||||||
pnpm add -g clawdbot@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
Then:
|
- Updates: [Updating](/install/updating)
|
||||||
|
- Uninstall: [Uninstall](/install/uninstall)
|
||||||
```bash
|
|
||||||
clawdbot onboard --install-daemon
|
|
||||||
```
|
|
||||||
|
|||||||
@ -118,7 +118,7 @@ Remove it with `npm rm -g clawdbot` (or `pnpm remove -g` / `bun remove -g` if yo
|
|||||||
|
|
||||||
### Source checkout (git clone)
|
### 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).
|
1) Uninstall the gateway service **before** deleting the repo (use the easy path above or manual service removal).
|
||||||
2) Delete the repo directory.
|
2) Delete the repo directory.
|
||||||
|
|||||||
@ -119,12 +119,13 @@ git pull
|
|||||||
pnpm install
|
pnpm install
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm ui:build # auto-installs UI deps on first run
|
pnpm ui:build # auto-installs UI deps on first run
|
||||||
pnpm clawdbot doctor
|
clawdbot doctor
|
||||||
pnpm clawdbot health
|
clawdbot health
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
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/`.
|
- `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.
|
- 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.
|
- 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`)
|
- `--timeout <ms>`: overall discovery window (default `2000`)
|
||||||
- `--json`: structured output for diffing
|
- `--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
|
macOS app’s discovery pipeline (NWBrowser + tailnet DNS‑SD fallback) differs from
|
||||||
the Node CLI’s `dns-sd` based discovery.
|
the Node CLI’s `dns-sd` based discovery.
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ cd clawdbot
|
|||||||
pnpm install
|
pnpm install
|
||||||
pnpm ui:build # auto-installs UI deps on first run
|
pnpm ui:build # auto-installs UI deps on first run
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm clawdbot onboard
|
clawdbot onboard
|
||||||
```
|
```
|
||||||
|
|
||||||
Full guide: [Getting Started](/start/getting-started)
|
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:
|
The repo recommends running from source and using the onboarding wizard:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/clawdbot/clawdbot.git
|
curl -fsSL https://clawd.bot/install.sh | bash
|
||||||
cd clawdbot
|
clawdbot onboard --install-daemon
|
||||||
|
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The wizard can also build UI assets automatically. After onboarding, you typically run the Gateway on port **18789**.
|
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?
|
### 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.
|
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
|
cd clawdbot
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm clawdbot doctor
|
clawdbot doctor
|
||||||
clawdbot daemon restart
|
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
|
⚠️ **Bun warning (WhatsApp + Telegram):** Bun has known issues with these
|
||||||
channels. If you use WhatsApp or Telegram, run the Gateway with **Node**.
|
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
|
## 4) Pair + connect your first chat surface
|
||||||
|
|
||||||
### WhatsApp (QR login)
|
### WhatsApp (QR login)
|
||||||
@ -158,9 +165,11 @@ cd clawdbot
|
|||||||
pnpm install
|
pnpm install
|
||||||
pnpm ui:build # auto-installs UI deps on first run
|
pnpm ui:build # auto-installs UI deps on first run
|
||||||
pnpm build
|
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):
|
Gateway (from this repo):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -169,15 +178,13 @@ node dist/entry.js gateway --port 18789 --verbose
|
|||||||
|
|
||||||
## 7) Verify end-to-end
|
## 7) Verify end-to-end
|
||||||
|
|
||||||
In a new terminal:
|
In a new terminal, send a test message:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
clawdbot status
|
|
||||||
clawdbot health
|
|
||||||
clawdbot message send --target +15555550123 --message "Hello from Clawdbot"
|
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.
|
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.
|
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:
|
From inside this repo, use the local CLI entry:
|
||||||
|
|
||||||
```bash
|
```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)
|
## Stable workflow (macOS app first)
|
||||||
|
|
||||||
1) Install + launch **Clawdbot.app** (menu bar).
|
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:
|
- Or via CLI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm clawdbot health
|
clawdbot health
|
||||||
```
|
```
|
||||||
|
|
||||||
### Common footguns
|
### 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:
|
Tip: to see what you can test on your machine (and the exact `provider/model` ids), run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm clawdbot models list
|
clawdbot models list
|
||||||
pnpm clawdbot models list --json
|
clawdbot models list --json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Live: Anthropic setup-token smoke
|
## Live: Anthropic setup-token smoke
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import { normalizeProviderId } from "../model-selection.js";
|
import { normalizeProviderId } from "../model-selection.js";
|
||||||
import { listProfilesForProvider } from "./profiles.js";
|
import { listProfilesForProvider } from "./profiles.js";
|
||||||
@ -37,6 +38,6 @@ export function formatAuthDoctorHint(params: {
|
|||||||
}`,
|
}`,
|
||||||
`- auth store oauth profiles: ${storeOauthProfiles || "(none)"}`,
|
`- auth store oauth profiles: ${storeOauthProfiles || "(none)"}`,
|
||||||
`- suggested profile: ${suggested}`,
|
`- suggested profile: ${suggested}`,
|
||||||
'Fix: run "clawdbot doctor --yes"',
|
`Fix: run "${formatCliCommand("clawdbot doctor --yes")}"`,
|
||||||
].join("\n");
|
].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 { ClawdbotConfig } from "../config/config.js";
|
||||||
import type { ModelProviderConfig } from "../config/types.js";
|
import type { ModelProviderConfig } from "../config/types.js";
|
||||||
import { getShellEnvAppliedKeys } from "../infra/shell-env.js";
|
import { getShellEnvAppliedKeys } from "../infra/shell-env.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
type AuthProfileStore,
|
type AuthProfileStore,
|
||||||
ensureAuthProfileStore,
|
ensureAuthProfileStore,
|
||||||
@ -103,7 +104,7 @@ export async function resolveApiKeyForProvider(params: {
|
|||||||
[
|
[
|
||||||
`No API key found for provider "${provider}".`,
|
`No API key found for provider "${provider}".`,
|
||||||
`Auth store: ${authStorePath} (agentDir: ${resolvedAgentDir}).`,
|
`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(" "),
|
].join(" "),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
|
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { DEFAULT_SANDBOX_IMAGE, SANDBOX_AGENT_WORKSPACE_MOUNT } from "./constants.js";
|
import { DEFAULT_SANDBOX_IMAGE, SANDBOX_AGENT_WORKSPACE_MOUNT } from "./constants.js";
|
||||||
import { readRegistry, updateRegistry } from "./registry.js";
|
import { readRegistry, updateRegistry } from "./registry.js";
|
||||||
import { computeSandboxConfigHash } from "./config-hash.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 }) {
|
function formatSandboxRecreateHint(params: { scope: SandboxConfig["scope"]; sessionKey: string }) {
|
||||||
if (params.scope === "session") {
|
if (params.scope === "session") {
|
||||||
return `clawdbot sandbox recreate --session ${params.sessionKey}`;
|
return formatCliCommand(`clawdbot sandbox recreate --session ${params.sessionKey}`);
|
||||||
}
|
}
|
||||||
if (params.scope === "agent") {
|
if (params.scope === "agent") {
|
||||||
const agentId = resolveSandboxAgentId(params.sessionKey) ?? "main";
|
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: {
|
export async function ensureSandboxContainer(params: {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { ClawdbotConfig } from "../../config/config.js";
|
|||||||
import { canonicalizeMainSessionAlias, resolveAgentMainSessionKey } from "../../config/sessions.js";
|
import { canonicalizeMainSessionAlias, resolveAgentMainSessionKey } from "../../config/sessions.js";
|
||||||
import { resolveSessionAgentId } from "../agent-scope.js";
|
import { resolveSessionAgentId } from "../agent-scope.js";
|
||||||
import { expandToolGroups } from "../tool-policy.js";
|
import { expandToolGroups } from "../tool-policy.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { resolveSandboxConfigForAgent } from "./config.js";
|
import { resolveSandboxConfigForAgent } from "./config.js";
|
||||||
import { resolveSandboxToolPolicyForAgent } from "./tool-policy.js";
|
import { resolveSandboxToolPolicyForAgent } from "./tool-policy.js";
|
||||||
import type { SandboxConfig, SandboxToolPolicyResolved } from "./types.js";
|
import type { SandboxConfig, SandboxToolPolicyResolved } from "./types.js";
|
||||||
@ -115,7 +116,9 @@ export function formatSandboxToolPolicyBlockedMessage(params: {
|
|||||||
if (runtime.mode === "non-main") {
|
if (runtime.mode === "non-main") {
|
||||||
lines.push(`- Use main session key (direct): ${runtime.mainSessionKey}`);
|
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");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { ReasoningLevel, ThinkLevel } from "../auto-reply/thinking.js";
|
import type { ReasoningLevel, ThinkLevel } from "../auto-reply/thinking.js";
|
||||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.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 { listDeliverableMessageChannels } from "../utils/message-channel.js";
|
||||||
import type { ResolvedTimeFormat } from "./date-time.js";
|
import type { ResolvedTimeFormat } from "./date-time.js";
|
||||||
import type { EmbeddedContextFile } from "./pi-embedded-helpers.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",
|
"Community: https://discord.com/invite/clawd",
|
||||||
"Find new skills: https://clawdhub.com",
|
"Find new skills: https://clawdhub.com",
|
||||||
"For Clawdbot behavior, commands, config, or architecture: consult local docs first.",
|
"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 CLI Quick Reference",
|
||||||
"Clawdbot is controlled via subcommands. Do not invent commands.",
|
"Clawdbot is controlled via subcommands. Do not invent commands.",
|
||||||
"To manage the Gateway daemon service (start/stop/restart):",
|
"To manage the Gateway daemon service (start/stop/restart):",
|
||||||
"- clawdbot daemon status",
|
`- ${formatCliCommand("clawdbot daemon status")}`,
|
||||||
"- clawdbot daemon start",
|
`- ${formatCliCommand("clawdbot daemon start")}`,
|
||||||
"- clawdbot daemon stop",
|
`- ${formatCliCommand("clawdbot daemon stop")}`,
|
||||||
"- clawdbot daemon restart",
|
`- ${formatCliCommand("clawdbot daemon restart")}`,
|
||||||
"If unsure, ask the user to run `clawdbot help` (or `clawdbot daemon --help`) and paste the output.",
|
`If unsure, ask the user to run \`${formatCliCommand("clawdbot help")}\` (or \`${formatCliCommand("clawdbot daemon --help")}\`) and paste the output.`,
|
||||||
"",
|
"",
|
||||||
...skillsSection,
|
...skillsSection,
|
||||||
...memorySection,
|
...memorySection,
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { Type } from "@sinclair/typebox";
|
|||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||||
import {
|
import {
|
||||||
DOCTOR_NONINTERACTIVE_HINT,
|
formatDoctorNonInteractiveHint,
|
||||||
type RestartSentinelPayload,
|
type RestartSentinelPayload,
|
||||||
writeRestartSentinel,
|
writeRestartSentinel,
|
||||||
} from "../../infra/restart-sentinel.js";
|
} from "../../infra/restart-sentinel.js";
|
||||||
@ -83,7 +83,7 @@ export function createGatewayTool(opts?: {
|
|||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
sessionKey,
|
sessionKey,
|
||||||
message: note ?? reason ?? null,
|
message: note ?? reason ?? null,
|
||||||
doctorHint: DOCTOR_NONINTERACTIVE_HINT,
|
doctorHint: formatDoctorNonInteractiveHint(),
|
||||||
stats: {
|
stats: {
|
||||||
mode: "gateway.restart",
|
mode: "gateway.restart",
|
||||||
reason,
|
reason,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
|
|
||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import type { AnyAgentTool } from "./common.js";
|
import type { AnyAgentTool } from "./common.js";
|
||||||
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
|
import { jsonResult, readNumberParam, readStringParam } from "./common.js";
|
||||||
import {
|
import {
|
||||||
@ -124,8 +125,7 @@ function missingSearchKeyPayload(provider: (typeof SEARCH_PROVIDERS)[number]) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
error: "missing_brave_api_key",
|
error: "missing_brave_api_key",
|
||||||
message:
|
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.`,
|
||||||
"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.",
|
|
||||||
docs: "https://docs.clawd.bot/tools/web",
|
docs: "https://docs.clawd.bot/tools/web",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import path from "node:path";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
import { isSubagentSessionKey } from "../routing/session-key.js";
|
import { isSubagentSessionKey } from "../routing/session-key.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { resolveUserPath } from "../utils.js";
|
import { resolveUserPath } from "../utils.js";
|
||||||
|
|
||||||
export function resolveDefaultAgentWorkspaceDir(
|
export function resolveDefaultAgentWorkspaceDir(
|
||||||
@ -135,7 +136,7 @@ After the user chooses, update:
|
|||||||
- Notes
|
- Notes
|
||||||
|
|
||||||
3) ~/.clawdbot/clawdbot.json
|
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>.
|
If multiple agents share a host, add --agent <id>.
|
||||||
|
|
||||||
## Cleanup
|
## Cleanup
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { createExecTool } from "../../agents/bash-tools.js";
|
|||||||
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
|
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
|
||||||
import { killProcessTree } from "../../agents/shell-utils.js";
|
import { killProcessTree } from "../../agents/shell-utils.js";
|
||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { logVerbose } from "../../globals.js";
|
import { logVerbose } from "../../globals.js";
|
||||||
import { clampInt } from "../../utils.js";
|
import { clampInt } from "../../utils.js";
|
||||||
import type { MsgContext } from "../templating.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.enabled");
|
||||||
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
||||||
if (params.sessionKey) {
|
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");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import type { ElevatedLevel, ReasoningLevel } from "./directives.js";
|
import type { ElevatedLevel, ReasoningLevel } from "./directives.js";
|
||||||
|
|
||||||
export const SYSTEM_MARK = "⚙️";
|
export const SYSTEM_MARK = "⚙️";
|
||||||
@ -44,7 +45,9 @@ export function formatElevatedUnavailableText(params: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (params.sessionKey) {
|
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");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { normalizeChannelId } from "../../channels/plugins/index.js";
|
|||||||
import { CHAT_CHANNEL_ORDER } from "../../channels/registry.js";
|
import { CHAT_CHANNEL_ORDER } from "../../channels/registry.js";
|
||||||
import type { AgentElevatedAllowFromConfig, ClawdbotConfig } from "../../config/config.js";
|
import type { AgentElevatedAllowFromConfig, ClawdbotConfig } from "../../config/config.js";
|
||||||
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
|
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import type { MsgContext } from "../templating.js";
|
import type { MsgContext } from "../templating.js";
|
||||||
|
|
||||||
function normalizeAllowToken(value?: string) {
|
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.enabled");
|
||||||
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
||||||
if (params.sessionKey) {
|
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");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { extractErrorCode, formatErrorMessage } from "../infra/errors.js";
|
import { extractErrorCode, formatErrorMessage } from "../infra/errors.js";
|
||||||
import { loadConfig } from "../config/config.js";
|
import { loadConfig } from "../config/config.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { resolveBrowserConfig } from "./config.js";
|
import { resolveBrowserConfig } from "./config.js";
|
||||||
|
|
||||||
let cachedConfigToken: string | null | undefined = undefined;
|
let cachedConfigToken: string | null | undefined = undefined;
|
||||||
@ -30,8 +31,7 @@ function enhanceBrowserFetchError(url: string, err: unknown, timeoutMs: number):
|
|||||||
const cause = unwrapCause(err);
|
const cause = unwrapCause(err);
|
||||||
const code = extractErrorCode(cause) ?? extractErrorCode(err) ?? "";
|
const code = extractErrorCode(cause) ?? extractErrorCode(err) ?? "";
|
||||||
|
|
||||||
const hint =
|
const hint = `Start (or restart) the Clawdbot gateway (Clawdbot.app menubar, or \`${formatCliCommand("clawdbot gateway")}\`) and try again.`;
|
||||||
"Start (or restart) the Clawdbot gateway (Clawdbot.app menubar, or `clawdbot gateway`) and try again.";
|
|
||||||
|
|
||||||
if (code === "ECONNREFUSED") {
|
if (code === "ECONNREFUSED") {
|
||||||
return new Error(
|
return new Error(
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { ensurePageState, getPageForTargetId } from "./pw-session.js";
|
import { ensurePageState, getPageForTargetId } from "./pw-session.js";
|
||||||
import { normalizeTimeoutMs } from "./pw-tools-core.shared.js";
|
import { normalizeTimeoutMs } from "./pw-tools-core.shared.js";
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ export async function responseBodyViaPlaywright(opts: {
|
|||||||
cleanup();
|
cleanup();
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
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);
|
}, timeout);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
||||||
import type { ChannelPlugin } from "./types.js";
|
import type { ChannelPlugin } from "./types.js";
|
||||||
@ -13,5 +14,7 @@ export function resolveChannelDefaultAccountId<ResolvedAccount>(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatPairingApproveHint(channelId: string): string {
|
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,
|
resolveSignalAccount,
|
||||||
} from "../../../signal/accounts.js";
|
} from "../../../signal/accounts.js";
|
||||||
import { formatDocsLink } from "../../../terminal/links.js";
|
import { formatDocsLink } from "../../../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||||
import { normalizeE164 } from "../../../utils.js";
|
import { normalizeE164 } from "../../../utils.js";
|
||||||
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
||||||
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.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"',
|
'Link device with: signal-cli link -n "Clawdbot"',
|
||||||
"Scan QR in Signal → Linked Devices",
|
"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")}`,
|
`Docs: ${formatDocsLink("/signal", "signal")}`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
"Signal next steps",
|
"Signal next steps",
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
resolveTelegramAccount,
|
resolveTelegramAccount,
|
||||||
} from "../../../telegram/accounts.js";
|
} from "../../../telegram/accounts.js";
|
||||||
import { formatDocsLink } from "../../../terminal/links.js";
|
import { formatDocsLink } from "../../../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||||
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
import type { WizardPrompter } from "../../../wizard/prompts.js";
|
||||||
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
|
||||||
import { addWildcardAllowFrom, promptAccountId } from "./helpers.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> {
|
async function noteTelegramUserIdHelp(prompter: WizardPrompter): Promise<void> {
|
||||||
await prompter.note(
|
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",
|
"2) Or call https://api.telegram.org/bot<bot_token>/getUpdates and read message.from.id",
|
||||||
"3) Third-party: DM @userinfobot or @getidsbot",
|
"3) Third-party: DM @userinfobot or @getidsbot",
|
||||||
`Docs: ${formatDocsLink("/telegram")}`,
|
`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 { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../../../runtime.js";
|
import type { RuntimeEnv } from "../../../runtime.js";
|
||||||
import { formatDocsLink } from "../../../terminal/links.js";
|
import { formatDocsLink } from "../../../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "../../../cli/command-format.js";
|
||||||
import { normalizeE164 } from "../../../utils.js";
|
import { normalizeE164 } from "../../../utils.js";
|
||||||
import {
|
import {
|
||||||
listWhatsAppAccountIds,
|
listWhatsAppAccountIds,
|
||||||
@ -321,7 +322,10 @@ export const whatsappOnboardingAdapter: ChannelOnboardingAdapter = {
|
|||||||
await prompter.note(`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`, "WhatsApp help");
|
await prompter.note(`Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`, "WhatsApp help");
|
||||||
}
|
}
|
||||||
} else if (!linked) {
|
} 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, {
|
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 type { ChannelAccountSnapshot, ChannelStatusIssue } from "../types.js";
|
||||||
import { asString, isRecord } from "./shared.js";
|
import { asString, isRecord } from "./shared.js";
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ export function collectWhatsAppStatusIssues(
|
|||||||
accountId,
|
accountId,
|
||||||
kind: "auth",
|
kind: "auth",
|
||||||
message: "Not linked (no WhatsApp Web session).",
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
@ -58,7 +59,7 @@ export function collectWhatsAppStatusIssues(
|
|||||||
accountId,
|
accountId,
|
||||||
kind: "runtime",
|
kind: "runtime",
|
||||||
message: `Linked but disconnected${reconnectAttempts != null ? ` (reconnectAttempts=${reconnectAttempts})` : ""}${lastError ? `: ${lastError}` : "."}`,
|
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 { movePathToTrash } from "../browser/trash.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
|
|
||||||
function bundledExtensionRootDir() {
|
function bundledExtensionRootDir() {
|
||||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||||
@ -103,7 +104,7 @@ export function registerBrowserExtensionCommands(
|
|||||||
defaultRuntime.error(
|
defaultRuntime.error(
|
||||||
danger(
|
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")}`,
|
`Docs: ${formatDocsLink("/tools/chrome-extension", "docs.clawd.bot/tools/chrome-extension")}`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { danger } from "../globals.js";
|
|||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
import { registerBrowserActionInputCommands } from "./browser-cli-actions-input.js";
|
import { registerBrowserActionInputCommands } from "./browser-cli-actions-input.js";
|
||||||
import { registerBrowserActionObserveCommands } from "./browser-cli-actions-observe.js";
|
import { registerBrowserActionObserveCommands } from "./browser-cli-actions-observe.js";
|
||||||
import { registerBrowserDebugCommands } from "./browser-cli-debug.js";
|
import { registerBrowserDebugCommands } from "./browser-cli-debug.js";
|
||||||
@ -32,7 +33,9 @@ export function registerBrowserCli(program: Command) {
|
|||||||
)
|
)
|
||||||
.action(() => {
|
.action(() => {
|
||||||
browser.outputHelp();
|
browser.outputHelp();
|
||||||
defaultRuntime.error(danger('Missing subcommand. Try: "clawdbot browser status"'));
|
defaultRuntime.error(
|
||||||
|
danger(`Missing subcommand. Try: "${formatCliCommand("clawdbot browser status")}"`),
|
||||||
|
);
|
||||||
defaultRuntime.exit(1);
|
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 { danger, info } from "../globals.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
|
||||||
type PathSegment = string;
|
type PathSegment = string;
|
||||||
@ -171,7 +172,7 @@ async function loadValidConfig() {
|
|||||||
for (const issue of snapshot.issues) {
|
for (const issue of snapshot.issues) {
|
||||||
defaultRuntime.error(`- ${issue.path || "<root>"}: ${issue.message}`);
|
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);
|
defaultRuntime.exit(1);
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { loadConfig, resolveGatewayPort } from "../../config/config.js";
|
|||||||
import { resolveIsNixMode } from "../../config/paths.js";
|
import { resolveIsNixMode } from "../../config/paths.js";
|
||||||
import { resolveGatewayService } from "../../daemon/service.js";
|
import { resolveGatewayService } from "../../daemon/service.js";
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
|
import { formatCliCommand } from "../command-format.js";
|
||||||
import { buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson } from "./response.js";
|
import { buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson } from "./response.js";
|
||||||
import { parsePort } from "./shared.js";
|
import { parsePort } from "./shared.js";
|
||||||
import type { DaemonInstallOptions } from "./types.js";
|
import type { DaemonInstallOptions } from "./types.js";
|
||||||
@ -82,7 +83,9 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
|||||||
});
|
});
|
||||||
if (!json) {
|
if (!json) {
|
||||||
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
} from "../../daemon/constants.js";
|
} from "../../daemon/constants.js";
|
||||||
import { resolveGatewayLogPaths } from "../../daemon/launchd.js";
|
import { resolveGatewayLogPaths } from "../../daemon/launchd.js";
|
||||||
import { getResolvedLoggerSettings } from "../../logging.js";
|
import { getResolvedLoggerSettings } from "../../logging.js";
|
||||||
|
import { formatCliCommand } from "../command-format.js";
|
||||||
|
|
||||||
export function parsePort(raw: unknown): number | null {
|
export function parsePort(raw: unknown): number | null {
|
||||||
if (raw === undefined || raw === null) return null;
|
if (raw === undefined || raw === null) return null;
|
||||||
@ -122,7 +123,7 @@ export function renderRuntimeHints(
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
if (runtime.missingUnit) {
|
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}`);
|
if (fileLog) hints.push(`File logs: ${fileLog}`);
|
||||||
return hints;
|
return hints;
|
||||||
}
|
}
|
||||||
@ -144,7 +145,10 @@ export function renderRuntimeHints(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function renderGatewayServiceStartHints(env: NodeJS.ProcessEnv = process.env): string[] {
|
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;
|
const profile = env.CLAWDBOT_PROFILE;
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "darwin": {
|
case "darwin": {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { isWSLEnv } from "../../infra/wsl.js";
|
|||||||
import { getResolvedLoggerSettings } from "../../logging.js";
|
import { getResolvedLoggerSettings } from "../../logging.js";
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "../command-format.js";
|
||||||
import { formatRuntimeStatus, renderRuntimeHints, safeDaemonEnv } from "./shared.js";
|
import { formatRuntimeStatus, renderRuntimeHints, safeDaemonEnv } from "./shared.js";
|
||||||
import {
|
import {
|
||||||
type DaemonStatus,
|
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("Service config issue:")} ${issue.message}${detail}`);
|
||||||
}
|
}
|
||||||
defaultRuntime.error(
|
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(
|
defaultRuntime.error(
|
||||||
errorText(
|
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}`,
|
`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();
|
spacer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +264,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
|||||||
for (const svc of legacyServices) {
|
for (const svc of legacyServices) {
|
||||||
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.detail})`);
|
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.detail})`);
|
||||||
}
|
}
|
||||||
defaultRuntime.error(errorText("Cleanup: clawdbot doctor"));
|
defaultRuntime.error(errorText(`Cleanup: ${formatCliCommand("clawdbot doctor")}`));
|
||||||
spacer();
|
spacer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,6 +293,6 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
|||||||
spacer();
|
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`);
|
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 { setConsoleSubsystemFilter, setConsoleTimestampPrefix } from "../../logging/console.js";
|
||||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
|
import { formatCliCommand } from "../command-format.js";
|
||||||
import { forceFreePortAndWait } from "../ports.js";
|
import { forceFreePortAndWait } from "../ports.js";
|
||||||
import { ensureDevGatewayConfig } from "./dev.js";
|
import { ensureDevGatewayConfig } from "./dev.js";
|
||||||
import { runGatewayLoop } from "./run-loop.js";
|
import { runGatewayLoop } from "./run-loop.js";
|
||||||
@ -161,7 +162,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
|||||||
if (!opts.allowUnconfigured && mode !== "local") {
|
if (!opts.allowUnconfigured && mode !== "local") {
|
||||||
if (!configExists) {
|
if (!configExists) {
|
||||||
defaultRuntime.error(
|
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 {
|
} else {
|
||||||
defaultRuntime.error(
|
defaultRuntime.error(
|
||||||
@ -277,7 +278,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
|||||||
) {
|
) {
|
||||||
const errMessage = describeUnknownError(err);
|
const errMessage = describeUnknownError(err);
|
||||||
defaultRuntime.error(
|
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 {
|
try {
|
||||||
const diagnostics = await inspectPortUsage(port);
|
const diagnostics = await inspectPortUsage(port);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
} from "../../daemon/constants.js";
|
} from "../../daemon/constants.js";
|
||||||
import { resolveGatewayService } from "../../daemon/service.js";
|
import { resolveGatewayService } from "../../daemon/service.js";
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
|
import { formatCliCommand } from "../command-format.js";
|
||||||
|
|
||||||
export function parsePort(raw: unknown): number | null {
|
export function parsePort(raw: unknown): number | null {
|
||||||
if (raw === undefined || raw === null) return null;
|
if (raw === undefined || raw === null) return null;
|
||||||
@ -67,21 +68,21 @@ export function renderGatewayServiceStopHints(env: NodeJS.ProcessEnv = process.e
|
|||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return [
|
return [
|
||||||
"Tip: clawdbot daemon stop",
|
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||||
`Or: launchctl bootout gui/$UID/${resolveGatewayLaunchAgentLabel(profile)}`,
|
`Or: launchctl bootout gui/$UID/${resolveGatewayLaunchAgentLabel(profile)}`,
|
||||||
];
|
];
|
||||||
case "linux":
|
case "linux":
|
||||||
return [
|
return [
|
||||||
"Tip: clawdbot daemon stop",
|
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||||
`Or: systemctl --user stop ${resolveGatewaySystemdServiceName(profile)}.service`,
|
`Or: systemctl --user stop ${resolveGatewaySystemdServiceName(profile)}.service`,
|
||||||
];
|
];
|
||||||
case "win32":
|
case "win32":
|
||||||
return [
|
return [
|
||||||
"Tip: clawdbot daemon stop",
|
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||||
`Or: schtasks /End /TN "${resolveGatewayWindowsTaskName(profile)}"`,
|
`Or: schtasks /End /TN "${resolveGatewayWindowsTaskName(profile)}"`,
|
||||||
];
|
];
|
||||||
default:
|
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 { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
import { resolveUserPath } from "../utils.js";
|
import { resolveUserPath } from "../utils.js";
|
||||||
|
|
||||||
export type HooksListOptions = {
|
export type HooksListOptions = {
|
||||||
@ -150,7 +151,7 @@ export function formatHooksList(report: HookStatusReport, opts: HooksListOptions
|
|||||||
|
|
||||||
if (hooks.length === 0) {
|
if (hooks.length === 0) {
|
||||||
const message = opts.eligible
|
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.";
|
: "No hooks found.";
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -194,7 +195,7 @@ export function formatHookInfo(
|
|||||||
if (opts.json) {
|
if (opts.json) {
|
||||||
return JSON.stringify({ error: "not found", hook: hookName }, null, 2);
|
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) {
|
if (opts.json) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { parseLogLine } from "../logging/parse-log-line.js";
|
|||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js";
|
import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js";
|
||||||
|
|
||||||
type LogsTailPayload = {
|
type LogsTailPayload = {
|
||||||
@ -117,7 +118,7 @@ function emitGatewayError(
|
|||||||
) {
|
) {
|
||||||
const details = buildGatewayConnectionDetails({ url: opts.url });
|
const details = buildGatewayConnectionDetails({ url: opts.url });
|
||||||
const message = "Gateway not reachable. Is it running and accessible?";
|
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);
|
const errorText = err instanceof Error ? err.message : String(err);
|
||||||
|
|
||||||
if (mode === "json") {
|
if (mode === "json") {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { isWSL } from "../../infra/wsl.js";
|
|||||||
import { loadNodeHostConfig } from "../../node-host/config.js";
|
import { loadNodeHostConfig } from "../../node-host/config.js";
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "../command-format.js";
|
||||||
import {
|
import {
|
||||||
buildDaemonServiceSnapshot,
|
buildDaemonServiceSnapshot,
|
||||||
createNullWriter,
|
createNullWriter,
|
||||||
@ -46,7 +47,10 @@ type NodeDaemonStatusOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function renderNodeServiceStartHints(): string[] {
|
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) {
|
switch (process.platform) {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return [
|
return [
|
||||||
@ -168,7 +172,9 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) {
|
|||||||
});
|
});
|
||||||
if (!json) {
|
if (!json) {
|
||||||
defaultRuntime.log(`Node service already ${service.loadedText}.`);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
} from "../pairing/pairing-store.js";
|
} from "../pairing/pairing-store.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
|
|
||||||
/** Parse channel, allowing extension channels not in core registry. */
|
/** Parse channel, allowing extension channels not in core registry. */
|
||||||
function parseChannel(raw: unknown, channels: PairingChannel[]): PairingChannel {
|
function parseChannel(raw: unknown, channels: PairingChannel[]): PairingChannel {
|
||||||
@ -95,12 +96,12 @@ export function registerPairingCli(program: Command) {
|
|||||||
const resolvedCode = opts.channel ? codeOrChannel : code;
|
const resolvedCode = opts.channel ? codeOrChannel : code;
|
||||||
if (!opts.channel && !code) {
|
if (!opts.channel && !code) {
|
||||||
throw new Error(
|
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) {
|
if (opts.channel && code != null) {
|
||||||
throw new Error(
|
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);
|
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 path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
|
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
|
||||||
|
|
||||||
describe("parseCliProfileArgs", () => {
|
describe("parseCliProfileArgs", () => {
|
||||||
@ -76,3 +77,63 @@ describe("applyCliProfileEnv", () => {
|
|||||||
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join("/custom", "clawdbot.json"));
|
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 os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
|
import { isValidProfileName } from "./profile-utils.js";
|
||||||
|
|
||||||
export type CliProfileParseResult =
|
export type CliProfileParseResult =
|
||||||
| { ok: true; profile: string | null; argv: string[] }
|
| { ok: true; profile: string | null; argv: string[] }
|
||||||
| { ok: false; error: string };
|
| { ok: false; error: string };
|
||||||
@ -21,12 +23,6 @@ function takeValue(
|
|||||||
return { value: trimmed || null, consumedNext: Boolean(next) };
|
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 {
|
export function parseCliProfileArgs(argv: string[]): CliProfileParseResult {
|
||||||
if (argv.length < 2) return { ok: true, profile: null, argv };
|
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 { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||||
import { loadClawdbotPlugins } from "../../plugins/loader.js";
|
import { loadClawdbotPlugins } from "../../plugins/loader.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.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"]);
|
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(pluginIssues.map((issue) => ` ${error(issue)}`).join("\n"));
|
||||||
}
|
}
|
||||||
params.runtime.error("");
|
params.runtime.error("");
|
||||||
params.runtime.error(`${muted("Run:")} ${commandText("clawdbot doctor --fix")}`);
|
params.runtime.error(
|
||||||
|
`${muted("Run:")} ${commandText(formatCliCommand("clawdbot doctor --fix"))}`,
|
||||||
|
);
|
||||||
if (!allowInvalid) {
|
if (!allowInvalid) {
|
||||||
params.runtime.exit(1);
|
params.runtime.exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { runSecurityAudit } from "../security/audit.js";
|
|||||||
import { fixSecurityFootguns } from "../security/fix.js";
|
import { fixSecurityFootguns } from "../security/fix.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { isRich, theme } from "../terminal/theme.js";
|
import { isRich, theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
|
|
||||||
type SecurityAuditOptions = {
|
type SecurityAuditOptions = {
|
||||||
json?: boolean;
|
json?: boolean;
|
||||||
@ -67,10 +68,10 @@ export function registerSecurityCli(program: Command) {
|
|||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
lines.push(heading("Clawdbot security audit"));
|
lines.push(heading("Clawdbot security audit"));
|
||||||
lines.push(muted(`Summary: ${formatSummary(report.summary)}`));
|
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) {
|
if (opts.fix) {
|
||||||
lines.push(muted(`Fix: clawdbot security audit --fix`));
|
lines.push(muted(`Fix: ${formatCliCommand("clawdbot security audit --fix")}`));
|
||||||
if (!fixResult) {
|
if (!fixResult) {
|
||||||
lines.push(muted("Fixes: failed to apply (unexpected error)"));
|
lines.push(muted("Fixes: failed to apply (unexpected error)"));
|
||||||
} else if (
|
} else if (
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { loadConfig } from "../config/config.js";
|
|||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
|
|
||||||
export type SkillsListOptions = {
|
export type SkillsListOptions = {
|
||||||
json?: boolean;
|
json?: boolean;
|
||||||
@ -101,7 +102,7 @@ export function formatSkillsList(report: SkillStatusReport, opts: SkillsListOpti
|
|||||||
|
|
||||||
if (skills.length === 0) {
|
if (skills.length === 0) {
|
||||||
const message = opts.eligible
|
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.";
|
: "No skills found.";
|
||||||
return appendClawdHubHint(message, opts.json);
|
return appendClawdHubHint(message, opts.json);
|
||||||
}
|
}
|
||||||
@ -148,7 +149,7 @@ export function formatSkillInfo(
|
|||||||
return JSON.stringify({ error: "not found", skill: skillName }, null, 2);
|
return JSON.stringify({ error: "not found", skill: skillName }, null, 2);
|
||||||
}
|
}
|
||||||
return appendClawdHubHint(
|
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,
|
opts.json,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
} from "../infra/update-runner.js";
|
} from "../infra/update-runner.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "./command-format.js";
|
||||||
import { stylePromptMessage } from "../terminal/prompt-style.js";
|
import { stylePromptMessage } from "../terminal/prompt-style.js";
|
||||||
import { theme } from "../terminal/theme.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") {
|
if (result.reason === "not-git-install") {
|
||||||
defaultRuntime.log(
|
defaultRuntime.log(
|
||||||
theme.warn(
|
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(
|
defaultRuntime.log(
|
||||||
@ -410,7 +411,9 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
|||||||
if (!opts.json) {
|
if (!opts.json) {
|
||||||
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
|
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
|
||||||
defaultRuntime.log(
|
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") {
|
if (result.mode === "npm" || result.mode === "pnpm") {
|
||||||
defaultRuntime.log(
|
defaultRuntime.log(
|
||||||
theme.muted(
|
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 {
|
} else {
|
||||||
defaultRuntime.log(
|
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 { listAgentIds } from "../agents/agent-scope.js";
|
||||||
import { normalizeAgentId } from "../routing/session-key.js";
|
import { normalizeAgentId } from "../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
GATEWAY_CLIENT_MODES,
|
GATEWAY_CLIENT_MODES,
|
||||||
GATEWAY_CLIENT_NAMES,
|
GATEWAY_CLIENT_NAMES,
|
||||||
@ -92,7 +93,7 @@ export async function agentViaGatewayCommand(opts: AgentCliOpts, runtime: Runtim
|
|||||||
const knownAgents = listAgentIds(cfg);
|
const knownAgents = listAgentIds(cfg);
|
||||||
if (!knownAgents.includes(agentId)) {
|
if (!knownAgents.includes(agentId)) {
|
||||||
throw new Error(
|
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";
|
} from "../infra/agent-events.js";
|
||||||
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
|
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { applyVerboseOverride } from "../sessions/level-overrides.js";
|
import { applyVerboseOverride } from "../sessions/level-overrides.js";
|
||||||
import { resolveSendPolicy } from "../sessions/send-policy.js";
|
import { resolveSendPolicy } from "../sessions/send-policy.js";
|
||||||
import { resolveMessageChannel } from "../utils/message-channel.js";
|
import { resolveMessageChannel } from "../utils/message-channel.js";
|
||||||
@ -75,7 +76,7 @@ export async function agentCommand(
|
|||||||
const knownAgents = listAgentIds(cfg);
|
const knownAgents = listAgentIds(cfg);
|
||||||
if (!knownAgents.includes(agentIdOverride)) {
|
if (!knownAgents.includes(agentIdOverride)) {
|
||||||
throw new Error(
|
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 type { ClawdbotConfig } from "../config/config.js";
|
||||||
import { readConfigFileSnapshot } from "../config/config.js";
|
import { readConfigFileSnapshot } from "../config/config.js";
|
||||||
import type { RuntimeEnv } from "../runtime.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")
|
? snapshot.issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n")
|
||||||
: "Unknown validation issue.";
|
: "Unknown validation issue.";
|
||||||
runtime.error(`Config invalid:\n${issues}`);
|
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);
|
runtime.exit(1);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { AgentBinding } from "../config/types.js";
|
|||||||
import { normalizeAgentId } from "../routing/session-key.js";
|
import { normalizeAgentId } from "../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { describeBinding } from "./agents.bindings.js";
|
import { describeBinding } from "./agents.bindings.js";
|
||||||
import { requireValidConfig } from "./agents.command-shared.js";
|
import { requireValidConfig } from "./agents.command-shared.js";
|
||||||
import type { AgentSummary } from "./agents.config.js";
|
import type { AgentSummary } from "./agents.config.js";
|
||||||
@ -116,7 +117,7 @@ export async function agentsListCommand(
|
|||||||
const lines = ["Agents:", ...summaries.map(formatSummary)];
|
const lines = ["Agents:", ...summaries.map(formatSummary)];
|
||||||
lines.push("Routing rules map channel/account/peer to an agent. Use --bindings for full rules.");
|
lines.push("Routing rules map channel/account/peer to an agent. Use --bindings for full rules.");
|
||||||
lines.push(
|
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"));
|
runtime.log(lines.join("\n"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { type ChannelId, getChannelPlugin } from "../../channels/plugins/index.js";
|
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 { type ClawdbotConfig, readConfigFileSnapshot } from "../../config/config.js";
|
||||||
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../../runtime.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")
|
? snapshot.issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n")
|
||||||
: "Unknown validation issue.";
|
: "Unknown validation issue.";
|
||||||
runtime.error(`Config invalid:\n${issues}`);
|
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);
|
runtime.exit(1);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { formatAge } from "../../infra/channel-summary.js";
|
|||||||
import { collectChannelStatusIssues } from "../../infra/channels-status-issues.js";
|
import { collectChannelStatusIssues } from "../../infra/channels-status-issues.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||||
import { formatDocsLink } from "../../terminal/links.js";
|
import { formatDocsLink } from "../../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { theme } from "../../terminal/theme.js";
|
import { theme } from "../../terminal/theme.js";
|
||||||
import { type ChatChannel, formatChannelAccountLabel, requireValidConfig } from "./shared.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})` : ""}`,
|
`- ${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("");
|
||||||
}
|
}
|
||||||
lines.push(
|
lines.push(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.js";
|
import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT } from "../config/config.js";
|
import { CONFIG_PATH_CLAWDBOT } from "../config/config.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
@ -23,7 +24,7 @@ export async function removeChannelConfigWizard(
|
|||||||
note(
|
note(
|
||||||
[
|
[
|
||||||
"No channel config found in clawdbot.json.",
|
"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"),
|
].join("\n"),
|
||||||
"Remove channel",
|
"Remove channel",
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
CONFIG_PATH_CLAWDBOT,
|
CONFIG_PATH_CLAWDBOT,
|
||||||
@ -198,7 +199,9 @@ export async function runConfigureWizard(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!snapshot.valid) {
|
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);
|
runtime.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
resolveSystemNodeInfo,
|
resolveSystemNodeInfo,
|
||||||
} from "../daemon/runtime-paths.js";
|
} from "../daemon/runtime-paths.js";
|
||||||
import { buildServiceEnvironment } from "../daemon/service-env.js";
|
import { buildServiceEnvironment } from "../daemon/service-env.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
|
import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
|
||||||
|
|
||||||
type WarnFn = (message: string, title?: string) => void;
|
type WarnFn = (message: string, title?: string) => void;
|
||||||
@ -65,5 +66,5 @@ export async function buildGatewayInstallPlan(params: {
|
|||||||
export function gatewayInstallErrorHint(platform = process.platform): string {
|
export function gatewayInstallErrorHint(platform = process.platform): string {
|
||||||
return platform === "win32"
|
return platform === "win32"
|
||||||
? "Tip: rerun from an elevated PowerShell (Start → type PowerShell → right-click → Run as administrator) or skip daemon install."
|
? "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 type { RuntimeEnv } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
import { isRich, theme } from "../terminal/theme.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_TOOL = "https://docs.clawd.bot/mcp.SearchClawdbot";
|
||||||
const SEARCH_TIMEOUT_MS = 30_000;
|
const SEARCH_TIMEOUT_MS = 30_000;
|
||||||
@ -150,10 +151,10 @@ export async function docsSearchCommand(queryParts: string[], runtime: RuntimeEn
|
|||||||
const docs = formatDocsLink("/", "docs.clawd.bot");
|
const docs = formatDocsLink("/", "docs.clawd.bot");
|
||||||
if (isRich()) {
|
if (isRich()) {
|
||||||
runtime.log(`${theme.muted("Docs:")} ${docs}`);
|
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 {
|
} else {
|
||||||
runtime.log("Docs: https://docs.clawd.bot/");
|
runtime.log("Docs: https://docs.clawd.bot/");
|
||||||
runtime.log('Search: clawdbot docs "your query"');
|
runtime.log(`Search: ${formatCliCommand('clawdbot docs "your query"')}`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
} from "../agents/auth-profiles.js";
|
} from "../agents/auth-profiles.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import { note } from "../terminal/note.js";
|
import { note } from "../terminal/note.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { DoctorPrompter } from "./doctor-prompter.js";
|
import type { DoctorPrompter } from "./doctor-prompter.js";
|
||||||
|
|
||||||
export async function maybeRepairAnthropicOAuthProfileId(
|
export async function maybeRepairAnthropicOAuthProfileId(
|
||||||
@ -49,9 +50,9 @@ function formatAuthIssueHint(issue: AuthIssue): string | null {
|
|||||||
return "Run `claude setup-token` on the gateway host.";
|
return "Run `claude setup-token` on the gateway host.";
|
||||||
}
|
}
|
||||||
if (issue.provider === "openai-codex" && issue.profileId === CODEX_CLI_PROFILE_ID) {
|
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 {
|
function formatAuthIssueLine(issue: AuthIssue): string {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
readConfigFileSnapshot,
|
readConfigFileSnapshot,
|
||||||
} from "../config/config.js";
|
} from "../config/config.js";
|
||||||
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { note } from "../terminal/note.js";
|
import { note } from "../terminal/note.js";
|
||||||
import { normalizeLegacyConfigValues } from "./doctor-legacy-config.js";
|
import { normalizeLegacyConfigValues } from "./doctor-legacy-config.js";
|
||||||
import type { DoctorOptions } from "./doctor-prompter.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 (changes.length > 0) note(changes.join("\n"), "Doctor changes");
|
||||||
if (migrated) cfg = migrated;
|
if (migrated) cfg = migrated;
|
||||||
} else {
|
} 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) {
|
if (shouldRepair) {
|
||||||
cfg = normalized.config;
|
cfg = normalized.config;
|
||||||
} else {
|
} 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) {
|
if (shouldRepair) {
|
||||||
cfg = autoEnable.config;
|
cfg = autoEnable.config;
|
||||||
} else {
|
} 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,
|
isSystemdUnavailableDetail,
|
||||||
renderSystemdUnavailableHints,
|
renderSystemdUnavailableHints,
|
||||||
} from "../daemon/systemd-hints.js";
|
} from "../daemon/systemd-hints.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { isWSLEnv } from "../infra/wsl.js";
|
import { isWSLEnv } from "../infra/wsl.js";
|
||||||
import type { GatewayServiceRuntime } from "../daemon/service-runtime.js";
|
import type { GatewayServiceRuntime } from "../daemon/service-runtime.js";
|
||||||
import { getResolvedLoggerSettings } from "../logging.js";
|
import { getResolvedLoggerSettings } from "../logging.js";
|
||||||
@ -69,10 +70,10 @@ export function buildGatewayRuntimeHints(
|
|||||||
hints.push(
|
hints.push(
|
||||||
`LaunchAgent label cached but plist missing. Clear with: launchctl bootout gui/$UID/${label}`,
|
`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) {
|
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}`);
|
if (fileLog) hints.push(`File logs: ${fileLog}`);
|
||||||
return hints;
|
return hints;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { renderSystemdUnavailableHints } from "../daemon/systemd-hints.js";
|
|||||||
import { formatPortDiagnostics, inspectPortUsage } from "../infra/ports.js";
|
import { formatPortDiagnostics, inspectPortUsage } from "../infra/ports.js";
|
||||||
import { isWSL } from "../infra/wsl.js";
|
import { isWSL } from "../infra/wsl.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { note } from "../terminal/note.js";
|
import { note } from "../terminal/note.js";
|
||||||
import { sleep } from "../utils.js";
|
import { sleep } from "../utils.js";
|
||||||
import {
|
import {
|
||||||
@ -201,7 +202,7 @@ export async function maybeRepairGatewayDaemon(params: {
|
|||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
const label = resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE);
|
const label = resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE);
|
||||||
note(
|
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",
|
"Gateway",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,11 @@ import type { ChannelId } from "../channels/plugins/types.js";
|
|||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
|
import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
|
||||||
import { note } from "../terminal/note.js";
|
import { note } from "../terminal/note.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
|
|
||||||
export async function noteSecurityWarnings(cfg: ClawdbotConfig) {
|
export async function noteSecurityWarnings(cfg: ClawdbotConfig) {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
const auditHint = `- Run: clawdbot security audit --deep`;
|
const auditHint = `- Run: ${formatCliCommand("clawdbot security audit --deep")}`;
|
||||||
|
|
||||||
const warnDmPolicy = async (params: {
|
const warnDmPolicy = async (params: {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { isTruthyEnvValue } from "../infra/env.js";
|
|||||||
import { runCommandWithTimeout } from "../process/exec.js";
|
import { runCommandWithTimeout } from "../process/exec.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { note } from "../terminal/note.js";
|
import { note } from "../terminal/note.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { DoctorOptions } from "./doctor-prompter.js";
|
import type { DoctorOptions } from "./doctor-prompter.js";
|
||||||
|
|
||||||
async function detectClawdbotGitCheckout(root: string): Promise<"git" | "not-git" | "unknown"> {
|
async function detectClawdbotGitCheckout(root: string): Promise<"git" | "not-git" | "unknown"> {
|
||||||
@ -71,7 +72,7 @@ export async function maybeOfferUpdateBeforeDoctor(params: {
|
|||||||
note(
|
note(
|
||||||
[
|
[
|
||||||
"This install is not a git checkout.",
|
"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"),
|
].join("\n"),
|
||||||
"Update",
|
"Update",
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
resolveConfiguredModelRef,
|
resolveConfiguredModelRef,
|
||||||
resolveHooksGmailModel,
|
resolveHooksGmailModel,
|
||||||
} from "../agents/model-selection.js";
|
} from "../agents/model-selection.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT, readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
import { CONFIG_PATH_CLAWDBOT, readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
||||||
import { resolveGatewayService } from "../daemon/service.js";
|
import { resolveGatewayService } from "../daemon/service.js";
|
||||||
@ -258,7 +259,7 @@ export async function doctorCommand(
|
|||||||
runtime.log(`Backup: ${backupPath}`);
|
runtime.log(`Backup: ${backupPath}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runtime.log('Run "clawdbot doctor --fix" to apply changes.');
|
runtime.log(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply changes.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.workspaceSuggestions !== false) {
|
if (options.workspaceSuggestions !== false) {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
} from "../../agents/agent-scope.js";
|
} from "../../agents/agent-scope.js";
|
||||||
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
|
||||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
CONFIG_PATH_CLAWDBOT,
|
CONFIG_PATH_CLAWDBOT,
|
||||||
readConfigFileSnapshot,
|
readConfigFileSnapshot,
|
||||||
@ -340,7 +341,9 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim
|
|||||||
|
|
||||||
const providers = resolvePluginProviders({ config, workspaceDir });
|
const providers = resolvePluginProviders({ config, workspaceDir });
|
||||||
if (providers.length === 0) {
|
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();
|
const prompter = createClackPrompter();
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
} from "../../infra/provider-usage.js";
|
} from "../../infra/provider-usage.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.js";
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
import { colorize, theme } from "../../terminal/theme.js";
|
import { colorize, theme } from "../../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { shortenHomePath } from "../../utils.js";
|
import { shortenHomePath } from "../../utils.js";
|
||||||
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
||||||
import { isRich } from "./list.format.js";
|
import { isRich } from "./list.format.js";
|
||||||
@ -395,8 +396,8 @@ export async function modelsStatusCommand(
|
|||||||
for (const provider of missingProvidersInUse) {
|
for (const provider of missingProvidersInUse) {
|
||||||
const hint =
|
const hint =
|
||||||
provider === "anthropic"
|
provider === "anthropic"
|
||||||
? "Run `claude setup-token` or `clawdbot configure`."
|
? `Run \`claude setup-token\` or \`${formatCliCommand("clawdbot configure")}\`.`
|
||||||
: "Run `clawdbot configure` or set an API key env var.";
|
: `Run \`${formatCliCommand("clawdbot configure")}\` or set an API key env var.`;
|
||||||
runtime.log(`- ${theme.heading(provider)} ${hint}`);
|
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 { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { formatDocsLink } from "../terminal/links.js";
|
import { formatDocsLink } from "../terminal/links.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { enablePluginInConfig } from "../plugins/enable.js";
|
import { enablePluginInConfig } from "../plugins/enable.js";
|
||||||
import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js";
|
import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js";
|
||||||
import type { ChannelChoice } from "./onboard-types.js";
|
import type { ChannelChoice } from "./onboard-types.js";
|
||||||
@ -186,7 +187,7 @@ async function noteChannelPrimer(
|
|||||||
await prompter.note(
|
await prompter.note(
|
||||||
[
|
[
|
||||||
"DM security: default is pairing; unknown DMs get a pairing code.",
|
"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=["*"].',
|
'Public DMs require dmPolicy="open" + allowFrom=["*"].',
|
||||||
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
||||||
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
`Docs: ${formatDocsLink("/start/pairing", "start/pairing")}`,
|
||||||
@ -233,7 +234,7 @@ async function maybeConfigureDmPolicies(params: {
|
|||||||
await prompter.note(
|
await prompter.note(
|
||||||
[
|
[
|
||||||
"Default: pairing (unknown DMs get a pairing code).",
|
"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.`,
|
`Allowlist DMs: ${policy.policyKey}="allowlist" + ${policy.allowFromKey} entries.`,
|
||||||
`Public DMs: ${policy.policyKey}="open" + ${policy.allowFromKey} includes "*".`,
|
`Public DMs: ${policy.policyKey}="open" + ${policy.allowFromKey} includes "*".`,
|
||||||
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
'Multi-user DMs: set session.dmScope="per-channel-peer" to isolate sessions.',
|
||||||
@ -581,7 +582,7 @@ export async function setupChannels(
|
|||||||
{
|
{
|
||||||
value: "__skip__",
|
value: "__skip__",
|
||||||
label: "Skip for now",
|
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,
|
initialValue: quickstartDefault,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { RuntimeEnv } from "../runtime.js";
|
|||||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||||
import { buildWorkspaceHookStatus } from "../hooks/hooks-status.js";
|
import { buildWorkspaceHookStatus } from "../hooks/hooks-status.js";
|
||||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
|
|
||||||
export async function setupInternalHooks(
|
export async function setupInternalHooks(
|
||||||
cfg: ClawdbotConfig,
|
cfg: ClawdbotConfig,
|
||||||
@ -73,9 +74,9 @@ export async function setupInternalHooks(
|
|||||||
`Enabled ${selected.length} hook${selected.length > 1 ? "s" : ""}: ${selected.join(", ")}`,
|
`Enabled ${selected.length} hook${selected.length > 1 ? "s" : ""}: ${selected.join(", ")}`,
|
||||||
"",
|
"",
|
||||||
"You can manage hooks later with:",
|
"You can manage hooks later with:",
|
||||||
" clawdbot hooks list",
|
` ${formatCliCommand("clawdbot hooks list")}`,
|
||||||
" clawdbot hooks enable <name>",
|
` ${formatCliCommand("clawdbot hooks enable <name>")}`,
|
||||||
" clawdbot hooks disable <name>",
|
` ${formatCliCommand("clawdbot hooks disable <name>")}`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
"Hooks Configured",
|
"Hooks Configured",
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import { readConfigFileSnapshot } from "../config/config.js";
|
import { readConfigFileSnapshot } from "../config/config.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
@ -12,7 +13,9 @@ export async function runNonInteractiveOnboarding(
|
|||||||
) {
|
) {
|
||||||
const snapshot = await readConfigFileSnapshot();
|
const snapshot = await readConfigFileSnapshot();
|
||||||
if (snapshot.exists && !snapshot.valid) {
|
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);
|
runtime.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT, resolveGatewayPort, writeConfigFile } from "../../config/config.js";
|
import { CONFIG_PATH_CLAWDBOT, resolveGatewayPort, writeConfigFile } from "../../config/config.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.js";
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { DEFAULT_GATEWAY_DAEMON_RUNTIME } from "../daemon-runtime.js";
|
import { DEFAULT_GATEWAY_DAEMON_RUNTIME } from "../daemon-runtime.js";
|
||||||
import { healthCommand } from "../health.js";
|
import { healthCommand } from "../health.js";
|
||||||
import {
|
import {
|
||||||
@ -123,7 +124,7 @@ export async function runNonInteractiveOnboardingLocal(params: {
|
|||||||
|
|
||||||
if (!opts.json) {
|
if (!opts.json) {
|
||||||
runtime.log(
|
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 type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../../config/config.js";
|
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../../config/config.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.js";
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
|
import { formatCliCommand } from "../../cli/command-format.js";
|
||||||
import { applyWizardMetadata } from "../onboard-helpers.js";
|
import { applyWizardMetadata } from "../onboard-helpers.js";
|
||||||
import type { OnboardOptions } from "../onboard-types.js";
|
import type { OnboardOptions } from "../onboard-types.js";
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ export async function runNonInteractiveOnboardingRemote(params: {
|
|||||||
runtime.log(`Remote gateway: ${remoteUrl}`);
|
runtime.log(`Remote gateway: ${remoteUrl}`);
|
||||||
runtime.log(`Auth: ${payload.auth}`);
|
runtime.log(`Auth: ${payload.auth}`);
|
||||||
runtime.log(
|
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 { installSkill } from "../agents/skills-install.js";
|
||||||
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||||
@ -152,7 +153,9 @@ export async function setupSkills(
|
|||||||
spin.stop(`Install failed: ${name}${code}${detail ? ` — ${detail}` : ""}`);
|
spin.stop(`Install failed: ${name}${code}${detail ? ` — ${detail}` : ""}`);
|
||||||
if (result.stderr) runtime.log(result.stderr.trim());
|
if (result.stderr) runtime.log(result.stderr.trim());
|
||||||
else if (result.stdout) runtime.log(result.stdout.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");
|
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 { DEFAULT_WORKSPACE, handleReset } from "./onboard-helpers.js";
|
||||||
import { runInteractiveOnboarding } from "./onboard-interactive.js";
|
import { runInteractiveOnboarding } from "./onboard-interactive.js";
|
||||||
import { runNonInteractiveOnboarding } from "./onboard-non-interactive.js";
|
import { runNonInteractiveOnboarding } from "./onboard-non-interactive.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { OnboardOptions } from "./onboard-types.js";
|
import type { OnboardOptions } from "./onboard-types.js";
|
||||||
|
|
||||||
export async function onboardCommand(opts: OnboardOptions, runtime: RuntimeEnv = defaultRuntime) {
|
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.",
|
"Non-interactive onboarding requires explicit risk acknowledgement.",
|
||||||
"Read: https://docs.clawd.bot/security",
|
"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"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
runtime.exit(1);
|
runtime.exit(1);
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
import { resolveGatewayService } from "../daemon/service.js";
|
import { resolveGatewayService } from "../daemon/service.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js";
|
import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
collectWorkspaceDirs,
|
collectWorkspaceDirs,
|
||||||
isPathWithin,
|
isPathWithin,
|
||||||
@ -143,7 +144,7 @@ export async function resetCommand(runtime: RuntimeEnv, opts: ResetOptions) {
|
|||||||
for (const dir of sessionDirs) {
|
for (const dir of sessionDirs) {
|
||||||
await removePath(dir, runtime, { dryRun, label: dir });
|
await removePath(dir, runtime, { dryRun, label: dir });
|
||||||
}
|
}
|
||||||
runtime.log("Next: clawdbot onboard --install-daemon");
|
runtime.log(`Next: ${formatCliCommand("clawdbot onboard --install-daemon")}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ export async function resetCommand(runtime: RuntimeEnv, opts: ResetOptions) {
|
|||||||
for (const workspace of workspaceDirs) {
|
for (const workspace of workspaceDirs) {
|
||||||
await removePath(workspace, runtime, { dryRun, label: workspace });
|
await removePath(workspace, runtime, { dryRun, label: workspace });
|
||||||
}
|
}
|
||||||
runtime.log("Next: clawdbot onboard --install-daemon");
|
runtime.log(`Next: ${formatCliCommand("clawdbot onboard --install-daemon")}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SandboxBrowserInfo, SandboxContainerInfo } from "../agents/sandbox.js";
|
import type { SandboxBrowserInfo, SandboxContainerInfo } from "../agents/sandbox.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import {
|
import {
|
||||||
formatAge,
|
formatAge,
|
||||||
@ -88,7 +89,9 @@ export function displaySummary(
|
|||||||
|
|
||||||
if (mismatchCount > 0) {
|
if (mismatchCount > 0) {
|
||||||
runtime.log(`\n⚠️ ${mismatchCount} container(s) with image mismatch detected.`);
|
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 { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||||
import { withProgress } from "../cli/progress.js";
|
import { withProgress } from "../cli/progress.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { loadConfig, readConfigFileSnapshot, resolveGatewayPort } from "../config/config.js";
|
import { loadConfig, readConfigFileSnapshot, resolveGatewayPort } from "../config/config.js";
|
||||||
import { readLastGatewayErrorLine } from "../daemon/diagnostics.js";
|
import { readLastGatewayErrorLine } from "../daemon/diagnostics.js";
|
||||||
import type { GatewayService } from "../daemon/service.js";
|
import type { GatewayService } from "../daemon/service.js";
|
||||||
@ -337,7 +338,7 @@ export async function statusAllCommand(
|
|||||||
Item: "Gateway",
|
Item: "Gateway",
|
||||||
Value: `${gatewayMode}${remoteUrlMissing ? " (remote.url missing)" : ""} · ${gatewayTarget} (${connection.urlSource}) · ${gatewayStatus}${gatewayAuth}`,
|
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
|
gatewaySelfLine
|
||||||
? { Item: "Gateway self", Value: gatewaySelfLine }
|
? { Item: "Gateway self", Value: gatewaySelfLine }
|
||||||
: { Item: "Gateway self", Value: "unknown" },
|
: { Item: "Gateway self", Value: "unknown" },
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type { RuntimeEnv } from "../runtime.js";
|
|||||||
import { runSecurityAudit } from "../security/audit.js";
|
import { runSecurityAudit } from "../security/audit.js";
|
||||||
import { renderTable } from "../terminal/table.js";
|
import { renderTable } from "../terminal/table.js";
|
||||||
import { theme } from "../terminal/theme.js";
|
import { theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
resolveMemoryCacheSummary,
|
resolveMemoryCacheSummary,
|
||||||
resolveMemoryFtsState,
|
resolveMemoryFtsState,
|
||||||
@ -374,8 +375,8 @@ export async function statusCommand(
|
|||||||
runtime.log(theme.muted(`… +${sorted.length - shown.length} more`));
|
runtime.log(theme.muted(`… +${sorted.length - shown.length} more`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runtime.log(theme.muted("Full report: clawdbot security audit"));
|
runtime.log(theme.muted(`Full report: ${formatCliCommand("clawdbot security audit")}`));
|
||||||
runtime.log(theme.muted("Deep probe: clawdbot security audit --deep"));
|
runtime.log(theme.muted(`Deep probe: ${formatCliCommand("clawdbot security audit --deep")}`));
|
||||||
|
|
||||||
runtime.log("");
|
runtime.log("");
|
||||||
runtime.log(theme.heading("Channels"));
|
runtime.log(theme.heading("Channels"));
|
||||||
@ -531,11 +532,11 @@ export async function statusCommand(
|
|||||||
runtime.log("");
|
runtime.log("");
|
||||||
}
|
}
|
||||||
runtime.log("Next steps:");
|
runtime.log("Next steps:");
|
||||||
runtime.log(" Need to share? clawdbot status --all");
|
runtime.log(` Need to share? ${formatCliCommand("clawdbot status --all")}`);
|
||||||
runtime.log(" Need to debug live? clawdbot logs --follow");
|
runtime.log(` Need to debug live? ${formatCliCommand("clawdbot logs --follow")}`);
|
||||||
if (gatewayReachable) {
|
if (gatewayReachable) {
|
||||||
runtime.log(" Need to test channels? clawdbot status --deep");
|
runtime.log(` Need to test channels? ${formatCliCommand("clawdbot status --deep")}`);
|
||||||
} else {
|
} else {
|
||||||
runtime.log(" Fix reachability first: clawdbot gateway status");
|
runtime.log(` Fix reachability first: ${formatCliCommand("clawdbot gateway status")}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
compareSemverStrings,
|
compareSemverStrings,
|
||||||
type UpdateCheckResult,
|
type UpdateCheckResult,
|
||||||
} from "../infra/update-check.js";
|
} from "../infra/update-check.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { VERSION } from "../version.js";
|
import { VERSION } from "../version.js";
|
||||||
|
|
||||||
export async function getUpdateCheckResult(params: {
|
export async function getUpdateCheckResult(params: {
|
||||||
@ -63,7 +64,7 @@ export function formatUpdateAvailableHint(update: UpdateCheckResult): string | n
|
|||||||
details.push(`npm ${availability.latestVersion}`);
|
details.push(`npm ${availability.latestVersion}`);
|
||||||
}
|
}
|
||||||
const suffix = details.length > 0 ? ` (${details.join(" · ")})` : "";
|
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 {
|
export function formatUpdateOneLiner(update: UpdateCheckResult): string {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
|
|
||||||
export function isSystemdUnavailableDetail(detail?: string): boolean {
|
export function isSystemdUnavailableDetail(detail?: string): boolean {
|
||||||
if (!detail) return false;
|
if (!detail) return false;
|
||||||
const normalized = detail.toLowerCase();
|
const normalized = detail.toLowerCase();
|
||||||
@ -20,6 +22,6 @@ export function renderSystemdUnavailableHints(options: { wsl?: boolean } = {}):
|
|||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
"systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.",
|
"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 { buildConfigSchema } from "../../config/schema.js";
|
||||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||||
import {
|
import {
|
||||||
DOCTOR_NONINTERACTIVE_HINT,
|
formatDoctorNonInteractiveHint,
|
||||||
type RestartSentinelPayload,
|
type RestartSentinelPayload,
|
||||||
writeRestartSentinel,
|
writeRestartSentinel,
|
||||||
} from "../../infra/restart-sentinel.js";
|
} from "../../infra/restart-sentinel.js";
|
||||||
@ -336,7 +336,7 @@ export const configHandlers: GatewayRequestHandlers = {
|
|||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
sessionKey,
|
sessionKey,
|
||||||
message: note ?? null,
|
message: note ?? null,
|
||||||
doctorHint: DOCTOR_NONINTERACTIVE_HINT,
|
doctorHint: formatDoctorNonInteractiveHint(),
|
||||||
stats: {
|
stats: {
|
||||||
mode: "config.apply",
|
mode: "config.apply",
|
||||||
root: CONFIG_PATH_CLAWDBOT,
|
root: CONFIG_PATH_CLAWDBOT,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { resolveClawdbotPackageRoot } from "../../infra/clawdbot-root.js";
|
import { resolveClawdbotPackageRoot } from "../../infra/clawdbot-root.js";
|
||||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||||
import {
|
import {
|
||||||
DOCTOR_NONINTERACTIVE_HINT,
|
formatDoctorNonInteractiveHint,
|
||||||
type RestartSentinelPayload,
|
type RestartSentinelPayload,
|
||||||
writeRestartSentinel,
|
writeRestartSentinel,
|
||||||
} from "../../infra/restart-sentinel.js";
|
} from "../../infra/restart-sentinel.js";
|
||||||
@ -75,7 +75,7 @@ export const updateHandlers: GatewayRequestHandlers = {
|
|||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
sessionKey,
|
sessionKey,
|
||||||
message: note ?? null,
|
message: note ?? null,
|
||||||
doctorHint: DOCTOR_NONINTERACTIVE_HINT,
|
doctorHint: formatDoctorNonInteractiveHint(),
|
||||||
stats: {
|
stats: {
|
||||||
mode: result.mode,
|
mode: result.mode,
|
||||||
root: result.root ?? undefined,
|
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 { CanvasHostServer } from "../canvas-host/server.js";
|
||||||
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
|
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
|
||||||
import { createDefaultDeps } from "../cli/deps.js";
|
import { createDefaultDeps } from "../cli/deps.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
CONFIG_PATH_CLAWDBOT,
|
CONFIG_PATH_CLAWDBOT,
|
||||||
isNixMode,
|
isNixMode,
|
||||||
@ -155,7 +156,7 @@ export async function startGatewayServer(
|
|||||||
const { config: migrated, changes } = migrateLegacyConfig(configSnapshot.parsed);
|
const { config: migrated, changes } = migrateLegacyConfig(configSnapshot.parsed);
|
||||||
if (!migrated) {
|
if (!migrated) {
|
||||||
throw new Error(
|
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);
|
await writeConfigFile(migrated);
|
||||||
@ -177,7 +178,7 @@ export async function startGatewayServer(
|
|||||||
.join("\n")
|
.join("\n")
|
||||||
: "Unknown validation issue.";
|
: "Unknown validation issue.";
|
||||||
throw new Error(
|
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";
|
} from "../config/config.js";
|
||||||
import { runCommandWithTimeout } from "../process/exec.js";
|
import { runCommandWithTimeout } from "../process/exec.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
buildDefaultHookUrl,
|
buildDefaultHookUrl,
|
||||||
buildGogWatchServeArgs,
|
buildGogWatchServeArgs,
|
||||||
@ -276,7 +277,7 @@ export async function runGmailSetup(opts: GmailSetupOptions) {
|
|||||||
defaultRuntime.log(`- push endpoint: ${pushEndpoint}`);
|
defaultRuntime.log(`- push endpoint: ${pushEndpoint}`);
|
||||||
defaultRuntime.log(`- hook url: ${hookUrl}`);
|
defaultRuntime.log(`- hook url: ${hookUrl}`);
|
||||||
defaultRuntime.log(`- config: ${CONFIG_PATH_CLAWDBOT}`);
|
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) {
|
export async function runGmailService(opts: GmailRunOptions) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
|
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 { ChannelId, ChannelOutboundTargetMode } from "../../channels/plugins/types.js";
|
||||||
import type { ClawdbotConfig } from "../../config/config.js";
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
import type { SessionEntry } from "../../config/sessions.js";
|
import type { SessionEntry } from "../../config/sessions.js";
|
||||||
@ -109,7 +110,7 @@ export function resolveOutboundTarget(params: {
|
|||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
error: new Error(
|
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";
|
import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js";
|
||||||
|
|
||||||
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
|
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
|
||||||
@ -20,7 +21,7 @@ export function buildPortHints(listeners: PortListener[], port: number): string[
|
|||||||
const hints: string[] = [];
|
const hints: string[] = [];
|
||||||
if (kinds.has("gateway")) {
|
if (kinds.has("gateway")) {
|
||||||
hints.push(
|
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")) {
|
if (kinds.has("ssh")) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { resolveStateDir } from "../config/paths.js";
|
import { resolveStateDir } from "../config/paths.js";
|
||||||
|
|
||||||
export type RestartSentinelLog = {
|
export type RestartSentinelLog = {
|
||||||
@ -44,7 +45,11 @@ export type RestartSentinel = {
|
|||||||
|
|
||||||
const SENTINEL_FILENAME = "restart-sentinel.json";
|
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 {
|
export function resolveRestartSentinelPath(env: NodeJS.ProcessEnv = process.env): string {
|
||||||
return path.join(resolveStateDir(env), SENTINEL_FILENAME);
|
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 { runExec } from "../process/exec.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { ensureBinary } from "./binaries.js";
|
import { ensureBinary } from "./binaries.js";
|
||||||
|
|
||||||
function parsePossiblyNoisyJsonObject(stdout: string): Record<string, unknown> {
|
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("Failed to enable Tailscale Funnel. Is it allowed on your tailnet?");
|
||||||
runtime.error(
|
runtime.error(
|
||||||
info(
|
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()) {
|
if (shouldLogVerbose()) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { resolveStateDir } from "../config/paths.js";
|
|||||||
import { resolveClawdbotPackageRoot } from "./clawdbot-root.js";
|
import { resolveClawdbotPackageRoot } from "./clawdbot-root.js";
|
||||||
import { compareSemverStrings, fetchNpmTagVersion, checkUpdateStatus } from "./update-check.js";
|
import { compareSemverStrings, fetchNpmTagVersion, checkUpdateStatus } from "./update-check.js";
|
||||||
import { VERSION } from "../version.js";
|
import { VERSION } from "../version.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
|
|
||||||
type UpdateCheckState = {
|
type UpdateCheckState = {
|
||||||
lastCheckedAt?: string;
|
lastCheckedAt?: string;
|
||||||
@ -102,7 +103,7 @@ export async function runGatewayUpdateCheck(params: {
|
|||||||
state.lastNotifiedVersion !== tagStatus.version || state.lastNotifiedTag !== tag;
|
state.lastNotifiedVersion !== tagStatus.version || state.lastNotifiedTag !== tag;
|
||||||
if (shouldNotify) {
|
if (shouldNotify) {
|
||||||
params.log.info(
|
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.lastNotifiedVersion = tagStatus.version;
|
||||||
nextState.lastNotifiedTag = tag;
|
nextState.lastNotifiedTag = tag;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { ensurePortAvailable, PortInUseError } from "../infra/ports.js";
|
|||||||
import { getTailnetHostname } from "../infra/tailscale.js";
|
import { getTailnetHostname } from "../infra/tailscale.js";
|
||||||
import { logInfo } from "../logger.js";
|
import { logInfo } from "../logger.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { startMediaServer } from "./server.js";
|
import { startMediaServer } from "./server.js";
|
||||||
import { saveMediaSource } from "./store.js";
|
import { saveMediaSource } from "./store.js";
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ export async function ensureMediaHosted(
|
|||||||
if (needsServerStart && !opts.startServer) {
|
if (needsServerStart && !opts.startServer) {
|
||||||
await fs.rm(saved.path).catch(() => {});
|
await fs.rm(saved.path).catch(() => {});
|
||||||
throw new Error(
|
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) {
|
if (needsServerStart && opts.startServer) {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { PairingChannel } from "./pairing-store.js";
|
import type { PairingChannel } from "./pairing-store.js";
|
||||||
|
|
||||||
export function buildPairingReply(params: {
|
export function buildPairingReply(params: {
|
||||||
@ -14,6 +15,6 @@ export function buildPairingReply(params: {
|
|||||||
`Pairing code: ${code}`,
|
`Pairing code: ${code}`,
|
||||||
"",
|
"",
|
||||||
"Ask the bot owner to approve with:",
|
"Ask the bot owner to approve with:",
|
||||||
`clawdbot pairing approve ${channel} <code>`,
|
formatCliCommand(`clawdbot pairing approve ${channel} <code>`),
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
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_BASE_URL = "https://chat.qwen.ai";
|
||||||
const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`;
|
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();
|
const text = await response.text();
|
||||||
if (response.status === 400) {
|
if (response.status === 400) {
|
||||||
throw new Error(
|
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}`);
|
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 { createConfigIO } from "../config/config.js";
|
||||||
import { resolveNativeSkillsEnabled } from "../config/commands.js";
|
import { resolveNativeSkillsEnabled } from "../config/commands.js";
|
||||||
import { resolveOAuthDir } from "../config/paths.js";
|
import { resolveOAuthDir } from "../config/paths.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||||
import { INCLUDE_KEY, MAX_INCLUDE_DEPTH } from "../config/includes.js";
|
import { INCLUDE_KEY, MAX_INCLUDE_DEPTH } from "../config/includes.js";
|
||||||
import { normalizeAgentId } from "../routing/session-key.js";
|
import { normalizeAgentId } from "../routing/session-key.js";
|
||||||
@ -105,7 +106,7 @@ export function collectSyncedFolderFindings(params: {
|
|||||||
severity: "warn",
|
severity: "warn",
|
||||||
title: "State/config path looks like a synced folder",
|
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.`,
|
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;
|
return findings;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { ClawdbotConfig } from "../config/config.js";
|
|||||||
import { resolveBrowserConfig } from "../browser/config.js";
|
import { resolveBrowserConfig } from "../browser/config.js";
|
||||||
import { resolveConfigPath, resolveStateDir } from "../config/paths.js";
|
import { resolveConfigPath, resolveStateDir } from "../config/paths.js";
|
||||||
import { resolveGatewayAuth } from "../gateway/auth.js";
|
import { resolveGatewayAuth } from "../gateway/auth.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import { buildGatewayConnectionDetails } from "../gateway/call.js";
|
import { buildGatewayConnectionDetails } from "../gateway/call.js";
|
||||||
import { probeGateway } from "../gateway/probe.js";
|
import { probeGateway } from "../gateway/probe.js";
|
||||||
import {
|
import {
|
||||||
@ -264,7 +265,7 @@ function collectBrowserControlFindings(cfg: ClawdbotConfig): SecurityAuditFindin
|
|||||||
severity: "warn",
|
severity: "warn",
|
||||||
title: "Browser control config looks invalid",
|
title: "Browser control config looks invalid",
|
||||||
detail: String(err),
|
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;
|
return findings;
|
||||||
}
|
}
|
||||||
@ -840,7 +841,7 @@ export async function runSecurityAudit(opts: SecurityAuditOptions): Promise<Secu
|
|||||||
severity: "warn",
|
severity: "warn",
|
||||||
title: "Gateway probe failed (deep)",
|
title: "Gateway probe failed (deep)",
|
||||||
detail: deep.gateway.error ?? "gateway unreachable",
|
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 { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js";
|
||||||
import { buildMentionRegexes, matchesMentionPatterns } from "../auto-reply/reply/mentions.js";
|
import { buildMentionRegexes, matchesMentionPatterns } from "../auto-reply/reply/mentions.js";
|
||||||
import { formatLocationText, toLocationContext } from "../channels/location.js";
|
import { formatLocationText, toLocationContext } from "../channels/location.js";
|
||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import {
|
import {
|
||||||
readSessionUpdatedAt,
|
readSessionUpdatedAt,
|
||||||
recordSessionMetaFromInbound,
|
recordSessionMetaFromInbound,
|
||||||
@ -234,7 +235,7 @@ export const buildTelegramMessageContext = async ({
|
|||||||
`Pairing code: ${code}`,
|
`Pairing code: ${code}`,
|
||||||
"",
|
"",
|
||||||
"Ask the bot owner to approve with:",
|
"Ask the bot owner to approve with:",
|
||||||
"clawdbot pairing approve telegram <code>",
|
formatCliCommand("clawdbot pairing approve telegram <code>"),
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { formatCliCommand } from "../cli/command-format.js";
|
||||||
import type { PollInput } from "../polls.js";
|
import type { PollInput } from "../polls.js";
|
||||||
import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.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;
|
const listener = listeners.get(id) ?? null;
|
||||||
if (!listener) {
|
if (!listener) {
|
||||||
throw new Error(
|
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 };
|
return { accountId: id, listener };
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user