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 CHANGED
@@ -35,9 +35,11 @@ yuanflow-cli list douyin
35
35
  环境变量:
36
36
 
37
37
  ```bash
38
- YUANFLOW_TOKEN=<你的令牌>
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) || path.join(homeDir, '.config');
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() || path.join(homeDir, '.codex');
41
- const claudeHome = (env.CLAUDE_CONFIG_DIR || '').trim() || path.join(homeDir, '.claude');
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: path.join(codexHome, 'skills'),
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: path.join(claudeHome, 'skills'),
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: path.join(homeDir, '.cursor', 'skills'),
63
- markers: [path.join(homeDir, '.cursor')],
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: path.join(homeDir, '.trae', 'skills'),
70
- markers: [path.join(homeDir, '.trae')],
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: path.join(configHome, 'opencode', 'skills'),
77
- markers: [path.join(configHome, 'opencode')],
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: path.join(homeDir, '.copilot', 'skills'),
84
- markers: [path.join(homeDir, '.copilot')],
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: path.join(homeDir, '.gemini', 'skills'),
91
- markers: [path.join(homeDir, '.gemini')],
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: path.join(configHome, 'agents', 'skills'),
98
- markers: [path.join(homeDir, '.kimi')],
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: path.join(homeDir, '.codeium', 'windsurf', 'skills'),
105
- markers: [path.join(homeDir, '.codeium', 'windsurf')],
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: path.join(homeDir, '.openclaw', 'skills'),
118
+ globalSkillsDir: joinTargetPath(homeDir, '.openclaw', 'skills'),
112
119
  markers: [
113
- path.join(homeDir, '.openclaw'),
114
- path.join(homeDir, '.clawdbot'),
115
- path.join(homeDir, '.moltbot'),
120
+ joinTargetPath(homeDir, '.openclaw'),
121
+ joinTargetPath(homeDir, '.clawdbot'),
122
+ joinTargetPath(homeDir, '.moltbot'),
116
123
  ],
117
124
  resolveGlobalSkillsDir(currentHomeDir, exists = defaultExists) {
118
- if (exists(path.join(currentHomeDir, '.openclaw'))) {
119
- return path.join(currentHomeDir, '.openclaw', 'skills');
125
+ if (exists(joinTargetPath(currentHomeDir, '.openclaw'))) {
126
+ return joinTargetPath(currentHomeDir, '.openclaw', 'skills');
120
127
  }
121
- if (exists(path.join(currentHomeDir, '.clawdbot'))) {
122
- return path.join(currentHomeDir, '.clawdbot', 'skills');
128
+ if (exists(joinTargetPath(currentHomeDir, '.clawdbot'))) {
129
+ return joinTargetPath(currentHomeDir, '.clawdbot', 'skills');
123
130
  }
124
- if (exists(path.join(currentHomeDir, '.moltbot'))) {
125
- return path.join(currentHomeDir, '.moltbot', 'skills');
131
+ if (exists(joinTargetPath(currentHomeDir, '.moltbot'))) {
132
+ return joinTargetPath(currentHomeDir, '.moltbot', 'skills');
126
133
  }
127
134
 
128
- return path.join(currentHomeDir, '.openclaw', 'skills');
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: path.join(homeDir, '.trae-cn', 'skills'),
136
- markers: [path.join(homeDir, '.trae-cn')],
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: path.join(homeDir, '.qoder', 'skills'),
143
- markers: [path.join(homeDir, '.qoder')],
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: path.join(homeDir, '.qwen', 'skills'),
150
- markers: [path.join(homeDir, '.qwen')],
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuanflow-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "YuanFlow API CLI and skill installer for supported AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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:YUANFLOW_TOKEN="<你的令牌>"
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 || config.token || process.env.YUANFLOW_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。请先执行 yuanflow-cli config set-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);