yuanflow-cli 0.1.2 → 0.1.4
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/README.md +3 -1
- package/lib/skill-installer/agents.cjs +43 -36
- package/lib/skill-installer/repo-source.cjs +14 -12
- package/package.json +1 -1
- package/skills/yuanflow-skill/SKILL.md +10 -0
- package/skills/yuanflow-skill/yuanflow-cli/SKILL.md +23 -4
- package/src/cli.js +2 -0
- package/src/request.js +2 -2
package/README.md
CHANGED
|
@@ -35,9 +35,11 @@ yuanflow-cli list douyin
|
|
|
35
35
|
环境变量:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
|
|
38
|
+
YUANCHUANG_API_TOKEN=<你的令牌>
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
token 优先级:`--token` > `YUANCHUANG_API_TOKEN` > 本地 `config.token`。独立 CLI 用户可以使用环境变量或 `config set-token`;在 YuanFlow 主程序内使用时,token 由主程序认证系统注入,不需要手动配置。
|
|
42
|
+
|
|
41
43
|
## Skill 安装器
|
|
42
44
|
|
|
43
45
|
```bash
|
|
@@ -22,8 +22,15 @@ function defaultExists(targetPath) {
|
|
|
22
22
|
return fs.existsSync(targetPath);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function joinTargetPath(baseDir, ...segments) {
|
|
26
|
+
if (typeof baseDir === 'string' && baseDir.startsWith('/')) {
|
|
27
|
+
return path.posix.join(baseDir, ...segments);
|
|
28
|
+
}
|
|
29
|
+
return path.join(baseDir, ...segments);
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function getConfigHome(homeDir, env) {
|
|
26
|
-
return (env && env.XDG_CONFIG_HOME) ||
|
|
33
|
+
return (env && env.XDG_CONFIG_HOME) || joinTargetPath(homeDir, '.config');
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
function getCanonicalSkillsDir({
|
|
@@ -37,117 +44,117 @@ function getCanonicalSkillsDir({
|
|
|
37
44
|
|
|
38
45
|
function buildAgentDefinitions({ homeDir = os.homedir(), env = process.env } = {}) {
|
|
39
46
|
const configHome = getConfigHome(homeDir, env);
|
|
40
|
-
const codexHome = (env.CODEX_HOME || '').trim() ||
|
|
41
|
-
const claudeHome = (env.CLAUDE_CONFIG_DIR || '').trim() ||
|
|
47
|
+
const codexHome = (env.CODEX_HOME || '').trim() || joinTargetPath(homeDir, '.codex');
|
|
48
|
+
const claudeHome = (env.CLAUDE_CONFIG_DIR || '').trim() || joinTargetPath(homeDir, '.claude');
|
|
42
49
|
|
|
43
50
|
return {
|
|
44
51
|
codex: {
|
|
45
52
|
name: 'codex',
|
|
46
53
|
displayName: 'Codex',
|
|
47
54
|
projectSkillsDir: '.agents/skills',
|
|
48
|
-
globalSkillsDir:
|
|
55
|
+
globalSkillsDir: joinTargetPath(codexHome, 'skills'),
|
|
49
56
|
markers: [codexHome],
|
|
50
57
|
},
|
|
51
58
|
'claude-code': {
|
|
52
59
|
name: 'claude-code',
|
|
53
60
|
displayName: 'Claude Code',
|
|
54
61
|
projectSkillsDir: '.claude/skills',
|
|
55
|
-
globalSkillsDir:
|
|
62
|
+
globalSkillsDir: joinTargetPath(claudeHome, 'skills'),
|
|
56
63
|
markers: [claudeHome],
|
|
57
64
|
},
|
|
58
65
|
cursor: {
|
|
59
66
|
name: 'cursor',
|
|
60
67
|
displayName: 'Cursor',
|
|
61
68
|
projectSkillsDir: '.agents/skills',
|
|
62
|
-
globalSkillsDir:
|
|
63
|
-
markers: [
|
|
69
|
+
globalSkillsDir: joinTargetPath(homeDir, '.cursor', 'skills'),
|
|
70
|
+
markers: [joinTargetPath(homeDir, '.cursor')],
|
|
64
71
|
},
|
|
65
72
|
trae: {
|
|
66
73
|
name: 'trae',
|
|
67
74
|
displayName: 'Trae',
|
|
68
75
|
projectSkillsDir: '.trae/skills',
|
|
69
|
-
globalSkillsDir:
|
|
70
|
-
markers: [
|
|
76
|
+
globalSkillsDir: joinTargetPath(homeDir, '.trae', 'skills'),
|
|
77
|
+
markers: [joinTargetPath(homeDir, '.trae')],
|
|
71
78
|
},
|
|
72
79
|
opencode: {
|
|
73
80
|
name: 'opencode',
|
|
74
81
|
displayName: 'OpenCode',
|
|
75
82
|
projectSkillsDir: '.agents/skills',
|
|
76
|
-
globalSkillsDir:
|
|
77
|
-
markers: [
|
|
83
|
+
globalSkillsDir: joinTargetPath(configHome, 'opencode', 'skills'),
|
|
84
|
+
markers: [joinTargetPath(configHome, 'opencode')],
|
|
78
85
|
},
|
|
79
86
|
'github-copilot': {
|
|
80
87
|
name: 'github-copilot',
|
|
81
88
|
displayName: 'GitHub Copilot',
|
|
82
89
|
projectSkillsDir: '.agents/skills',
|
|
83
|
-
globalSkillsDir:
|
|
84
|
-
markers: [
|
|
90
|
+
globalSkillsDir: joinTargetPath(homeDir, '.copilot', 'skills'),
|
|
91
|
+
markers: [joinTargetPath(homeDir, '.copilot')],
|
|
85
92
|
},
|
|
86
93
|
'gemini-cli': {
|
|
87
94
|
name: 'gemini-cli',
|
|
88
95
|
displayName: 'Gemini CLI',
|
|
89
96
|
projectSkillsDir: '.agents/skills',
|
|
90
|
-
globalSkillsDir:
|
|
91
|
-
markers: [
|
|
97
|
+
globalSkillsDir: joinTargetPath(homeDir, '.gemini', 'skills'),
|
|
98
|
+
markers: [joinTargetPath(homeDir, '.gemini')],
|
|
92
99
|
},
|
|
93
100
|
'kimi-cli': {
|
|
94
101
|
name: 'kimi-cli',
|
|
95
102
|
displayName: 'Kimi Code CLI',
|
|
96
103
|
projectSkillsDir: '.agents/skills',
|
|
97
|
-
globalSkillsDir:
|
|
98
|
-
markers: [
|
|
104
|
+
globalSkillsDir: joinTargetPath(configHome, 'agents', 'skills'),
|
|
105
|
+
markers: [joinTargetPath(homeDir, '.kimi')],
|
|
99
106
|
},
|
|
100
107
|
windsurf: {
|
|
101
108
|
name: 'windsurf',
|
|
102
109
|
displayName: 'Windsurf',
|
|
103
110
|
projectSkillsDir: '.windsurf/skills',
|
|
104
|
-
globalSkillsDir:
|
|
105
|
-
markers: [
|
|
111
|
+
globalSkillsDir: joinTargetPath(homeDir, '.codeium', 'windsurf', 'skills'),
|
|
112
|
+
markers: [joinTargetPath(homeDir, '.codeium', 'windsurf')],
|
|
106
113
|
},
|
|
107
114
|
openclaw: {
|
|
108
115
|
name: 'openclaw',
|
|
109
116
|
displayName: 'OpenClaw',
|
|
110
117
|
projectSkillsDir: 'skills',
|
|
111
|
-
globalSkillsDir:
|
|
118
|
+
globalSkillsDir: joinTargetPath(homeDir, '.openclaw', 'skills'),
|
|
112
119
|
markers: [
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
joinTargetPath(homeDir, '.openclaw'),
|
|
121
|
+
joinTargetPath(homeDir, '.clawdbot'),
|
|
122
|
+
joinTargetPath(homeDir, '.moltbot'),
|
|
116
123
|
],
|
|
117
124
|
resolveGlobalSkillsDir(currentHomeDir, exists = defaultExists) {
|
|
118
|
-
if (exists(
|
|
119
|
-
return
|
|
125
|
+
if (exists(joinTargetPath(currentHomeDir, '.openclaw'))) {
|
|
126
|
+
return joinTargetPath(currentHomeDir, '.openclaw', 'skills');
|
|
120
127
|
}
|
|
121
|
-
if (exists(
|
|
122
|
-
return
|
|
128
|
+
if (exists(joinTargetPath(currentHomeDir, '.clawdbot'))) {
|
|
129
|
+
return joinTargetPath(currentHomeDir, '.clawdbot', 'skills');
|
|
123
130
|
}
|
|
124
|
-
if (exists(
|
|
125
|
-
return
|
|
131
|
+
if (exists(joinTargetPath(currentHomeDir, '.moltbot'))) {
|
|
132
|
+
return joinTargetPath(currentHomeDir, '.moltbot', 'skills');
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
return
|
|
135
|
+
return joinTargetPath(currentHomeDir, '.openclaw', 'skills');
|
|
129
136
|
},
|
|
130
137
|
},
|
|
131
138
|
'trae-cn': {
|
|
132
139
|
name: 'trae-cn',
|
|
133
140
|
displayName: 'Trae CN',
|
|
134
141
|
projectSkillsDir: '.trae/skills',
|
|
135
|
-
globalSkillsDir:
|
|
136
|
-
markers: [
|
|
142
|
+
globalSkillsDir: joinTargetPath(homeDir, '.trae-cn', 'skills'),
|
|
143
|
+
markers: [joinTargetPath(homeDir, '.trae-cn')],
|
|
137
144
|
},
|
|
138
145
|
qoder: {
|
|
139
146
|
name: 'qoder',
|
|
140
147
|
displayName: 'Qoder',
|
|
141
148
|
projectSkillsDir: '.qoder/skills',
|
|
142
|
-
globalSkillsDir:
|
|
143
|
-
markers: [
|
|
149
|
+
globalSkillsDir: joinTargetPath(homeDir, '.qoder', 'skills'),
|
|
150
|
+
markers: [joinTargetPath(homeDir, '.qoder')],
|
|
144
151
|
},
|
|
145
152
|
'qwen-code': {
|
|
146
153
|
name: 'qwen-code',
|
|
147
154
|
displayName: 'Qwen Code',
|
|
148
155
|
projectSkillsDir: '.qwen/skills',
|
|
149
|
-
globalSkillsDir:
|
|
150
|
-
markers: [
|
|
156
|
+
globalSkillsDir: joinTargetPath(homeDir, '.qwen', 'skills'),
|
|
157
|
+
markers: [joinTargetPath(homeDir, '.qwen')],
|
|
151
158
|
},
|
|
152
159
|
};
|
|
153
160
|
}
|
|
@@ -90,27 +90,20 @@ function resolveRepositoryRoot(extractDir) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function resolveLocalSkillRoot(packageRoot) {
|
|
93
|
+
const localRoot = path.resolve(packageRoot, '..', 'YuanFlow-skill');
|
|
94
|
+
if (fs.existsSync(path.join(localRoot, 'SKILL.md'))) {
|
|
95
|
+
return localRoot;
|
|
96
|
+
}
|
|
97
|
+
|
|
93
98
|
const packagedRoot = path.resolve(packageRoot, 'skills', 'yuanflow-skill');
|
|
94
99
|
if (fs.existsSync(path.join(packagedRoot, 'SKILL.md'))) {
|
|
95
100
|
return packagedRoot;
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
const localRoot = path.resolve(packageRoot, '..', 'YuanFlow-skill');
|
|
99
|
-
if (fs.existsSync(path.join(localRoot, 'SKILL.md'))) {
|
|
100
|
-
return localRoot;
|
|
101
|
-
}
|
|
102
103
|
return '';
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
async function prepareSkillSource({ packageRoot }) {
|
|
106
|
-
const localRoot = resolveLocalSkillRoot(packageRoot);
|
|
107
|
-
if (localRoot) {
|
|
108
|
-
return {
|
|
109
|
-
sourceRoot: localRoot,
|
|
110
|
-
cleanup() {},
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
107
|
const repoConfig = readRepositoryConfig(packageRoot);
|
|
115
108
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'yuanflow-skill-'));
|
|
116
109
|
const archiveFile = path.join(tempRoot, `${repoConfig.repo}-${repoConfig.ref}.tar.gz`);
|
|
@@ -138,6 +131,15 @@ async function prepareSkillSource({ packageRoot }) {
|
|
|
138
131
|
cloneRepository({ repoConfig, targetDir: cloneDir });
|
|
139
132
|
sourceRoot = resolveRepositoryRoot(cloneDir);
|
|
140
133
|
} catch (gitError) {
|
|
134
|
+
const localRoot = resolveLocalSkillRoot(packageRoot);
|
|
135
|
+
if (localRoot) {
|
|
136
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
137
|
+
return {
|
|
138
|
+
sourceRoot: localRoot,
|
|
139
|
+
cleanup() {},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
141
143
|
const archiveMessage = archiveError instanceof Error ? archiveError.message : String(archiveError);
|
|
142
144
|
const gitMessage = gitError instanceof Error ? gitError.message : String(gitError);
|
|
143
145
|
throw new Error(`下载 skill 仓库失败。archive: ${archiveMessage}; git: ${gitMessage}`);
|
package/package.json
CHANGED
|
@@ -14,6 +14,16 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
14
14
|
- 根目录这一份 `SKILL.md`
|
|
15
15
|
- `yuanflow-cli/`
|
|
16
16
|
|
|
17
|
+
## 环境判断
|
|
18
|
+
|
|
19
|
+
执行本 Skill 前,先判断当前运行环境:
|
|
20
|
+
|
|
21
|
+
1. 如果当前 Agent 可用 YuanFlow 内置工具,例如 `yuanflow_cli_call` 或 `yuanflow_gateway_request`,优先使用 YuanFlow 内置工具。不要要求用户安装 npm,也不要要求用户配置 token。
|
|
22
|
+
2. 如果当前不在 YuanFlow 内,且本地可执行 `yuanflow-cli --help`,使用本地 CLI。
|
|
23
|
+
3. 如果当前不在 YuanFlow 内,且本地没有 `yuanflow-cli`,再提示用户安装 `npm install -g yuanflow-cli`。
|
|
24
|
+
|
|
25
|
+
不要在回复、日志或文件中暴露 token。
|
|
26
|
+
|
|
17
27
|
## 分流规则
|
|
18
28
|
|
|
19
29
|
### 1. 走 `yuanflow-cli`
|
|
@@ -17,7 +17,19 @@ description: Use when the user needs atomic API calls for social-media platforms
|
|
|
17
17
|
- 用户要用 CLI 调接口并保存 JSON
|
|
18
18
|
- 用户要让 Agent 稳定解析调用结果
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## 调用方式选择
|
|
21
|
+
|
|
22
|
+
优先级如下:
|
|
23
|
+
|
|
24
|
+
1. YuanFlow 内置工具:如果当前工具列表里存在 `yuanflow_cli_call`,使用它调用社媒接口。
|
|
25
|
+
2. YuanFlow 网关工具:如果只有 `yuanflow_gateway_request`,可用它访问受控服务接口。
|
|
26
|
+
3. 本地 CLI:如果当前环境可执行 `yuanflow-cli --help`,使用本地 CLI。
|
|
27
|
+
4. 安装提示:只有以上都不可用时,才提示用户安装 `npm install -g yuanflow-cli`。
|
|
28
|
+
|
|
29
|
+
在 YuanFlow 主程序内,不要提示用户配置 token;token 由主程序 KEY 认证注入。
|
|
30
|
+
在外部 Agent 环境中,使用环境变量 `YUANCHUANG_API_TOKEN`,也可以使用本地 `config set-token`。
|
|
31
|
+
|
|
32
|
+
## 外部 Agent 安装和更新
|
|
21
33
|
|
|
22
34
|
要求 Node.js 20 或更高版本。
|
|
23
35
|
|
|
@@ -44,7 +56,13 @@ npm view yuanflow-cli version
|
|
|
44
56
|
yuanflow-cli --help
|
|
45
57
|
```
|
|
46
58
|
|
|
47
|
-
## 配置
|
|
59
|
+
## 外部 Agent 配置
|
|
60
|
+
|
|
61
|
+
独立 CLI 使用时,可以通过环境变量或本地 config 保存 token。token 优先级是:`--token` > `YUANCHUANG_API_TOKEN` > 本地 `config.token`。
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
$env:YUANCHUANG_API_TOKEN="<你的令牌>"
|
|
65
|
+
```
|
|
48
66
|
|
|
49
67
|
保存 Yuan API 令牌和服务地址:
|
|
50
68
|
|
|
@@ -63,13 +81,13 @@ yuanflow-cli douyin video-detail "https://v.douyin.com/xxx/" --token <你的令
|
|
|
63
81
|
或用环境变量:
|
|
64
82
|
|
|
65
83
|
```powershell
|
|
66
|
-
$env:
|
|
84
|
+
$env:YUANCHUANG_API_TOKEN="<你的令牌>"
|
|
67
85
|
yuanflow-cli douyin hot-search
|
|
68
86
|
```
|
|
69
87
|
|
|
70
88
|
## Agent 调用规则
|
|
71
89
|
|
|
72
|
-
给 AI Agent 使用时,先查命令和 schema,再调用接口。
|
|
90
|
+
给 AI Agent 使用时,先查命令和 schema,再调用接口。YuanFlow 内置环境下,优先调用 `yuanflow_cli_call`;外部 Agent 环境下,优先调用 `yuanflow-cli`。
|
|
73
91
|
|
|
74
92
|
```powershell
|
|
75
93
|
yuanflow-cli commands list
|
|
@@ -205,3 +223,4 @@ yuanflow-cli bilibili video-detail "https://www.bilibili.com/video/BVxxx" -o bil
|
|
|
205
223
|
- 调真实接口前,先用 `--dry-run` 核对 URL、method、headers 和 body。
|
|
206
224
|
- Agent 自动处理结果时,统一加 `--format agent-json`。
|
|
207
225
|
- 不要把 token 写进文档、日志或最终回复。
|
|
226
|
+
- 不要要求用户提供或粘贴 token。
|
package/src/cli.js
CHANGED
|
@@ -367,6 +367,8 @@ function printHelp() {
|
|
|
367
367
|
|
|
368
368
|
说明:
|
|
369
369
|
所有请求都会调用 Yuan API 的 /social/*path,并使用 Authorization: Bearer <token>。
|
|
370
|
+
token 优先级:--token > YUANCHUANG_API_TOKEN > 本地 config.token。
|
|
371
|
+
YuanFlow 主程序内使用时,token 由主程序认证系统注入,不需要手动配置。
|
|
370
372
|
AI Agent 推荐使用 --format agent-json 获取稳定 JSON 外壳。
|
|
371
373
|
`);
|
|
372
374
|
}
|
package/src/request.js
CHANGED
|
@@ -5,14 +5,14 @@ import { findEndpointByPath, normalizeSocialPath } from './registry.js';
|
|
|
5
5
|
export async function callEndpoint(socialPath, options = {}) {
|
|
6
6
|
const config = await readConfig();
|
|
7
7
|
const baseUrl = cleanBaseUrl(options.baseUrl || config.baseUrl);
|
|
8
|
-
const token = options.token ||
|
|
8
|
+
const token = options.token || process.env.YUANCHUANG_API_TOKEN || config.token || '';
|
|
9
9
|
const normalizedPath = normalizeSocialPath(socialPath);
|
|
10
10
|
const endpoint = findEndpointByPath(normalizedPath);
|
|
11
11
|
const method = options.method || endpoint?.method || 'POST';
|
|
12
12
|
const url = new URL(`/social${normalizedPath}`, baseUrl);
|
|
13
13
|
|
|
14
14
|
if (!token) {
|
|
15
|
-
throw new Error('缺少 token
|
|
15
|
+
throw new Error('缺少 token。请设置 YUANCHUANG_API_TOKEN,或执行 yuanflow-cli config set-token <你的令牌>');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const body = await resolveBody(options);
|