@honor-claw/yoyo 0.0.1-beta.5 → 0.0.1-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +3 -7
- package/openclaw.plugin.json +4 -0
- package/package.json +1 -2
- package/skills/search/SKILL.md +182 -0
- package/skills/search/scripts/search.sh +69 -0
- package/src/cloud-channel/client.ts +8 -3
- package/src/commands/env/impl.ts +38 -0
- package/src/commands/env/index.ts +1 -0
- package/src/commands/index.ts +7 -1
- package/src/commands/login/impl.ts +6 -4
- package/src/honor-auth/config.ts +25 -17
- package/src/modules/claw-configs/config-manager.ts +51 -0
- package/src/modules/claw-configs/types.ts +2 -0
- package/src/modules/login/impl.ts +22 -2
- package/src/runtime.ts +20 -0
- package/src/schemas.ts +3 -2
- package/src/services/connection/impl.ts +7 -3
- package/src/utils/version.ts +21 -0
package/index.ts
CHANGED
|
@@ -2,24 +2,20 @@ import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
|
2
2
|
import { setYoyoRuntime } from "./src/runtime.js";
|
|
3
3
|
import { registerCommands } from "./src/commands/index.js";
|
|
4
4
|
import { YoyoPluginConfigSchema } from "./src/schemas.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createClawConnectionService } from "./src/services/connection/index.js";
|
|
6
6
|
import { setClawLogger } from "./src/utils/logger.js";
|
|
7
|
-
import { copyTemplateToWorkspace } from "./src/agent/index.js";
|
|
8
7
|
|
|
9
8
|
const plugin = {
|
|
10
9
|
id: "yoyo",
|
|
11
10
|
name: "YOYOClaw",
|
|
12
11
|
description: "OpenClaw Honor Yoyo connection plugin",
|
|
13
12
|
configSchema: YoyoPluginConfigSchema,
|
|
14
|
-
|
|
13
|
+
register(api: OpenClawPluginApi) {
|
|
15
14
|
setYoyoRuntime(api.runtime);
|
|
16
15
|
setClawLogger(api.logger);
|
|
17
16
|
|
|
18
|
-
// 复制智能体模板到工作目录
|
|
19
|
-
await copyTemplateToWorkspace(api)
|
|
20
|
-
|
|
21
17
|
// 利用服务来管理核心连接任务进行~
|
|
22
|
-
api.registerService(
|
|
18
|
+
api.registerService(createClawConnectionService(api));
|
|
23
19
|
|
|
24
20
|
// 注册所有的命令行
|
|
25
21
|
registerCommands(api);
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honor-claw/yoyo",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw Honor Yoyo connection plugin",
|
|
6
6
|
"scripts": {
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
]
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@sinclair/typebox": "0.34.48",
|
|
34
33
|
"http-proxy-agent": "^8.0.0",
|
|
35
34
|
"https-proxy-agent": "^8.0.0",
|
|
36
35
|
"jsonwebtoken": "^9.0.3",
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: search
|
|
3
|
+
description: "Search the web using Tavily's LLM-optimized search API. Returns relevant results with content snippets, scores, and metadata. Use when you need to find web content on any topic without writing code."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Search Skill
|
|
7
|
+
|
|
8
|
+
Search the web and get relevant results optimized for LLM consumption.
|
|
9
|
+
|
|
10
|
+
## Authentication
|
|
11
|
+
|
|
12
|
+
The script uses OAuth via the Tavily MCP server. **No manual setup required** - on first run, it will:
|
|
13
|
+
1. Check for existing tokens in `~/.mcp-auth/`
|
|
14
|
+
2. If none found, automatically open your browser for OAuth authentication
|
|
15
|
+
|
|
16
|
+
> **Note:** You must have an existing Tavily account. The OAuth flow only supports login — account creation is not available through this flow. [Sign up at tavily.com](https://tavily.com) first if you don't have an account.
|
|
17
|
+
|
|
18
|
+
### Alternative: API Key
|
|
19
|
+
|
|
20
|
+
If you prefer using an API key, get one at https://tavily.com and add to `~/.claude/settings.json`:
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"env": {
|
|
24
|
+
"TAVILY_API_KEY": "tvly-your-api-key-here"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Using the Script
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
./scripts/search.sh '<json>'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Examples:**
|
|
38
|
+
```bash
|
|
39
|
+
# Basic search
|
|
40
|
+
./scripts/search.sh '{"query": "python async patterns"}'
|
|
41
|
+
|
|
42
|
+
# With options
|
|
43
|
+
./scripts/search.sh '{"query": "React hooks tutorial", "max_results": 10}'
|
|
44
|
+
|
|
45
|
+
# Advanced search with filters
|
|
46
|
+
./scripts/search.sh '{"query": "AI news", "time_range": "week", "max_results": 10}'
|
|
47
|
+
|
|
48
|
+
# Domain-filtered search
|
|
49
|
+
./scripts/search.sh '{"query": "machine learning", "include_domains": ["arxiv.org", "github.com"], "search_depth": "advanced"}'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Basic Search
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
curl --request POST \
|
|
56
|
+
--url https://api.tavily.com/search \
|
|
57
|
+
--header "Authorization: Bearer $TAVILY_API_KEY" \
|
|
58
|
+
--header 'Content-Type: application/json' \
|
|
59
|
+
--data '{
|
|
60
|
+
"query": "latest developments in quantum computing",
|
|
61
|
+
"max_results": 5
|
|
62
|
+
}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Advanced Search
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl --request POST \
|
|
69
|
+
--url https://api.tavily.com/search \
|
|
70
|
+
--header "Authorization: Bearer $TAVILY_API_KEY" \
|
|
71
|
+
--header 'Content-Type: application/json' \
|
|
72
|
+
--data '{
|
|
73
|
+
"query": "machine learning best practices",
|
|
74
|
+
"max_results": 10,
|
|
75
|
+
"search_depth": "advanced",
|
|
76
|
+
"include_domains": ["arxiv.org", "github.com"]
|
|
77
|
+
}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### Endpoint
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
POST https://api.tavily.com/search
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Headers
|
|
89
|
+
|
|
90
|
+
| Header | Value |
|
|
91
|
+
|--------|-------|
|
|
92
|
+
| `Authorization` | `Bearer <TAVILY_API_KEY>` |
|
|
93
|
+
| `Content-Type` | `application/json` |
|
|
94
|
+
|
|
95
|
+
### Request Body
|
|
96
|
+
|
|
97
|
+
| Field | Type | Default | Description |
|
|
98
|
+
|-------|------|---------|-------------|
|
|
99
|
+
| `query` | string | Required | Search query (keep under 400 chars) |
|
|
100
|
+
| `max_results` | integer | 10 | Maximum results (0-20) |
|
|
101
|
+
| `search_depth` | string | `"basic"` | `ultra-fast`, `fast`, `basic`, `advanced` |
|
|
102
|
+
| `topic` | string | `"general"` | Search topic (general only) |
|
|
103
|
+
| `time_range` | string | null | `day`, `week`, `month`, `year` |
|
|
104
|
+
| `start_date` | string | null | Return results after this date (`YYYY-MM-DD`) |
|
|
105
|
+
| `end_date` | string | null | Return results before this date (`YYYY-MM-DD`) |
|
|
106
|
+
| `include_domains` | array | [] | Domains to include (max 300) |
|
|
107
|
+
| `exclude_domains` | array | [] | Domains to exclude (max 150) |
|
|
108
|
+
| `country` | string | null | Boost results from a specific country (general topic only) |
|
|
109
|
+
| `include_raw_content` | boolean | false | Include full page content |
|
|
110
|
+
| `include_images` | boolean | false | Include image results |
|
|
111
|
+
| `include_image_descriptions` | boolean | false | Include descriptions for images |
|
|
112
|
+
| `include_favicon` | boolean | false | Include favicon URL for each result |
|
|
113
|
+
|
|
114
|
+
### Response Format
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"query": "latest developments in quantum computing",
|
|
119
|
+
"results": [
|
|
120
|
+
{
|
|
121
|
+
"title": "Page Title",
|
|
122
|
+
"url": "https://example.com/page",
|
|
123
|
+
"content": "Extracted text snippet...",
|
|
124
|
+
"score": 0.85
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"response_time": 1.2
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Search Depth
|
|
132
|
+
|
|
133
|
+
| Depth | Latency | Relevance | Content Type |
|
|
134
|
+
|-------|---------|-----------|--------------|
|
|
135
|
+
| `ultra-fast` | Lowest | Lower | NLP summary |
|
|
136
|
+
| `fast` | Low | Good | Chunks |
|
|
137
|
+
| `basic` | Medium | High | NLP summary |
|
|
138
|
+
| `advanced` | Higher | Highest | Chunks |
|
|
139
|
+
|
|
140
|
+
**When to use each:**
|
|
141
|
+
- `ultra-fast`: Real-time chat, autocomplete
|
|
142
|
+
- `fast`: Need chunks but latency matters
|
|
143
|
+
- `basic`: General-purpose, balanced
|
|
144
|
+
- `advanced`: Precision matters (default recommendation)
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
### Domain-Filtered Search
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
curl --request POST \
|
|
152
|
+
--url https://api.tavily.com/search \
|
|
153
|
+
--header "Authorization: Bearer $TAVILY_API_KEY" \
|
|
154
|
+
--header 'Content-Type: application/json' \
|
|
155
|
+
--data '{
|
|
156
|
+
"query": "Python async best practices",
|
|
157
|
+
"include_domains": ["docs.python.org", "realpython.com", "github.com"],
|
|
158
|
+
"search_depth": "advanced"
|
|
159
|
+
}'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Search with Full Content
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
curl --request POST \
|
|
166
|
+
--url https://api.tavily.com/search \
|
|
167
|
+
--header "Authorization: Bearer $TAVILY_API_KEY" \
|
|
168
|
+
--header 'Content-Type: application/json' \
|
|
169
|
+
--data '{
|
|
170
|
+
"query": "React hooks tutorial",
|
|
171
|
+
"max_results": 3,
|
|
172
|
+
"include_raw_content": true
|
|
173
|
+
}'
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Tips
|
|
177
|
+
|
|
178
|
+
- **Keep queries under 400 characters** - Think search query, not prompt
|
|
179
|
+
- **Break complex queries into sub-queries** - Better results than one massive query
|
|
180
|
+
- **Use `include_domains`** to focus on trusted sources
|
|
181
|
+
- **Use `time_range`** for recent information
|
|
182
|
+
- **Filter by `score`** (0-1) to get highest relevance results
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tavily Search API script
|
|
3
|
+
# Usage: ./search.sh '{"query": "your search query", ...}'
|
|
4
|
+
# Example: ./search.sh '{"query": "AI news", "time_range": "week", "max_results": 10}'
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# Check for TAVILY_API_KEY environment variable
|
|
9
|
+
if [ -z "$TAVILY_API_KEY" ]; then
|
|
10
|
+
echo "Error: TAVILY_API_KEY environment variable is required" >&2
|
|
11
|
+
echo "Please set it: export TAVILY_API_KEY='your-api-key'" >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
JSON_INPUT="$1"
|
|
16
|
+
|
|
17
|
+
if [ -z "$JSON_INPUT" ]; then
|
|
18
|
+
echo "Usage: ./search.sh '<json>'"
|
|
19
|
+
echo ""
|
|
20
|
+
echo "Required:"
|
|
21
|
+
echo " query: string - Search query (keep under 400 chars)"
|
|
22
|
+
echo ""
|
|
23
|
+
echo "Optional:"
|
|
24
|
+
echo " search_depth: \"ultra-fast\", \"fast\", \"basic\" (default), \"advanced\""
|
|
25
|
+
echo " topic: \"general\" (default)"
|
|
26
|
+
echo " max_results: 1-20 (default: 10)"
|
|
27
|
+
echo " time_range: \"day\", \"week\", \"month\", \"year\""
|
|
28
|
+
echo " start_date: \"YYYY-MM-DD\""
|
|
29
|
+
echo " end_date: \"YYYY-MM-DD\""
|
|
30
|
+
echo " include_domains: [\"domain1.com\", \"domain2.com\"]"
|
|
31
|
+
echo " exclude_domains: [\"domain1.com\", \"domain2.com\"]"
|
|
32
|
+
echo " country: country name (general topic only)"
|
|
33
|
+
echo " include_raw_content: true/false"
|
|
34
|
+
echo " include_images: true/false"
|
|
35
|
+
echo " include_image_descriptions: true/false"
|
|
36
|
+
echo " include_favicon: true/false"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Example:"
|
|
39
|
+
echo " ./search.sh '{\"query\": \"latest AI trends\", \"time_range\": \"week\"}'"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
MCP_REQUEST='{
|
|
44
|
+
"jsonrpc": "2.0",
|
|
45
|
+
"id": 1,
|
|
46
|
+
"method": "tools/call",
|
|
47
|
+
"params": {
|
|
48
|
+
"name": "tavily_search",
|
|
49
|
+
"arguments": '"$JSON_INPUT"'
|
|
50
|
+
}
|
|
51
|
+
}'
|
|
52
|
+
|
|
53
|
+
# Call Tavily MCP server via HTTPS
|
|
54
|
+
RESPONSE=$(curl -s --request POST \
|
|
55
|
+
--url "https://mcp.tavily.com/mcp" \
|
|
56
|
+
--header "Authorization: Bearer $TAVILY_API_KEY" \
|
|
57
|
+
--header 'Content-Type: application/json' \
|
|
58
|
+
--header 'Accept: application/json, text/event-stream' \
|
|
59
|
+
--header 'x-client-source: claude-code-skill' \
|
|
60
|
+
--data "$MCP_REQUEST")
|
|
61
|
+
|
|
62
|
+
# Parse SSE response and extract the JSON result
|
|
63
|
+
JSON_DATA=$(echo "$RESPONSE" | grep '^data:' | sed 's/^data://' | head -1)
|
|
64
|
+
|
|
65
|
+
if [ -n "$JSON_DATA" ]; then
|
|
66
|
+
echo "$JSON_DATA"
|
|
67
|
+
else
|
|
68
|
+
echo "$RESPONSE"
|
|
69
|
+
fi
|
|
@@ -138,7 +138,13 @@ export class ClawCloudSocketClient {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
try {
|
|
141
|
-
|
|
141
|
+
const msgText = JSON.stringify(message);
|
|
142
|
+
|
|
143
|
+
useClawLogger().debug?.(
|
|
144
|
+
`[yoyoclaw-cloud] send message to cloud:, ${msgText.slice(0, 500)}`
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
this.ws.send(msgText);
|
|
142
148
|
return true;
|
|
143
149
|
} catch (error) {
|
|
144
150
|
useClawLogger().error(
|
|
@@ -279,8 +285,7 @@ export class ClawCloudSocketClient {
|
|
|
279
285
|
*/
|
|
280
286
|
private calculateRetryDelay(): number {
|
|
281
287
|
const delay =
|
|
282
|
-
START_RETRY_DELAY *
|
|
283
|
-
Math.pow(RETRY_BACKOFF_FACTOR, this.retryCount - 1);
|
|
288
|
+
START_RETRY_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, this.retryCount - 1);
|
|
284
289
|
return Math.min(delay, MAX_RETRY_DELAY);
|
|
285
290
|
}
|
|
286
291
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getConfigManager } from "../../modules/claw-configs/config-manager.js";
|
|
2
|
+
import { useClawLogger } from "../../utils/logger.js";
|
|
3
|
+
import type { Command } from "commander";
|
|
4
|
+
|
|
5
|
+
export function registerEnvCommand(_: unknown, command: Command) {
|
|
6
|
+
const nextCommand = command
|
|
7
|
+
.command("env")
|
|
8
|
+
.description("Manage runtime environment (test/production)")
|
|
9
|
+
.option("--set <env>", "Set environment: test or production")
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
const { set: setEnv } = options;
|
|
12
|
+
const logger = useClawLogger();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const configManager = getConfigManager();
|
|
16
|
+
|
|
17
|
+
if (setEnv) {
|
|
18
|
+
// 设置环境
|
|
19
|
+
if (setEnv !== "test" && setEnv !== "production") {
|
|
20
|
+
logger.error("❌ Invalid environment. Use 'test' or 'production'.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await configManager.updateEnv(setEnv);
|
|
25
|
+
logger.info(`✅ Environment set to: ${setEnv}`);
|
|
26
|
+
} else {
|
|
27
|
+
// 获取当前环境
|
|
28
|
+
const currentEnv = configManager.getEnv();
|
|
29
|
+
logger.info(`📋 Current environment: ${currentEnv}`);
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
33
|
+
logger.error(`❌ Failed to manage environment: ${errorMessage}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return nextCommand;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './impl.js';
|
package/src/commands/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
2
|
import { registerLoginCommand } from "./login/index.js";
|
|
3
3
|
import { registerStatusCommand } from "./status/index.js";
|
|
4
|
-
|
|
4
|
+
import { registerEnvCommand } from "./env/index.js";
|
|
5
|
+
import { isBetaVersion } from "../utils/version.js";
|
|
5
6
|
|
|
6
7
|
export function registerCommands(api: OpenClawPluginApi) {
|
|
7
8
|
api.registerCli(
|
|
@@ -15,6 +16,11 @@ export function registerCommands(api: OpenClawPluginApi) {
|
|
|
15
16
|
// @ts-ignore
|
|
16
17
|
registerStatusCommand(api, rootCommand);
|
|
17
18
|
// registerLogoutCommand(api, rootCommand);
|
|
19
|
+
|
|
20
|
+
// 只在 beta 版本时注册 env 命令
|
|
21
|
+
if (isBetaVersion()) {
|
|
22
|
+
registerEnvCommand(api, rootCommand);
|
|
23
|
+
}
|
|
18
24
|
},
|
|
19
25
|
{ commands: ["honor"] }
|
|
20
26
|
);
|
|
@@ -5,16 +5,18 @@ import { type Command } from "commander";
|
|
|
5
5
|
export function registerLoginCommand(api: OpenClawPluginApi, command: Command) {
|
|
6
6
|
const nextCommand = command
|
|
7
7
|
.command("login")
|
|
8
|
-
.description("
|
|
8
|
+
.description("login to yoyoclaw and register devices")
|
|
9
9
|
.option("--skip-auth", "debug mode, no auth required")
|
|
10
|
+
.option("-u, --userId <userId>", "user ID for direct login")
|
|
11
|
+
.option("--token <token>", "token for direct login")
|
|
10
12
|
.action(async (options) => {
|
|
11
|
-
const { skipAuth } = options;
|
|
13
|
+
const { skipAuth, userId, token } = options;
|
|
12
14
|
|
|
13
15
|
api.logger.info(
|
|
14
|
-
`honor login CLI command called with skip auth: ${!!skipAuth}`
|
|
16
|
+
`honor login CLI command called with skip auth: ${!!skipAuth}, userId: ${userId || 'none'}`
|
|
15
17
|
);
|
|
16
18
|
|
|
17
|
-
await performLogin({ noAuth: skipAuth });
|
|
19
|
+
await performLogin({ noAuth: skipAuth, userId, token });
|
|
18
20
|
});
|
|
19
21
|
|
|
20
22
|
return nextCommand;
|
package/src/honor-auth/config.ts
CHANGED
|
@@ -2,34 +2,42 @@
|
|
|
2
2
|
* 荣耀认证配置管理
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { HonorAuthConfig } from
|
|
5
|
+
import type { HonorAuthConfig } from "./types.js";
|
|
6
|
+
import { getYoyoEnv } from "../runtime.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* 默认配置
|
|
9
10
|
*/
|
|
10
|
-
const
|
|
11
|
-
authHost:
|
|
12
|
-
clientId:
|
|
13
|
-
redirectUri:
|
|
11
|
+
const TEST_ENV_CONFIG: HonorAuthConfig = {
|
|
12
|
+
authHost: "https://hnoauth-login-test-drcn.cloud.honor.com",
|
|
13
|
+
clientId: "221641491",
|
|
14
|
+
redirectUri: "http://127.0.0.1:8081/deepLink",
|
|
14
15
|
localPort: 8081,
|
|
15
|
-
scope:
|
|
16
|
-
reqClientType:
|
|
17
|
-
loginChannel:
|
|
16
|
+
scope: "openid profile",
|
|
17
|
+
reqClientType: "110",
|
|
18
|
+
loginChannel: "99011000",
|
|
19
|
+
};
|
|
20
|
+
const PRD_ENV_CONFIG: HonorAuthConfig = {
|
|
21
|
+
authHost: "https://hnoauth-login-drcn.cloud.honor.com",
|
|
22
|
+
clientId: "221641491",
|
|
23
|
+
redirectUri: "http://127.0.0.1:8081/deepLink",
|
|
24
|
+
localPort: 8081,
|
|
25
|
+
scope: "openid profile",
|
|
26
|
+
reqClientType: "110",
|
|
27
|
+
loginChannel: "99011000",
|
|
18
28
|
};
|
|
19
29
|
|
|
20
30
|
/**
|
|
21
31
|
* 获取配置(支持自定义覆盖)
|
|
32
|
+
* 根据当前运行环境自动选择对应的配置
|
|
22
33
|
*/
|
|
23
|
-
export function getConfig(
|
|
34
|
+
export function getConfig(
|
|
35
|
+
overrides?: Partial<HonorAuthConfig>
|
|
36
|
+
): HonorAuthConfig {
|
|
37
|
+
const env = getYoyoEnv();
|
|
38
|
+
const baseConfig = env === 'production' ? PRD_ENV_CONFIG : TEST_ENV_CONFIG;
|
|
24
39
|
return {
|
|
25
|
-
...
|
|
40
|
+
...baseConfig,
|
|
26
41
|
...overrides,
|
|
27
42
|
};
|
|
28
43
|
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 获取默认配置
|
|
32
|
-
*/
|
|
33
|
-
export function getDefaultConfig(): HonorAuthConfig {
|
|
34
|
-
return { ...DEFAULT_CONFIG };
|
|
35
|
-
}
|
|
@@ -109,6 +109,57 @@ export class ConfigManager {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* 获取运行环境配置
|
|
114
|
+
*/
|
|
115
|
+
getEnv(): 'test' | 'production' {
|
|
116
|
+
try {
|
|
117
|
+
const config = this.loadConfig();
|
|
118
|
+
const env = config.plugins?.entries?.[PLUGIN_YOYO_ID]?.config
|
|
119
|
+
?.env as 'test' | 'production' | undefined;
|
|
120
|
+
return env || 'test';
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(`[claw-configs] Failed to read env config: ${error}`);
|
|
123
|
+
return 'test';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 更新运行环境配置
|
|
129
|
+
*/
|
|
130
|
+
async updateEnv(env: 'test' | 'production'): Promise<void> {
|
|
131
|
+
try {
|
|
132
|
+
const currentConfig = this.loadConfig();
|
|
133
|
+
const currentUserConfig = this.getUserConfig() || {};
|
|
134
|
+
|
|
135
|
+
const updatedConfig: OpenClawConfig = {
|
|
136
|
+
...currentConfig,
|
|
137
|
+
plugins: {
|
|
138
|
+
...currentConfig.plugins,
|
|
139
|
+
entries: {
|
|
140
|
+
...currentConfig.plugins?.entries,
|
|
141
|
+
[PLUGIN_YOYO_ID]: {
|
|
142
|
+
...currentConfig.plugins?.entries?.[PLUGIN_YOYO_ID],
|
|
143
|
+
enabled: true,
|
|
144
|
+
config: {
|
|
145
|
+
user: currentUserConfig,
|
|
146
|
+
env,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
await this.saveConfig(updatedConfig);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Failed to update env config: ${
|
|
157
|
+
error instanceof Error ? error.message : String(error)
|
|
158
|
+
}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
112
163
|
/**
|
|
113
164
|
* 更新用户配置
|
|
114
165
|
*/
|
|
@@ -12,6 +12,10 @@ import { saveToken } from "../../honor-auth/token-manager.js";
|
|
|
12
12
|
export interface LoginOptions {
|
|
13
13
|
/** 是否跳过认证流程,默认为 false */
|
|
14
14
|
noAuth?: boolean;
|
|
15
|
+
/** 用户ID,如果提供则直接使用此ID登录 */
|
|
16
|
+
userId?: string;
|
|
17
|
+
/** Token,如果提供则直接使用此Token登录 */
|
|
18
|
+
token?: string;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
/**
|
|
@@ -23,7 +27,7 @@ export interface LoginOptions {
|
|
|
23
27
|
* @param options 登录选项
|
|
24
28
|
*/
|
|
25
29
|
export async function performLogin(options: LoginOptions = {}) {
|
|
26
|
-
const { noAuth = false } = options;
|
|
30
|
+
const { noAuth = false, userId, token } = options;
|
|
27
31
|
try {
|
|
28
32
|
console.log("🚀 开始登录流程...");
|
|
29
33
|
|
|
@@ -31,7 +35,23 @@ export async function performLogin(options: LoginOptions = {}) {
|
|
|
31
35
|
const deviceInfo = getDeviceInfo();
|
|
32
36
|
|
|
33
37
|
let userInfo: HonorUserInfo;
|
|
34
|
-
|
|
38
|
+
// 检查是否提供了 userId 和 token 参数
|
|
39
|
+
if (userId && token) {
|
|
40
|
+
console.log("🔓 使用提供的 userId 和 token 直接登录...");
|
|
41
|
+
userInfo = {
|
|
42
|
+
userId,
|
|
43
|
+
token,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// 保存Token,displayName 使用空字符串
|
|
47
|
+
await saveToken({
|
|
48
|
+
token,
|
|
49
|
+
userInfo: { userId, displayName: "" },
|
|
50
|
+
});
|
|
51
|
+
console.log("✅ 使用提供的用户信息");
|
|
52
|
+
console.log(`👤 用户: ${userInfo.userId}`);
|
|
53
|
+
console.log(`📱 设备ID: ${deviceInfo?.deviceId}`);
|
|
54
|
+
} else if (noAuth) {
|
|
35
55
|
console.log("🔓 跳过 OAuth2 认证,使用测试用户信息...");
|
|
36
56
|
userInfo = {
|
|
37
57
|
userId: "test",
|
package/src/runtime.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
+
import { getConfigManager } from "./modules/claw-configs/config-manager.js";
|
|
2
3
|
|
|
3
4
|
let runtime: PluginRuntime | null = null;
|
|
4
5
|
|
|
@@ -12,3 +13,22 @@ export function getYoyoRuntime(): PluginRuntime {
|
|
|
12
13
|
}
|
|
13
14
|
return runtime;
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Yoyo 环境类型
|
|
19
|
+
*/
|
|
20
|
+
export type YoyoEnv = "test" | "production";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 获取当前运行环境
|
|
24
|
+
* 从配置文件动态读取环境信息
|
|
25
|
+
*/
|
|
26
|
+
export function getYoyoEnv(): YoyoEnv {
|
|
27
|
+
try {
|
|
28
|
+
const configManager = getConfigManager();
|
|
29
|
+
return configManager.getEnv();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`[runtime] Failed to get env from config: ${error}`);
|
|
32
|
+
return "test"; // 默认返回测试环境
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/schemas.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 用户信息配置
|
|
@@ -16,5 +16,6 @@ const UserInfoSchema = z.object({
|
|
|
16
16
|
*/
|
|
17
17
|
export const YoyoPluginConfigSchema = z.object({
|
|
18
18
|
user: UserInfoSchema.optional(),
|
|
19
|
+
/** 运行环境:test 或 production */
|
|
20
|
+
env: z.enum(['test', 'production']).optional(),
|
|
19
21
|
});
|
|
20
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OpenClawPluginService } from "openclaw/plugin-sdk";
|
|
1
|
+
import { OpenClawPluginApi, OpenClawPluginService } from "openclaw/plugin-sdk";
|
|
2
2
|
import { ClawChannel } from "../../cloud-channel/channel.js";
|
|
3
3
|
import { useClawLogger } from "../../utils/logger.js";
|
|
4
4
|
import { ClawConnection, type ConnectionStatus } from "./types.js";
|
|
@@ -6,6 +6,7 @@ import { loadToken, clearToken } from "../../honor-auth/token-manager.js";
|
|
|
6
6
|
import { getDeviceInfo, registerDevice } from "../../modules/device/index.js";
|
|
7
7
|
import { DeviceInfo, HonorUserInfo } from "../../types.js";
|
|
8
8
|
import { getConfigManager } from "../../modules/claw-configs/config-manager.js";
|
|
9
|
+
import { copyTemplateToWorkspace } from "../../agent/copy-templates.js";
|
|
9
10
|
|
|
10
11
|
const clawConnection: ClawConnection = {
|
|
11
12
|
channel: null,
|
|
@@ -20,7 +21,7 @@ let pollInterval = 5000; // 初始轮询间隔 5 秒
|
|
|
20
21
|
*
|
|
21
22
|
* 维护一个全局的channel,配合各个命令行、服务时机调用
|
|
22
23
|
*/
|
|
23
|
-
export const
|
|
24
|
+
export const createClawConnectionService= (api: OpenClawPluginApi) => ({
|
|
24
25
|
id: "yoyoclaw-connection",
|
|
25
26
|
async start() {
|
|
26
27
|
useClawLogger().info("[yoyoclaw] plugin service enabled");
|
|
@@ -28,6 +29,9 @@ export const ClawConnectionService: OpenClawPluginService = {
|
|
|
28
29
|
// 启动轮询机制
|
|
29
30
|
startPolling();
|
|
30
31
|
|
|
32
|
+
// 复制智能体模板到工作目录
|
|
33
|
+
copyTemplateToWorkspace(api);
|
|
34
|
+
|
|
31
35
|
// 修改配置文件,配置`plugins.allow`和`gateway.nodes.allowCommands`
|
|
32
36
|
try {
|
|
33
37
|
const configManager = getConfigManager();
|
|
@@ -48,7 +52,7 @@ export const ClawConnectionService: OpenClawPluginService = {
|
|
|
48
52
|
// 停止时销毁channel
|
|
49
53
|
destroyConnection();
|
|
50
54
|
},
|
|
51
|
-
};
|
|
55
|
+
} as OpenClawPluginService);
|
|
52
56
|
|
|
53
57
|
/**
|
|
54
58
|
* 启动轮询机制
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 检查当前版本是否为 beta 版本
|
|
7
|
+
*/
|
|
8
|
+
export function isBetaVersion(): boolean {
|
|
9
|
+
try {
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const packagePath = join(__dirname, "../../package.json");
|
|
13
|
+
const pkgContent = readFileSync(packagePath, "utf-8");
|
|
14
|
+
const pkg = JSON.parse(pkgContent);
|
|
15
|
+
const version = pkg.version || "";
|
|
16
|
+
return version.includes("beta");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`Failed to check version: ${error}`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|