diff --git a/cmd/picoclaw-launcher/README.md b/cmd/picoclaw-launcher/README.md index 641279bb1..a279eceee 100644 --- a/cmd/picoclaw-launcher/README.md +++ b/cmd/picoclaw-launcher/README.md @@ -3,17 +3,19 @@ > [!WARNING] > This project is a temporary solution and will be refactored in the future to provide a complete web service. Therefore, the APIs in this directory are not stable. -A standalone launcher for PicoClaw, providing visual JSON editing and OAuth provider authentication management. +A standalone launcher for PicoClaw, providing visual JSON editing, OAuth provider authentication management, and gateway process control. ## Features - ๐Ÿ“ **Config Editor** โ€” Sidebar-based settings UI with model management, channel configuration forms, and a raw JSON editor - ๐Ÿค– **Model Management** โ€” Model card grid with availability status (grayed out without API key), primary model selection, add/edit/delete with required/optional field separation -- ๐Ÿ“ก **Channel Configuration** โ€” Form-based settings for 12 channel types (Telegram, Discord, Slack, WeCom, DingTalk, Feishu, LINE, WhatsApp, QQ, OneBot, MaixCAM, etc.) with documentation links -- ๐Ÿ” **Provider Auth** โ€” Login to OpenAI (Device Code), Anthropic (API Token), Google Antigravity (Browser OAuth) +- ๐Ÿ“ก **Channel Configuration** โ€” Form-based settings for 12+ channel types (Telegram, Discord, Slack, WeCom, DingTalk, Feishu, LINE, WhatsApp, QQ, OneBot, MaixCAM, MagicForm, etc.) with documentation links +- ๐Ÿ” **Provider Auth** โ€” Login to OpenAI (Device Code), Anthropic (API Token), Google Antigravity (Browser OAuth with PKCE) +- ๐Ÿš€ **Gateway Process Control** โ€” Start, stop, and monitor the `picoclaw gateway` process with live log streaming - ๐ŸŒ **Embedded Frontend** โ€” Compiles to a single binary with no external dependencies - ๐ŸŒ **i18n** โ€” Chinese/English language switching with browser auto-detection - ๐ŸŽจ **Theme** โ€” Light / Dark / System theme toggle with localStorage persistence +- ๐Ÿ”’ **Security Headers** โ€” `X-Content-Type-Options`, `X-Frame-Options`, and `Content-Security-Policy` on all responses ## Quick Start @@ -31,12 +33,12 @@ go build -o picoclaw-launcher ./cmd/picoclaw-launcher/ ./picoclaw-launcher -public ``` -Open `http://localhost:18800` in your browser. +The launcher automatically opens `http://localhost:18800` in your default browser on startup. ## CLI Options ``` -Usage: picoclaw-config [options] [config.json] +Usage: picoclaw-launcher [options] [config.json] Arguments: config.json Path to the configuration file (default: ~/.picoclaw/config.json) @@ -45,10 +47,14 @@ Options: -public Listen on all interfaces (0.0.0.0), allowing access from other devices ``` +When `-public` is set, the startup banner also prints the local network IP address for LAN access. + ## API Reference Base URL: `http://localhost:18800` +Default port: `18800` + --- ### Static Files @@ -70,7 +76,7 @@ Reads the current configuration file. ```json { "config": { ... }, - "path": "/Users/xiao/.picoclaw/config.json" + "path": "/home/user/.picoclaw/config.json" } ``` @@ -78,7 +84,7 @@ Reads the current configuration file. #### PUT /api/config -Saves the configuration. The request body must be a complete Config JSON object. +Saves the configuration. The request body must be a complete Config JSON object (max 1 MB). **Request Body** โ€” `application/json` @@ -122,6 +128,13 @@ Returns the authentication status of all providers and any in-progress device co "status": "active", "account_id": "user-xxx", "expires_at": "2026-03-01T00:00:00Z" + }, + { + "provider": "google-antigravity", + "auth_method": "oauth", + "status": "active", + "email": "user@example.com", + "project_id": "projects/123/locations/global/codeAssistModels/default" } ], "pending_device": { @@ -135,7 +148,7 @@ Returns the authentication status of all providers and any in-progress device co `status` values: `active` | `expired` | `needs_refresh` -`pending_device` is only present when a device code login is in progress. +`pending_device` is only present when a device code login is in progress. Once completed, it shows `status: "success"` and is cleared on the next poll. --- @@ -149,11 +162,11 @@ Initiates a provider login. { "provider": "openai" } ``` -Supported `provider` values: `openai` | `anthropic` | `google-antigravity` +Supported `provider` values: `openai` | `anthropic` | `google-antigravity` (alias: `antigravity`) ##### OpenAI (Device Code Flow) -Returns device code info. The server polls for completion in the background. +Returns device code info. The server polls for completion in the background (15-minute timeout). ```json { @@ -164,7 +177,7 @@ Returns device code info. The server polls for completion in the background. } ``` -The user opens `device_url` in a browser and enters `user_code`. Once authenticated, `GET /api/auth/status` will show `pending_device.status` as `success`. +The user opens `device_url` in a browser and enters `user_code`. Once authenticated, `GET /api/auth/status` will show `pending_device.status` as `success`. If a device code flow is already in progress, the existing session is returned. ##### Anthropic (API Token) @@ -180,7 +193,9 @@ Requires a `token` field in the request: { "status": "success", "message": "Anthropic token saved" } ``` -##### Google Antigravity (Browser OAuth) +The token is saved to the auth credential store and the config is updated to set `auth_method: "token"` on any Anthropic model entry. + +##### Google Antigravity (Browser OAuth with PKCE) Returns an authorization URL for the frontend to open in a new tab: @@ -192,7 +207,7 @@ Returns an authorization URL for the frontend to open in a new tab: } ``` -After authentication, Google redirects to `GET /auth/callback`, which saves the credentials and redirects back to the picoclaw-config UI. +After authentication, Google redirects to `GET /auth/callback`, which exchanges the authorization code for tokens using PKCE, fetches the user's email and Cloud Code Assist project ID, saves the credentials, and redirects back to the launcher UI at `/#auth`. OAuth sessions expire after 10 minutes if not completed. --- @@ -206,7 +221,7 @@ Logs out from a provider. { "provider": "openai" } ``` -Omit or leave `provider` empty to log out from all providers. +Omit or leave `provider` empty to log out from all providers. Clears both the auth credential store and `auth_method` fields in the config file. **Response** `200 OK` @@ -224,14 +239,21 @@ OAuth browser callback endpoint (used by Google Antigravity). Called by the OAut - `state` โ€” OAuth state for CSRF validation - `code` โ€” Authorization code -On success, redirects to `/#auth`. +On success, redirects to `/#auth`. On failure, displays an error page. +--- ### Process API #### GET /api/process/status -Gets the running status of the `picoclaw gateway` process. +Gets the running status of the `picoclaw gateway` process by probing its health endpoint. + +The gateway address is read from the config file (`gateway.host` and `gateway.port`, default `127.0.0.1:18790`). + +**Query Parameters** (optional, for incremental log streaming): +- `log_offset` โ€” Last received log line index (0-based) +- `log_run_id` โ€” Run ID from previous response (detects gateway restarts) **Response** `200 OK` (Running) @@ -239,7 +261,11 @@ Gets the running status of the `picoclaw gateway` process. { "process_status": "running", "status": "ok", - "uptime": "1.010814s" + "uptime": "1.010814s", + "logs": ["[INFO] Gateway started on :18790", "..."], + "log_total": 42, + "log_run_id": 1, + "log_source": "launcher" } ``` @@ -248,15 +274,23 @@ Gets the running status of the `picoclaw gateway` process. ```json { "process_status": "stopped", - "error": "Get \"http://localhost:18790/health\": dial tcp [::1]:18790: connect: connection refused" + "error": "Get \"http://localhost:18790/health\": dial tcp [::1]:18790: connect: connection refused", + "logs": [], + "log_total": 0, + "log_run_id": 0, + "log_source": "none" } ``` +`log_source` values: `launcher` (logs captured from a process started by the launcher) | `none` (no log source available, e.g. gateway started externally or never launched) + --- #### POST /api/process/start -Starts the `picoclaw gateway` process in the background. +Starts the `picoclaw gateway` process in the background. The launcher looks for the `picoclaw` binary first in the same directory as itself, then falls back to `$PATH`. + +Stdout and stderr from the gateway process are captured into a ring buffer (200 lines) and can be streamed via `GET /api/process/status`. **Response** `200 OK` @@ -273,6 +307,8 @@ Starts the `picoclaw gateway` process in the background. Stops the running `picoclaw gateway` process. +On Linux/macOS, uses `pkill -f "picoclaw gateway"`. On Windows, uses PowerShell to find and stop matching processes. + **Response** `200 OK` ```json @@ -286,5 +322,5 @@ Stops the running `picoclaw gateway` process. ## Testing ```bash -go test -v ./cmd/picoclaw-launcher/ +go test -v ./cmd/picoclaw-launcher/... ``` diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 000000000..95948ca19 --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,367 @@ +# PicoClaw CLI Reference + +## Install + +```bash +go install github.com/nuestra-ai/picoclaw/cmd/picoclaw@latest +``` + +## Global Config + +Create `~/.picoclaw/config.json` (or set `PICOCLAW_CONFIG` env var): + +```jsonc +{ + "model_list": [ + { + "model_name": "main", + "model": "anthropic/claude-sonnet-4.6", + "api_key": "sk-ant-...", + "api_base": "https://api.anthropic.com/v1" + } + ], + "agents": { + "defaults": { + "model_name": "main", + "max_tokens": 4096, + "max_tool_iterations": 20 + } + } +} +``` + +The global config is the base. Per-workspace configs overlay it (see [Workspace Config](#workspace-config)). + +--- + +## Subcommands + +| Command | Alias | Description | +|---------|-------|-------------| +| `picoclaw agent` | | Interact with the agent (interactive REPL or one-shot) | +| `picoclaw gateway` | `g` | Start the HTTP gateway server | +| `picoclaw auth` | | Manage authentication (login, logout, status, models) | +| `picoclaw cron` | `c` | Manage scheduled tasks | +| `picoclaw skills` | | Manage skills (list, install, remove, search) | +| `picoclaw status` | | Show current status | +| `picoclaw migrate` | | Migrate config/workspace from another installation | +| `picoclaw onboard` | | Interactive first-run setup | +| `picoclaw version` | | Print version | + +--- + +## picoclaw agent + +Interact with the agent directly. Without `-m`, starts an interactive REPL. With `-m`, sends a single message and exits. + +### Flags + +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--message` | `-m` | | Single message (non-interactive). Omit for interactive REPL. | +| `--session` | `-s` | `""` (โ†’ `agent:main:cli:default`) | Session key for conversation isolation (e.g. `stackId:conversationId`). | +| `--model` | | | Override model name from config. | +| `--workspace` | | | Override agent workspace directory. | +| `--config-dir` | | | Directory containing `config.json` (model/agent/tool overrides) and bootstrap files (`AGENTS.md`, `IDENTITY.md`, `SOUL.md`, `USER.md`). | +| `--tools` | | | Comma-separated tool allowlist (e.g. `read_file,web_fetch`). Only these tools are enabled. | +| `--skills` | | | Comma-separated skill filter (e.g. `summarize,translate`). Only these skills are loaded. | +| `--debug` | `-d` | `false` | Enable debug logging. | + +### Config Precedence (highest wins) + +``` +CLI flags (--model, --tools, --skills, --workspace) + > config-dir/config.json + > ~/.picoclaw/config.json + > defaults +``` + +### Session Key Formatting + +The `--session` value is automatically prefixed: + +| Input | Resulting key | +|-------|--------------| +| _(empty)_ | `agent:main:cli:default` | +| `s1:c1` | `agent:main:cli:s1:c1` | +| `my-project` | `agent:main:cli:my-project` | +| `agent:custom:key` | `agent:custom:key` (used as-is) | + +Session files are stored at `{workspace}/sessions/{sanitized_key}.json`. + +### Examples + +```bash +# Interactive mode with default session +picoclaw agent + +# One-shot message +picoclaw agent -m "Hello, world" + +# With workspace isolation +picoclaw agent -m "Summarize the report" \ + -s tenant1:conv42 \ + --workspace /data/workspaces/tenant1/conv42 \ + --config-dir /data/workspaces/tenant1/config + +# Restricted tools, custom model +picoclaw agent -m "Search the web for recent news" \ + --tools web,web_fetch \ + --model gpt-5.2 + +# Debug mode to see session key, model, and iteration details +picoclaw agent -d -m "Hello" -s test +``` + +--- + +## picoclaw gateway + +Start the HTTP gateway server. Channels (Telegram, Discord, MagicForm, etc.) receive messages via webhooks and respond asynchronously. + +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--debug` | `-d` | `false` | Enable debug logging. | + +```bash +picoclaw gateway +# or with debug logging: +picoclaw gateway -d +``` + +Listens on `{gateway.host}:{gateway.port}` from the config (default `127.0.0.1:18790`). + +--- + +## picoclaw auth + +Manage provider authentication. + +### picoclaw auth login + +```bash +picoclaw auth login -p +``` + +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--provider` | `-p` | _(required)_ | Provider: `openai`, `anthropic`, `google-antigravity` | +| `--device-code` | | `false` | Use device code flow for headless environments (OpenAI) | +| `--setup-token` | | `false` | Use setup-token flow for Anthropic (from `claude setup-token`) | + +**Anthropic login** prompts to choose between: +1. **Setup token** (recommended) โ€” paste a token from `claude setup-token` +2. **API key** โ€” paste an `sk-ant-...` key from console.anthropic.com + +Use `--setup-token` to skip the prompt and go directly to option 1. + +**OpenAI login** uses browser-based OAuth by default, or `--device-code` for headless environments. + +**Google Antigravity login** uses browser-based OAuth with PKCE. Also fetches user email and Cloud Code Assist project ID. + +### picoclaw auth logout + +```bash +picoclaw auth logout [-p ] +``` + +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--provider` | `-p` | `""` (all) | Provider to logout from. Empty = logout from all. | + +### picoclaw auth status + +```bash +picoclaw auth status +``` + +Shows all authenticated providers with method, status, account info, and expiry. For Anthropic OAuth credentials, also displays 5-hour and 7-day usage percentages. + +### picoclaw auth models + +```bash +picoclaw auth models +``` + +Lists available models for Google Antigravity (requires prior login). Shows model ID, display name, and quota status. + +--- + +## picoclaw cron + +Manage scheduled tasks. + +### picoclaw cron list + +```bash +picoclaw cron list +``` + +### picoclaw cron add + +```bash +picoclaw cron add -n -m (--every | --cron ) +``` + +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--name` | `-n` | _(required)_ | Job name | +| `--message` | `-m` | _(required)_ | Message for the agent | +| `--every` | `-e` | | Run every N seconds | +| `--cron` | `-c` | | Cron expression (e.g. `0 9 * * *`) | +| `--deliver` | `-d` | `false` | Deliver response to a channel | +| `--channel` | | | Channel for delivery | +| `--to` | | | Recipient for delivery | + +`--every` and `--cron` are mutually exclusive; one is required. + +### picoclaw cron remove / enable / disable + +```bash +picoclaw cron remove +picoclaw cron enable +picoclaw cron disable +``` + +--- + +## picoclaw skills + +Manage skills (install, remove, list, search). + +```bash +picoclaw skills list # List installed skills +picoclaw skills list-builtin # List built-in skills +picoclaw skills install # Install from URL or local path +picoclaw skills install-builtin # Install built-in skills to workspace +picoclaw skills remove # Remove an installed skill +picoclaw skills search # Search skill registries +picoclaw skills show # Show skill details +``` + +The `install` command also supports `--registry ` to install from a named registry. + +--- + +## picoclaw migrate + +Migrate config and workspace from another installation (e.g. OpenClaw). + +| Flag | Default | Description | +|------|---------|-------------| +| `--from` | `openclaw` | Source to migrate from | +| `--dry-run` | `false` | Preview changes without applying | +| `--refresh` | `false` | Re-run migration (overwrite existing) | +| `--config-only` | `false` | Migrate config only | +| `--workspace-only` | `false` | Migrate workspace only | +| `--force` | `false` | Overwrite existing files | +| `--source-home` | | Custom source home directory | +| `--target-home` | | Custom target home directory | + +--- + +## Workspace Config + +A workspace-level `config.json` (placed in the config directory) overlays the global config. Only these fields are honored: + +| Field | Behavior | Required? | +|-------|----------|-----------| +| `model_list` | Replaces global model_list | Yes, if using a different API key | +| `agents.defaults` | Merges non-zero fields (model_name, max_tokens, temperature, etc.) | No | +| `agents.list` | Replaces global agents list | No | +| `tools` | Merges only keys present in the file (unmentioned tools are not affected) | No | +| `session` | Merges non-zero fields (dm_scope, identity_links) | No | +| `bindings` | Replaces global bindings | No | + +**Not honored** (infrastructure-level): `gateway`, `heartbeat`, `devices`, `providers`, `channels`. + +Example `config.json`: + +```json +{ + "model_list": [ + { + "model_name": "main", + "model": "anthropic/claude-sonnet-4.6", + "api_key": "sk-ant-tenant-key", + "api_base": "https://api.anthropic.com/v1" + } + ], + "agents": { + "defaults": { + "model_name": "main", + "max_tokens": 4096, + "temperature": 0.7 + } + }, + "tools": { + "exec": { "enabled": false } + } +} +``` + +--- + +## Directory Layout + +PicoClaw uses the following workspace directory structure for per-tenant/per-conversation isolation: + +``` +{workspace_root}/ + {stackId}/ + config/ # configDir -- shared per-stack + config.json # API key, model, agent settings + AGENTS.md # Agent instructions (optional) + IDENTITY.md # Agent identity (optional) + SOUL.md # Agent personality (optional) + USER.md # User context (optional) + {conversationId}/ # workspace -- per-conversation + sessions/ # Conversation history (managed by PicoClaw) + memory/ # Persistent agent memory (managed by PicoClaw) + skills/ # Workspace-local skills (optional) +``` + +The config directory contains shared settings (API keys, bootstrap files) for all conversations in a stack. Each conversation gets its own workspace directory with isolated sessions and memory. + +--- + +## Tool Names Reference + +For `--tools` (CLI) or `allowedTools` (webhook API): + +| Tool name | Description | +|-----------|-------------| +| `read_file` | Read files from workspace | +| `write_file` | Write files to workspace | +| `edit_file` | Edit files in workspace | +| `append_file` | Append to files in workspace | +| `list_dir` | List directory contents | +| `exec` | Execute shell commands | +| `spawn` | Spawn background processes | +| `cron` | Schedule recurring tasks | +| `web` | Web search (DuckDuckGo, Brave, etc.) | +| `web_fetch` | Fetch and parse web pages | +| `skills` | Run installed skills | +| `find_skills` | Search skill registries | +| `install_skill` | Install skills from registry | +| `subagent` | Spawn sub-agents | +| `message` | Send messages to channels | +| `mcp` | Model Context Protocol tools | +| `i2c` | I2C hardware bus (Linux only) | +| `spi` | SPI hardware bus (Linux only) | + +--- + +## Troubleshooting + +**Workspace config ignored** +- Verify `config.json` exists in the `--config-dir` path. +- Check that the JSON is valid (`picoclaw agent -d` shows parse errors). +- Only allowed fields are merged. `gateway`, `heartbeat`, `devices`, `providers` are ignored. + +**Tools unexpectedly disabled** +- Workspace `config.json` only affects tools explicitly mentioned. If you set `{"tools": {"exec": {"enabled": false}}}`, only `exec` is disabled; all other tools keep their global config values. + +**Session not persisting** +- Ensure the same `--workspace` path is used for the same conversation. +- Sessions are stored at `{workspace}/sessions/`. Different workspace paths = different sessions. diff --git a/docs/magicform-integration.md b/docs/magicform-integration.md new file mode 100644 index 000000000..a34b264c3 --- /dev/null +++ b/docs/magicform-integration.md @@ -0,0 +1,254 @@ +# MagicForm Integration Spec + +MagicForm delegates agentic tasks to PicoClaw via webhooks. PicoClaw processes the request asynchronously and POSTs the result back to a callback URL. + +> For general PicoClaw installation, CLI usage, and config reference, see [cli.md](cli.md). + +--- + +## Pre-requisites + +1. **Install PicoClaw** โ€” see [cli.md ยง Install](cli.md#install) +2. **Global config** โ€” see [cli.md ยง Global Config](cli.md#global-config) + +### Directory layout + +MagicForm pre-provisions directories on disk before calling PicoClaw (see [cli.md ยง Directory Layout](cli.md#directory-layout) for the general structure): + +``` +{workspace_root}/ + {stackId}/ + config/ # configDir -- shared per-stack + config.json # API key, model, agent settings for this stack + AGENTS.md # Agent instructions (optional) + IDENTITY.md # Agent identity (optional) + SOUL.md # Agent personality (optional) + USER.md # User context (optional) + {conversationId}/ # workspace -- per-conversation + sessions/ # Conversation history (managed by PicoClaw) + memory/ # Persistent agent memory (managed by PicoClaw) + skills/ # Workspace-local skills (optional) +``` + +### Workspace config + +Per-stack config overlays are placed in the config directory. See [cli.md ยง Workspace Config](cli.md#workspace-config) for merge rules and example. + +--- + +## Gateway Mode + +### Channel config + +Add to `~/.picoclaw/config.json`: + +```jsonc +{ + "gateway": { + "host": "0.0.0.0", + "port": 18790 + }, + "channels": { + "magicform": { + "enabled": true, + "token": "your-shared-secret", + "backend_url": "https://api.magicform.example.com", + "webhook_path": "/hooks/magicform", + "workspace_root": "/data/workspaces", + "allow_from": [] + } + } +} +``` + +| Field | Description | Default | +|-------|-------------|---------| +| `token` | Bearer token for webhook auth. Empty = allow all (dev only). | `""` | +| `backend_url` | Fallback callback URL base (used when payload omits `callbackUrl`). | `""` | +| `webhook_path` | HTTP path for the webhook endpoint. | `/hooks/magicform` | +| `workspace_root` | Root directory for workspace/configDir path validation. Required for path security. | `""` | +| `allow_from` | Sender ID allowlist. Empty = allow all. Accepts strings and numbers (e.g. `["user1", 12345]`). | `[]` | + +All fields can be set via environment variables: + +```bash +PICOCLAW_CHANNELS_MAGICFORM_ENABLED=true +PICOCLAW_CHANNELS_MAGICFORM_TOKEN=your-shared-secret +PICOCLAW_CHANNELS_MAGICFORM_BACKEND_URL=https://api.magicform.example.com +PICOCLAW_CHANNELS_MAGICFORM_WEBHOOK_PATH=/hooks/magicform +PICOCLAW_CHANNELS_MAGICFORM_WORKSPACE_ROOT=/data/workspaces +PICOCLAW_CHANNELS_MAGICFORM_ALLOW_FROM=sender1,sender2 +``` + +### Start the gateway + +```bash +picoclaw gateway +# or with debug logging: +picoclaw gateway -d +``` + +Listens on `{host}:{port}` (default `127.0.0.1:18790`). + +### Health check + +``` +GET /health/magicform +``` + +Response: + +```json +{"status": "ok", "channel": "magicform"} +``` + +### Webhook: send a message + +``` +POST /hooks/magicform +Authorization: Bearer your-shared-secret +Content-Type: application/json +``` + +#### Request body + +```json +{ + "stackId": "s1", + "conversationId": "c1", + "userId": "user-123", + "message": "Summarize the latest sales report", + "workspace": "s1/c1", + "configDir": "s1/config", + "callbackUrl": "https://api.magicform.example.com/claw-agent/callback", + "allowedTools": ["read_file", "web_fetch"], + "allowedSkills": ["summarize"] +} +``` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `stackId` | string | Yes | Tenant/stack identifier. | +| `conversationId` | string | Yes | Conversation identifier. | +| `userId` | string | No | Sender identifier (defaults to `"anonymous"`). | +| `message` | string | Yes | The user's message. | +| `workspace` | string | No | Agent working directory, relative to `workspace_root`. | +| `configDir` | string | No | Config directory, relative to `workspace_root`. Contains `config.json` and bootstrap files. | +| `callbackUrl` | string | No | Where to POST the response. Falls back to `backend_url + "/claw-agent/callback"`. | +| `allowedTools` | string[] | No | Tool allowlist. Empty = all tools enabled. See [cli.md ยง Tool Names](cli.md#tool-names-reference). | +| `allowedSkills` | string[] | No | Skill filter. Empty = all skills loaded. | + +**Path security**: `workspace` and `configDir` must be relative paths that resolve under `workspace_root`. Traversal attempts (e.g. `../../etc`) are rejected with `400 Bad Request`. + +**Request size limit**: Webhook payloads are limited to 1 MB. Larger requests receive `413 Request Entity Too Large`. + +**Method**: Only `POST` is accepted. Other methods receive `405 Method Not Allowed`. + +#### Response + +Returns `200 OK` immediately. Processing happens asynchronously. + +### Callback: receive the result + +PicoClaw POSTs the result to the callback URL: + +``` +POST {callbackUrl} +Authorization: Bearer your-shared-secret +Content-Type: application/json +``` + +```json +{ + "stackId": "s1", + "conversationId": "c1", + "response": "Here is the summary of the latest sales report...", + "type": "final" +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `stackId` | string | Echoed from request. | +| `conversationId` | string | Echoed from request. | +| `response` | string | The agent's response text. | +| `type` | string | Always `"final"`. | + +### Session isolation + +Each request gets a unique session key: `agent:main:magicform:{stackId}:{conversationId}`. + +Sessions are stored at `{workspace}/sessions/`. Different conversations within the same stack share the config directory (API keys, bootstrap files) but have separate workspace directories, sessions, and memory. + +### Processing flow + +``` +MagicForm PicoClaw Gateway + | | + |-- POST /hooks/magicform ----------->| + |<------------ 200 OK ---------------| + | | + | resolveWorkspace(workspace) + | resolveWorkspace(configDir) + | publish InboundMessage to bus + | | + | Agent loop: + | create temp sessions + context + | copyBootstrapFiles(configDir -> workspace) + | loadWorkspaceConfig(configDir) + | mergeWorkspaceConfig into cloned global cfg + | createProvider (per-request) + | apply tool/skill filters + | run LLM iterations + | | + |<-- POST callbackUrl (result) -------| +``` + +--- + +## Testing with CLI + +You can test the same workspace/config setup without the gateway using `picoclaw agent`. See [cli.md ยง picoclaw agent](cli.md#picoclaw-agent) for full flag reference. + +```bash +# One-shot with tenant isolation (same paths MagicForm would use) +picoclaw agent -m "Summarize the report" \ + -s s1:c1 \ + --workspace /data/workspaces/s1/c1 \ + --config-dir /data/workspaces/s1/config + +# Restricted tools, matching a webhook allowedTools filter +picoclaw agent -m "Search the web for recent news" \ + --tools web,web_fetch + +# Debug mode to see session key, model, and iteration details +picoclaw agent -d -m "Hello" -s test +``` + +--- + +## Troubleshooting + +**Webhook returns 405 Method Not Allowed** +- The endpoint only accepts `POST` requests. Ensure you are not sending a `GET` or other method. + +**Webhook returns 401 Unauthorized** +- Check that the `Authorization: Bearer {token}` header matches the `token` in the MagicForm channel config. Token comparison uses constant-time comparison. + +**Webhook returns 400 "workspace path escapes workspace_root"** +- The `workspace` or `configDir` path in the payload resolves outside `workspace_root`. Ensure paths are relative (e.g. `s1/c1`, not `/data/workspaces/s1/c1`). + +**Webhook returns 413 Request Entity Too Large** +- The request payload exceeds the 1 MB limit. Reduce the message size. + +**Webhook returns 400 "workspace_root not configured"** +- Set `workspace_root` in the MagicForm channel config. + +**Callback not received** +- Check that `callbackUrl` in the payload or `backend_url` in config is reachable from PicoClaw. +- Check PicoClaw logs for callback errors. +- Request contexts expire after 10 minutes. + +**Session not persisting across requests** +- Ensure the same `workspace` path is sent for the same conversation. +- Sessions are stored at `{workspace}/sessions/`. Different workspace paths = different sessions.