mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
Merge pull request #537 from Esubaalew/main
fix: correct documentation misalignment across translations and guides
This commit is contained in:
+19
-12
@@ -212,19 +212,24 @@ picoclaw onboard
|
||||
|
||||
```json
|
||||
{
|
||||
"model_list": [
|
||||
{
|
||||
"model_name": "gpt4",
|
||||
"model": "openai/gpt-5.2",
|
||||
"api_key": "sk-your-openai-key",
|
||||
"api_base": "https://api.openai.com/v1"
|
||||
}
|
||||
],
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"workspace": "~/.picoclaw/workspace",
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
"model": "gpt4"
|
||||
}
|
||||
},
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"api_key": "xxx",
|
||||
"api_base": "https://openrouter.ai/api/v1"
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "VOTRE_TOKEN_BOT",
|
||||
"allow_from": ["VOTRE_USER_ID"]
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
@@ -290,7 +295,7 @@ Discutez avec votre PicoClaw via Telegram, Discord, DingTalk, LINE ou WeCom
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "VOTRE_TOKEN_BOT",
|
||||
"allowFrom": ["VOTRE_USER_ID"]
|
||||
"allow_from": ["VOTRE_USER_ID"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,7 +338,7 @@ picoclaw gateway
|
||||
"discord": {
|
||||
"enabled": true,
|
||||
"token": "VOTRE_TOKEN_BOT",
|
||||
"allowFrom": ["VOTRE_USER_ID"]
|
||||
"allow_from": ["VOTRE_USER_ID"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -765,6 +770,8 @@ Le sous-agent a accès aux outils (message, web_search, etc.) et peut communique
|
||||
| `anthropic` (À tester) | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) |
|
||||
| `openai` (À tester) | LLM (GPT direct) | [platform.openai.com](https://platform.openai.com) |
|
||||
| `deepseek` (À tester) | LLM (DeepSeek direct) | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||
| `qwen` | LLM (Alibaba Qwen) | [dashscope.aliyuncs.com](https://dashscope.aliyuncs.com/compatible-mode/v1) |
|
||||
| `cerebras` | LLM (Cerebras) | [cerebras.ai](https://api.cerebras.ai/v1) |
|
||||
| `groq` | LLM + **Transcription vocale** (Whisper) | [console.groq.com](https://console.groq.com) |
|
||||
|
||||
<details>
|
||||
@@ -1087,7 +1094,7 @@ Ajoutez la clé dans `~/.picoclaw/config.json` si vous utilisez Brave :
|
||||
"tools": {
|
||||
"web": {
|
||||
"brave": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"api_key": "VOTRE_CLE_API_BRAVE",
|
||||
"max_results": 5
|
||||
},
|
||||
|
||||
+28
-33
@@ -174,35 +174,25 @@ picoclaw onboard
|
||||
|
||||
```json
|
||||
{
|
||||
"model_list": [
|
||||
{
|
||||
"model_name": "gpt4",
|
||||
"model": "openai/gpt-5.2",
|
||||
"api_key": "sk-your-openai-key",
|
||||
"api_base": "https://api.openai.com/v1"
|
||||
}
|
||||
],
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"workspace": "~/.picoclaw/workspace",
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
"model": "gpt4"
|
||||
}
|
||||
},
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"api_key": "xxx",
|
||||
"api_base": "https://openrouter.ai/api/v1"
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "YOUR_TELEGRAM_BOT_TOKEN",
|
||||
"allow_from": []
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
"exec_timeout_minutes": 5
|
||||
}
|
||||
},
|
||||
"heartbeat": {
|
||||
"enabled": true,
|
||||
"interval": 30
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -214,7 +204,7 @@ picoclaw onboard
|
||||
|
||||
> **注意**: 完全な設定テンプレートは `config.example.json` を参照してください。
|
||||
|
||||
**3. チャット**
|
||||
**4. チャット**
|
||||
|
||||
```bash
|
||||
picoclaw agent -m "What is 2+2?"
|
||||
@@ -764,10 +754,10 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
|
||||
},
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"apiKey": "sk-or-v1-xxx"
|
||||
"api_key": "sk-or-v1-xxx"
|
||||
},
|
||||
"groq": {
|
||||
"apiKey": "gsk_xxx"
|
||||
"api_key": "gsk_xxx"
|
||||
}
|
||||
},
|
||||
"channels": {
|
||||
@@ -786,17 +776,17 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
|
||||
},
|
||||
"feishu": {
|
||||
"enabled": false,
|
||||
"appId": "cli_xxx",
|
||||
"appSecret": "xxx",
|
||||
"encryptKey": "",
|
||||
"verificationToken": "",
|
||||
"app_id": "cli_xxx",
|
||||
"app_secret": "xxx",
|
||||
"encrypt_key": "",
|
||||
"verification_token": "",
|
||||
"allow_from": []
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"apiKey": "BSA..."
|
||||
"api_key": "BSA..."
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
@@ -1001,9 +991,14 @@ Web 検索を有効にするには:
|
||||
{
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"brave": {
|
||||
"enabled": true,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ picoclaw gateway
|
||||
}
|
||||
```
|
||||
|
||||
> Set `allow_from` to empty to allow all users, or specify QQ numbers to restrict access.
|
||||
> Set `allow_from` to empty to allow all users, or specify DingTalk user IDs to restrict access.
|
||||
|
||||
**3. Run**
|
||||
|
||||
@@ -867,15 +867,15 @@ This design also enables **multi-agent support** with flexible provider selectio
|
||||
}
|
||||
```
|
||||
|
||||
**Anthropic (with OAuth)**
|
||||
**Anthropic (with API key)**
|
||||
```json
|
||||
{
|
||||
"model_name": "claude-sonnet-4.6",
|
||||
"model": "anthropic/claude-sonnet-4.6",
|
||||
"auth_method": "oauth"
|
||||
"api_key": "sk-ant-your-key"
|
||||
}
|
||||
```
|
||||
> Run `picoclaw auth login --provider anthropic` to set up OAuth credentials.
|
||||
> Run `picoclaw auth login --provider anthropic` to paste your API token.
|
||||
|
||||
**Ollama (local)**
|
||||
```json
|
||||
|
||||
+15
-14
@@ -213,19 +213,17 @@ picoclaw onboard
|
||||
|
||||
```json
|
||||
{
|
||||
"model_list": [
|
||||
{
|
||||
"model_name": "gpt4",
|
||||
"model": "openai/gpt-5.2",
|
||||
"api_key": "sk-your-openai-key",
|
||||
"api_base": "https://api.openai.com/v1"
|
||||
}
|
||||
],
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"workspace": "~/.picoclaw/workspace",
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
}
|
||||
},
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"api_key": "xxx",
|
||||
"api_base": "https://openrouter.ai/api/v1"
|
||||
"model": "gpt4"
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
@@ -291,7 +289,7 @@ Converse com seu PicoClaw via Telegram, Discord, DingTalk, LINE ou WeCom.
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "YOUR_BOT_TOKEN",
|
||||
"allowFrom": ["YOUR_USER_ID"]
|
||||
"allow_from": ["YOUR_USER_ID"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +332,7 @@ picoclaw gateway
|
||||
"discord": {
|
||||
"enabled": true,
|
||||
"token": "YOUR_BOT_TOKEN",
|
||||
"allowFrom": ["YOUR_USER_ID"]
|
||||
"allow_from": ["YOUR_USER_ID"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -766,6 +764,8 @@ O subagente tem acesso às ferramentas (message, web_search, etc.) e pode se com
|
||||
| `anthropic` (Em teste) | LLM (Claude direto) | [console.anthropic.com](https://console.anthropic.com) |
|
||||
| `openai` (Em teste) | LLM (GPT direto) | [platform.openai.com](https://platform.openai.com) |
|
||||
| `deepseek` (Em teste) | LLM (DeepSeek direto) | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||
| `qwen` | Alibaba Qwen | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) |
|
||||
| `cerebras` | Cerebras | [cerebras.ai](https://cerebras.ai) |
|
||||
| `groq` | LLM + **Transcrição de voz** (Whisper) | [console.groq.com](https://console.groq.com) |
|
||||
|
||||
<details>
|
||||
@@ -1088,7 +1088,7 @@ Adicione a key em `~/.picoclaw/config.json` se usar o Brave:
|
||||
"tools": {
|
||||
"web": {
|
||||
"brave": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
@@ -1119,3 +1119,4 @@ Isso acontece quando outra instância do bot está em execução. Certifique-se
|
||||
| **Zhipu** | 200K tokens/mês | Melhor para usuários chineses |
|
||||
| **Brave Search** | 2000 consultas/mês | Funcionalidade de busca web |
|
||||
| **Groq** | Plano gratuito disponível | Inferência ultra-rápida (Llama, Mixtral) |
|
||||
| **Cerebras** | Plano gratuito disponível | Inferência ultra-rápida (Llama 3.3 70B) |
|
||||
|
||||
+17
-23
@@ -193,32 +193,24 @@ picoclaw onboard
|
||||
|
||||
```json
|
||||
{
|
||||
"model_list": [
|
||||
{
|
||||
"model_name": "gpt4",
|
||||
"model": "openai/gpt-5.2",
|
||||
"api_key": "sk-your-openai-key",
|
||||
"api_base": "https://api.openai.com/v1"
|
||||
}
|
||||
],
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"workspace": "~/.picoclaw/workspace",
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
"model": "gpt4"
|
||||
}
|
||||
},
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"api_key": "xxx",
|
||||
"api_base": "https://openrouter.ai/api/v1"
|
||||
}
|
||||
},
|
||||
"tools": {
|
||||
"web": {
|
||||
"brave": {
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
}
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"token": "YOUR_TELEGRAM_BOT_TOKEN",
|
||||
"allow_from": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -747,6 +739,8 @@ Subagent có quyền truy cập các công cụ (message, web_search, v.v.) và
|
||||
| `openai` (Đang thử nghiệm) | LLM (GPT trực tiếp) | [platform.openai.com](https://platform.openai.com) |
|
||||
| `deepseek` (Đang thử nghiệm) | LLM (DeepSeek trực tiếp) | [platform.deepseek.com](https://platform.deepseek.com) |
|
||||
| `groq` | LLM + **Chuyển giọng nói** (Whisper) | [console.groq.com](https://console.groq.com) |
|
||||
| `qwen` | LLM (Qwen trực tiếp) | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) |
|
||||
| `cerebras` | LLM (Cerebras trực tiếp) | [cerebras.ai](https://cerebras.ai) |
|
||||
|
||||
<details>
|
||||
<summary><b>Cấu hình Zhipu</b></summary>
|
||||
@@ -1065,7 +1059,7 @@ Thêm key vào `~/.picoclaw/config.json` nếu dùng Brave:
|
||||
"tools": {
|
||||
"web": {
|
||||
"brave": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
|
||||
+25
-8
@@ -238,9 +238,14 @@ picoclaw onboard
|
||||
],
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"brave": {
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
@@ -345,7 +350,8 @@ picoclaw gateway
|
||||
"discord": {
|
||||
"enabled": true,
|
||||
"token": "YOUR_BOT_TOKEN",
|
||||
"allow_from": ["YOUR_USER_ID"]
|
||||
"allow_from": ["YOUR_USER_ID"],
|
||||
"mention_only": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,7 +531,7 @@ picoclaw gateway
|
||||
|
||||
只需通过 CLI 或任何集成的聊天应用发送一条消息,即可将 PicoClaw 连接到 Agent 社交网络。
|
||||
|
||||
**阅读 `https://clawdchat.ai/skill.md` 并按照说明加入 [ClawdChat.ai**](https://clawdchat.ai)
|
||||
**阅读 `https://clawdchat.ai/skill.md` 并按照说明加入 [ClawdChat.ai](https://clawdchat.ai)
|
||||
|
||||
## ⚙️ 配置详解
|
||||
|
||||
@@ -857,8 +863,8 @@ Agent 读取 HEARTBEAT.md
|
||||
"zhipu": {
|
||||
"api_key": "Your API Key",
|
||||
"api_base": "https://open.bigmodel.cn/api/paas/v4"
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@@ -921,8 +927,14 @@ picoclaw agent -m "你好"
|
||||
},
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"api_key": "BSA..."
|
||||
"brave": {
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
@@ -989,9 +1001,14 @@ Discord: [https://discord.gg/V4sAZ9XWpN](https://discord.gg/V4sAZ9XWpN)
|
||||
{
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"brave": {
|
||||
"enabled": false,
|
||||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
-315
@@ -378,7 +378,7 @@ const antigravityPlugin = {
|
||||
description: "OAuth flow for Google Antigravity (Cloud Code Assist)",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
|
||||
register(api: OpenClawPluginApi) {
|
||||
register(api: PicoClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: "google-antigravity",
|
||||
label: "Google Antigravity",
|
||||
@@ -405,7 +405,7 @@ const antigravityPlugin = {
|
||||
|
||||
```typescript
|
||||
type ProviderAuthContext = {
|
||||
config: OpenClawConfig;
|
||||
config: PicoClawConfig;
|
||||
agentDir?: string;
|
||||
workspaceDir?: string;
|
||||
prompter: WizardPrompter; // UI prompts/notifications
|
||||
@@ -426,7 +426,7 @@ type ProviderAuthResult = {
|
||||
profileId: string;
|
||||
credential: AuthProfileCredential;
|
||||
}>;
|
||||
configPatch?: Partial<OpenClawConfig>;
|
||||
configPatch?: Partial<PicoClawConfig>;
|
||||
defaultModel?: string;
|
||||
notes?: string[];
|
||||
};
|
||||
@@ -438,10 +438,9 @@ type ProviderAuthResult = {
|
||||
|
||||
### 1. Required Environment/Dependencies
|
||||
|
||||
- Node.js ≥ 22
|
||||
- OpenClaw plugin-sdk
|
||||
- crypto module (built-in)
|
||||
- http module (built-in)
|
||||
- Go ≥ 1.21
|
||||
- PicoClaw codebase (`pkg/providers/` and `pkg/auth/`)
|
||||
- `crypto` and `net/http` standard library packages
|
||||
|
||||
### 2. Required Headers for API Calls
|
||||
|
||||
@@ -572,36 +571,40 @@ Each SSE message (`data: {...}`) is wrapped in a `response` field:
|
||||
|
||||
## Configuration
|
||||
|
||||
### openclaw.json Configuration
|
||||
### config.json Configuration
|
||||
|
||||
```json5
|
||||
```json
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "google-antigravity/claude-opus-4-6-thinking",
|
||||
},
|
||||
},
|
||||
},
|
||||
"model_list": [
|
||||
{
|
||||
"model_name": "gemini-flash",
|
||||
"model": "antigravity/gemini-3-flash",
|
||||
"auth_method": "oauth"
|
||||
}
|
||||
],
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": "gemini-flash"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auth Profile Storage
|
||||
|
||||
Auth profiles are stored in `~/.openclaw/agent/auth-profiles.json`:
|
||||
Auth profiles are stored in `~/.picoclaw/auth.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"profiles": {
|
||||
"google-antigravity:user@example.com": {
|
||||
"type": "oauth",
|
||||
"credentials": {
|
||||
"google-antigravity": {
|
||||
"access_token": "ya29...",
|
||||
"refresh_token": "1//...",
|
||||
"expires_at": "2026-01-01T00:00:00Z",
|
||||
"provider": "google-antigravity",
|
||||
"access": "ya29...",
|
||||
"refresh": "1//...",
|
||||
"expires": 1704067200000,
|
||||
"auth_method": "oauth",
|
||||
"email": "user@example.com",
|
||||
"projectId": "my-project-id"
|
||||
"project_id": "my-project-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -611,277 +614,85 @@ Auth profiles are stored in `~/.openclaw/agent/auth-profiles.json`:
|
||||
|
||||
## Creating a New Provider in PicoClaw
|
||||
|
||||
PicoClaw providers are implemented as Go packages under `pkg/providers/`. To add a new provider:
|
||||
|
||||
### Step-by-Step Implementation
|
||||
|
||||
#### 1. Create Plugin Structure
|
||||
#### 1. Create Provider File
|
||||
|
||||
Create a new Go file in `pkg/providers/`:
|
||||
|
||||
```
|
||||
extensions/
|
||||
└── your-provider-auth/
|
||||
├── openclaw.plugin.json
|
||||
├── package.json
|
||||
├── README.md
|
||||
└── index.ts
|
||||
pkg/providers/
|
||||
└── your_provider.go
|
||||
```
|
||||
|
||||
#### 2. Define Plugin Manifest
|
||||
#### 2. Implement the Provider Interface
|
||||
|
||||
**openclaw.plugin.json:**
|
||||
```json
|
||||
{
|
||||
"id": "your-provider-auth",
|
||||
"providers": ["your-provider"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
Your provider must implement the `Provider` interface defined in `pkg/providers/types.go`:
|
||||
|
||||
```go
|
||||
package providers
|
||||
|
||||
type YourProvider struct {
|
||||
apiKey string
|
||||
apiBase string
|
||||
}
|
||||
```
|
||||
|
||||
**package.json:**
|
||||
```json
|
||||
{
|
||||
"name": "@openclaw/your-provider-auth",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "Your Provider OAuth plugin",
|
||||
"type": "module"
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Implement OAuth Flow
|
||||
|
||||
```typescript
|
||||
import {
|
||||
buildOauthProviderAuthResult,
|
||||
emptyPluginConfigSchema,
|
||||
type OpenClawPluginApi,
|
||||
type ProviderAuthContext,
|
||||
} from "openclaw/plugin-sdk";
|
||||
|
||||
const YOUR_CLIENT_ID = "your-client-id";
|
||||
const YOUR_CLIENT_SECRET = "your-client-secret";
|
||||
const AUTH_URL = "https://provider.com/oauth/authorize";
|
||||
const TOKEN_URL = "https://provider.com/oauth/token";
|
||||
const REDIRECT_URI = "http://localhost:PORT/oauth-callback";
|
||||
|
||||
async function loginYourProvider(params: {
|
||||
isRemote: boolean;
|
||||
openUrl: (url: string) => Promise<void>;
|
||||
prompt: (message: string) => Promise<string>;
|
||||
note: (message: string, title?: string) => Promise<void>;
|
||||
log: (message: string) => void;
|
||||
progress: { update: (msg: string) => void; stop: (msg?: string) => void };
|
||||
}) {
|
||||
// 1. Generate PKCE
|
||||
const { verifier, challenge } = generatePkce();
|
||||
const state = randomBytes(16).toString("hex");
|
||||
|
||||
// 2. Build auth URL
|
||||
const authUrl = buildAuthUrl({ challenge, state });
|
||||
|
||||
// 3. Start callback server (if not remote)
|
||||
const callbackServer = !params.isRemote
|
||||
? await startCallbackServer({ timeoutMs: 5 * 60 * 1000 })
|
||||
: null;
|
||||
|
||||
// 4. Open browser or show URL
|
||||
if (callbackServer) {
|
||||
await params.openUrl(authUrl);
|
||||
const callback = await callbackServer.waitForCallback();
|
||||
code = callback.searchParams.get("code");
|
||||
} else {
|
||||
await params.note(`Auth URL: ${authUrl}`, "OAuth");
|
||||
const input = await params.prompt("Paste redirect URL:");
|
||||
const parsed = parseCallbackInput(input);
|
||||
code = parsed.code;
|
||||
}
|
||||
|
||||
// 5. Exchange code for tokens
|
||||
const tokens = await exchangeCode({ code, verifier });
|
||||
|
||||
// 6. Fetch additional user data
|
||||
const email = await fetchUserEmail(tokens.access);
|
||||
|
||||
return { ...tokens, email };
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Register Provider
|
||||
|
||||
```typescript
|
||||
const yourProviderPlugin = {
|
||||
id: "your-provider-auth",
|
||||
name: "Your Provider Auth",
|
||||
description: "OAuth for Your Provider",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: "your-provider",
|
||||
label: "Your Provider",
|
||||
docsPath: "/providers/models",
|
||||
aliases: ["yp"],
|
||||
|
||||
auth: [
|
||||
{
|
||||
id: "oauth",
|
||||
label: "OAuth Login",
|
||||
hint: "Browser-based authentication",
|
||||
kind: "oauth",
|
||||
|
||||
run: async (ctx: ProviderAuthContext) => {
|
||||
const spin = ctx.prompter.progress("Starting OAuth...");
|
||||
|
||||
try {
|
||||
const result = await loginYourProvider({
|
||||
isRemote: ctx.isRemote,
|
||||
openUrl: ctx.openUrl,
|
||||
prompt: async (msg) => String(await ctx.prompter.text({ message: msg })),
|
||||
note: ctx.prompter.note,
|
||||
log: (msg) => ctx.runtime.log(msg),
|
||||
progress: spin,
|
||||
});
|
||||
|
||||
return buildOauthProviderAuthResult({
|
||||
providerId: "your-provider",
|
||||
defaultModel: "your-provider/model-name",
|
||||
access: result.access,
|
||||
refresh: result.refresh,
|
||||
expires: result.expires,
|
||||
email: result.email,
|
||||
notes: ["Provider-specific notes"],
|
||||
});
|
||||
} catch (err) {
|
||||
spin.stop("OAuth failed");
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default yourProviderPlugin;
|
||||
```
|
||||
|
||||
#### 5. Implement Usage Tracking (Optional)
|
||||
|
||||
```typescript
|
||||
// src/infra/provider-usage.fetch.your-provider.ts
|
||||
export async function fetchYourProviderUsage(
|
||||
token: string,
|
||||
timeoutMs: number,
|
||||
fetchFn: typeof fetch
|
||||
): Promise<ProviderUsageSnapshot> {
|
||||
// Fetch usage data from provider API
|
||||
const response = await fetchFn("https://api.provider.com/usage", {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
provider: "your-provider",
|
||||
displayName: "Your Provider",
|
||||
windows: [
|
||||
{ label: "Credits", usedPercent: data.usedPercent },
|
||||
],
|
||||
plan: data.planName,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. Register Usage Fetcher
|
||||
|
||||
```typescript
|
||||
// src/infra/provider-usage.load.ts
|
||||
case "your-provider":
|
||||
return await fetchYourProviderUsage(auth.token, timeoutMs, fetchFn);
|
||||
```
|
||||
|
||||
#### 7. Add Provider to Type Definitions
|
||||
|
||||
```typescript
|
||||
// src/infra/provider-usage.types.ts
|
||||
export type SupportedProvider =
|
||||
| "anthropic"
|
||||
| "github-copilot"
|
||||
| "google-gemini-cli"
|
||||
| "google-antigravity"
|
||||
| "your-provider" // Add here
|
||||
| "minimax"
|
||||
| "openai-codex";
|
||||
```
|
||||
|
||||
#### 8. Add Auth Choice Handler
|
||||
|
||||
```typescript
|
||||
// src/commands/auth-choice.apply.your-provider.ts
|
||||
import { applyAuthChoicePluginProvider } from "./auth-choice.apply.plugin-provider.js";
|
||||
|
||||
export async function applyAuthChoiceYourProvider(
|
||||
params: ApplyAuthChoiceParams
|
||||
): Promise<ApplyAuthChoiceResult | null> {
|
||||
return await applyAuthChoicePluginProvider(params, {
|
||||
authChoice: "your-provider",
|
||||
pluginId: "your-provider-auth",
|
||||
providerId: "your-provider",
|
||||
methodId: "oauth",
|
||||
label: "Your Provider",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### 9. Export from Main Index
|
||||
|
||||
```typescript
|
||||
// src/commands/auth-choice.apply.ts
|
||||
import { applyAuthChoiceYourProvider } from "./auth-choice.apply.your-provider.js";
|
||||
|
||||
// In the switch statement:
|
||||
case "your-provider":
|
||||
return await applyAuthChoiceYourProvider(params);
|
||||
```
|
||||
|
||||
### Helper Utilities
|
||||
|
||||
#### PKCE Generation
|
||||
```typescript
|
||||
function generatePkce(): { verifier: string; challenge: string } {
|
||||
const verifier = randomBytes(32).toString("hex");
|
||||
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
||||
return { verifier, challenge };
|
||||
}
|
||||
```
|
||||
|
||||
#### Callback Server
|
||||
```typescript
|
||||
async function startCallbackServer(params: { timeoutMs: number }) {
|
||||
const port = 51121; // Your port
|
||||
|
||||
const server = createServer((request, response) => {
|
||||
const url = new URL(request.url!, `http://localhost:${port}`);
|
||||
|
||||
if (url.pathname === "/oauth-callback") {
|
||||
response.writeHead(200, { "Content-Type": "text/html" });
|
||||
response.end("<h1>Authentication complete</h1>");
|
||||
resolveCallback(url);
|
||||
server.close();
|
||||
func NewYourProvider(apiKey, apiBase, proxy string) *YourProvider {
|
||||
if apiBase == "" {
|
||||
apiBase = "https://api.your-provider.com/v1"
|
||||
}
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
server.listen(port, "127.0.0.1", resolve);
|
||||
server.once("error", reject);
|
||||
});
|
||||
|
||||
return {
|
||||
waitForCallback: () => callbackPromise,
|
||||
close: () => new Promise((resolve) => server.close(resolve)),
|
||||
};
|
||||
return &YourProvider{apiKey: apiKey, apiBase: apiBase}
|
||||
}
|
||||
|
||||
func (p *YourProvider) Chat(ctx context.Context, messages []Message, tools []Tool, cb StreamCallback) error {
|
||||
// Implement chat completion with streaming
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Register in the Factory
|
||||
|
||||
Add your provider to the protocol switch in `pkg/providers/factory.go`:
|
||||
|
||||
```go
|
||||
case "your-provider":
|
||||
return NewYourProvider(sel.apiKey, sel.apiBase, sel.proxy), nil
|
||||
```
|
||||
|
||||
#### 4. Add Default Config (Optional)
|
||||
|
||||
Add a default entry in `pkg/config/defaults.go`:
|
||||
|
||||
```go
|
||||
{
|
||||
ModelName: "your-model",
|
||||
Model: "your-provider/model-name",
|
||||
APIKey: "",
|
||||
},
|
||||
```
|
||||
|
||||
#### 5. Add Auth Support (Optional)
|
||||
|
||||
If your provider requires OAuth or special authentication, add a case to `cmd/picoclaw/cmd_auth.go`:
|
||||
|
||||
```go
|
||||
case "your-provider":
|
||||
authLoginYourProvider()
|
||||
```
|
||||
|
||||
#### 6. Configure via `config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"model_list": [
|
||||
{
|
||||
"model_name": "your-model",
|
||||
"model": "your-provider/model-name",
|
||||
"api_key": "your-api-key",
|
||||
"api_base": "https://api.your-provider.com/v1"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -892,33 +703,27 @@ async function startCallbackServer(params: { timeoutMs: number }) {
|
||||
### CLI Commands
|
||||
|
||||
```bash
|
||||
# Enable the plugin
|
||||
openclaw plugins enable your-provider-auth
|
||||
# Authenticate with a provider
|
||||
picoclaw auth login --provider your-provider
|
||||
|
||||
# Restart gateway
|
||||
openclaw gateway restart
|
||||
# List models (for Antigravity)
|
||||
picoclaw auth models
|
||||
|
||||
# Authenticate
|
||||
openclaw models auth login --provider your-provider --set-default
|
||||
# Start the gateway
|
||||
picoclaw gateway
|
||||
|
||||
# List models
|
||||
openclaw models list
|
||||
|
||||
# Set model
|
||||
openclaw models set your-provider/model-name
|
||||
|
||||
# Check usage
|
||||
openclaw models usage
|
||||
# Run an agent with a specific model
|
||||
picoclaw agent -m "Hello" --model your-model
|
||||
```
|
||||
|
||||
### Environment Variables for Testing
|
||||
|
||||
```bash
|
||||
# Test specific providers only
|
||||
export OPENCLAW_LIVE_PROVIDERS="your-provider,google-antigravity"
|
||||
# Override default model
|
||||
export PICOCLAW_AGENTS_DEFAULTS_MODEL=your-model
|
||||
|
||||
# Test with specific models
|
||||
export OPENCLAW_LIVE_GATEWAY_MODELS="your-provider/model-name"
|
||||
# Override provider settings
|
||||
export PICOCLAW_MODEL_LIST='[{"model_name":"your-model","model":"your-provider/model-name","api_key":"..."}]'
|
||||
```
|
||||
|
||||
---
|
||||
@@ -926,16 +731,16 @@ export OPENCLAW_LIVE_GATEWAY_MODELS="your-provider/model-name"
|
||||
## References
|
||||
|
||||
- **Source Files:**
|
||||
- `extensions/google-antigravity-auth/index.ts` - Full OAuth implementation
|
||||
- `src/infra/provider-usage.fetch.antigravity.ts` - Usage fetching
|
||||
- `src/agents/pi-embedded-runner/google.ts` - Model sanitization
|
||||
- `src/agents/model-forward-compat.ts` - Forward compatibility
|
||||
- `src/plugin-sdk/provider-auth-result.ts` - Auth result builder
|
||||
- `src/plugins/types.ts` - Plugin type definitions
|
||||
- `pkg/providers/antigravity_provider.go` - Antigravity provider implementation
|
||||
- `pkg/auth/oauth.go` - OAuth flow implementation
|
||||
- `pkg/auth/store.go` - Auth credential storage (`~/.picoclaw/auth.json`)
|
||||
- `pkg/providers/factory.go` - Provider factory and protocol routing
|
||||
- `pkg/providers/types.go` - Provider interface definitions
|
||||
- `cmd/picoclaw/cmd_auth.go` - Auth CLI commands
|
||||
|
||||
- **Documentation:**
|
||||
- `docs/concepts/model-providers.md` - Provider overview
|
||||
- `docs/concepts/usage-tracking.md` - Usage tracking
|
||||
- `docs/ANTIGRAVITY_USAGE.md` - Antigravity usage guide
|
||||
- `docs/migration/model-list-migration.md` - Migration guide
|
||||
|
||||
---
|
||||
|
||||
@@ -987,7 +792,7 @@ Some models might show up in the available models list but return an empty respo
|
||||
## Troubleshooting
|
||||
|
||||
### "Token expired"
|
||||
- Refresh OAuth tokens: `openclaw models auth login --provider google-antigravity`
|
||||
- Refresh OAuth tokens: `picoclaw auth login --provider antigravity`
|
||||
|
||||
### "Gemini for Google Cloud is not enabled"
|
||||
- Enable the API in your Google Cloud Console
|
||||
@@ -998,5 +803,5 @@ Some models might show up in the available models list but return an empty respo
|
||||
|
||||
### Models not appearing in list
|
||||
- Verify OAuth authentication completed successfully
|
||||
- Check auth profile storage: `~/.openclaw/agent/auth-profiles.json`
|
||||
- Ensure the plugin is enabled: `openclaw plugins list`
|
||||
- Check auth profile storage: `~/.picoclaw/auth.json`
|
||||
- Re-run `picoclaw auth login --provider antigravity`
|
||||
|
||||
@@ -47,14 +47,12 @@ picoclaw agent -m "Hello" --model claude-opus-4-6-thinking
|
||||
|
||||
If you are deploying via Coolify or Docker, follow these steps to test:
|
||||
|
||||
1. **Branch**: Use the `feat/antigravity-provider` branch.
|
||||
2. **Environment Variables**:
|
||||
* `PICOCLAW_AGENTS_DEFAULTS_PROVIDER=antigravity`
|
||||
* `PICOCLAW_AGENTS_DEFAULTS_MODEL=gemini-3-flash`
|
||||
3. **Authentication persistence**:
|
||||
1. **Environment Variables**:
|
||||
* `PICOCLAW_AGENTS_DEFAULTS_MODEL=gemini-flash`
|
||||
2. **Authentication persistence**:
|
||||
If you've logged in locally, you can copy your credentials to the server:
|
||||
```bash
|
||||
scp ~/.picoclaw/auth-profiles.json user@your-server:~/.picoclaw/
|
||||
scp ~/.picoclaw/auth.json user@your-server:~/.picoclaw/
|
||||
```
|
||||
*Alternatively*, run the `auth login` command once on the server if you have terminal access.
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Provider Architecture Refactoring - Test Suite Summary
|
||||
|
||||
> PRD: `tasks/prd-provider-refactoring.md`
|
||||
|
||||
This document summarizes the complete test suite designed for the Provider architecture refactoring.
|
||||
|
||||
## Test File Structure
|
||||
@@ -12,10 +10,8 @@ pkg/
|
||||
│ ├── model_config_test.go # US-001, US-002: ModelConfig struct and GetModelConfig tests
|
||||
│ └── migration_test.go # US-003: Backward compatibility and migration tests
|
||||
├── providers/
|
||||
│ ├── registry_test.go # US-006: Load balancing tests
|
||||
│ ├── integration_test.go # E2E integration tests
|
||||
│ └── factory/
|
||||
│ └── factory_test.go # US-004, US-005: Provider factory tests
|
||||
│ ├── factory_test.go # US-004, US-005: Provider factory tests
|
||||
│ └── factory_provider_test.go # Factory provider integration tests
|
||||
```
|
||||
|
||||
---
|
||||
@@ -122,7 +118,6 @@ go test ./pkg/... -race
|
||||
# Run specific package tests
|
||||
go test ./pkg/config -v
|
||||
go test ./pkg/providers -v
|
||||
go test ./pkg/providers/factory -v
|
||||
|
||||
# Run E2E tests
|
||||
go test ./pkg/providers -run TestE2E -v
|
||||
|
||||
@@ -85,6 +85,7 @@ The `model` field uses a protocol prefix format: `[protocol/]model-identifier`
|
||||
| `openai/` | OpenAI API (default) | `openai/gpt-5.2` |
|
||||
| `anthropic/` | Anthropic API | `anthropic/claude-opus-4` |
|
||||
| `antigravity/` | Google via Antigravity OAuth | `antigravity/gemini-2.0-flash` |
|
||||
| `gemini/` | Google Gemini API | `gemini/gemini-2.0-flash-exp` |
|
||||
| `claude-cli/` | Claude CLI (local) | `claude-cli/claude-sonnet-4.6` |
|
||||
| `codex-cli/` | Codex CLI (local) | `codex-cli/codex-4` |
|
||||
| `github-copilot/` | GitHub Copilot | `github-copilot/gpt-4o` |
|
||||
@@ -93,6 +94,13 @@ The `model` field uses a protocol prefix format: `[protocol/]model-identifier`
|
||||
| `deepseek/` | DeepSeek API | `deepseek/deepseek-chat` |
|
||||
| `cerebras/` | Cerebras API | `cerebras/llama-3.3-70b` |
|
||||
| `qwen/` | Alibaba Qwen | `qwen/qwen-max` |
|
||||
| `zhipu/` | Zhipu AI | `zhipu/glm-4` |
|
||||
| `nvidia/` | NVIDIA NIM | `nvidia/llama-3.1-nemotron-70b` |
|
||||
| `ollama/` | Ollama (local) | `ollama/llama3` |
|
||||
| `vllm/` | vLLM (local) | `vllm/my-model` |
|
||||
| `moonshot/` | Moonshot AI | `moonshot/moonshot-v1-8k` |
|
||||
| `shengsuanyun/` | ShengSuanYun | `shengsuanyun/deepseek-v3` |
|
||||
| `volcengine/` | Volcengine | `volcengine/doubao-pro-32k` |
|
||||
|
||||
**Note**: If no prefix is specified, `openai/` is used as the default.
|
||||
|
||||
|
||||
@@ -71,14 +71,14 @@ Interested in a specific feature? You can "claim" these tasks and start building
|
||||
* Support for OneBot, additional platforms
|
||||
* attachments (images, audio, video, files).
|
||||
* **Skills:**
|
||||
* Implementing `find_skill` to discover tools via [openclaw/skills](https://github.com/openclaw/skills) and other platforms.
|
||||
* Implementing `find_skill` to discover tools via [ClawhHub](https://clawhub.ai) and other platforms.
|
||||
* **Operations:** * MCP Support.
|
||||
* Android operations (e.g., botdrop).
|
||||
* Browser automation via CDP or ActionBook.
|
||||
|
||||
|
||||
* **Multi-Agent Ecosystem:**
|
||||
* **Basic Model-Agnet** S
|
||||
* **Basic Model-Agent**
|
||||
* **Model Routing:** Small models for easy tasks, large models for hard ones (to save tokens).
|
||||
* **Swarm Mode.**
|
||||
* **AIEOS Integration.**
|
||||
|
||||
+37
-16
@@ -9,8 +9,8 @@ PicoClaw's tools configuration is located in the `tools` field of `config.json`.
|
||||
"tools": {
|
||||
"web": { ... },
|
||||
"exec": { ... },
|
||||
"approval": { ... },
|
||||
"cron": { ... }
|
||||
"cron": { ... },
|
||||
"skills": { ... }
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -83,25 +83,12 @@ By default, PicoClaw blocks the following dangerous commands:
|
||||
"custom_deny_patterns": [
|
||||
"\\brm\\s+-r\\b",
|
||||
"\\bkillall\\s+python"
|
||||
],
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Approval Tool
|
||||
|
||||
The approval tool controls permissions for dangerous operations.
|
||||
|
||||
| Config | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `enabled` | bool | true | Enable approval functionality |
|
||||
| `write_file` | bool | true | Require approval for file writes |
|
||||
| `edit_file` | bool | true | Require approval for file edits |
|
||||
| `append_file` | bool | true | Require approval for file appends |
|
||||
| `exec` | bool | true | Require approval for command execution |
|
||||
| `timeout_minutes` | int | 5 | Approval timeout in minutes |
|
||||
|
||||
## Cron Tool
|
||||
|
||||
The cron tool is used for scheduling periodic tasks.
|
||||
@@ -110,6 +97,40 @@ The cron tool is used for scheduling periodic tasks.
|
||||
|--------|------|---------|-------------|
|
||||
| `exec_timeout_minutes` | int | 5 | Execution timeout in minutes, 0 means no limit |
|
||||
|
||||
## Skills Tool
|
||||
|
||||
The skills tool configures skill discovery and installation via registries like ClawHub.
|
||||
|
||||
### Registries
|
||||
|
||||
| Config | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `registries.clawhub.enabled` | bool | true | Enable ClawHub registry |
|
||||
| `registries.clawhub.base_url` | string | `https://clawhub.ai` | ClawHub base URL |
|
||||
| `registries.clawhub.search_path` | string | `/api/v1/search` | Search API path |
|
||||
| `registries.clawhub.skills_path` | string | `/api/v1/skills` | Skills API path |
|
||||
| `registries.clawhub.download_path` | string | `/api/v1/download` | Download API path |
|
||||
|
||||
### Configuration Example
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"skills": {
|
||||
"registries": {
|
||||
"clawhub": {
|
||||
"enabled": true,
|
||||
"base_url": "https://clawhub.ai",
|
||||
"search_path": "/api/v1/search",
|
||||
"skills_path": "/api/v1/skills",
|
||||
"download_path": "/api/v1/download"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
All configuration options can be overridden via environment variables with the format `PICOCLAW_TOOLS_<SECTION>_<KEY>`:
|
||||
|
||||
Reference in New Issue
Block a user