@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 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 { ClawConnectionService } from "./src/services/connection/index.js";
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
- async register(api: OpenClawPluginApi) {
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(ClawConnectionService);
18
+ api.registerService(createClawConnectionService(api));
23
19
 
24
20
  // 注册所有的命令行
25
21
  registerCommands(api);
@@ -22,6 +22,10 @@
22
22
  }
23
23
  },
24
24
  "additionalProperties": false
25
+ },
26
+ "env": {
27
+ "type": "string",
28
+ "enum": ["test", "production"]
25
29
  }
26
30
  }
27
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@honor-claw/yoyo",
3
- "version": "0.0.1-beta.5",
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
- this.ws.send(JSON.stringify(message));
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';
@@ -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
- // import { registerLogoutCommand } from "./logout/index.js";
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("Login to YOYOClaw and register devices")
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;
@@ -2,34 +2,42 @@
2
2
  * 荣耀认证配置管理
3
3
  */
4
4
 
5
- import type { HonorAuthConfig } from './types.js';
5
+ import type { HonorAuthConfig } from "./types.js";
6
+ import { getYoyoEnv } from "../runtime.js";
6
7
 
7
8
  /**
8
9
  * 默认配置
9
10
  */
10
- const DEFAULT_CONFIG: HonorAuthConfig = {
11
- authHost: 'https://hnoauth-login-test-drcn.cloud.honor.com',
12
- clientId: '221641491',
13
- redirectUri: 'http://127.0.0.1:8081/deepLink',
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: 'openid profile',
16
- reqClientType: '110',
17
- loginChannel: '99011000',
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(overrides?: Partial<HonorAuthConfig>): HonorAuthConfig {
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
- ...DEFAULT_CONFIG,
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
  */
@@ -26,5 +26,7 @@ export interface YoyoClawPluginConfig {
26
26
  enabled?: boolean;
27
27
  config?: {
28
28
  user?: UserConfig;
29
+ /** 运行环境:test 或 production */
30
+ env?: 'test' | 'production';
29
31
  };
30
32
  }
@@ -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
- if (noAuth) {
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 'zod';
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 ClawConnectionService: OpenClawPluginService = {
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
+ }