yuanflow-cli 0.1.7 → 0.1.8

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
@@ -36,7 +36,7 @@ yuanflow-cli works download --platform douyin --target "https://v.douyin.com/xxx
36
36
  yuanflow-cli search content --platform xiaohongshu --keyword "美妆" --format agent-json
37
37
  yuanflow-cli search users --platform instagram --keyword "nasa" --format agent-json
38
38
  yuanflow-cli knowledge docs --format agent-json
39
- yuanflow-cli knowledge entry --task-intent generate_script --content-goal "写一个创业者短视频选题" --format agent-json
39
+ yuanflow-cli knowledge entry --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
40
40
  yuanflow-cli oss signed-url --key path/to/file.png --ttl-seconds 1800 --format agent-json
41
41
  yuanflow-cli list douyin
42
42
  ```
@@ -96,12 +96,12 @@ Agent 不确定参数时先执行 `commands list`,再用 `schema works.douyin.
96
96
 
97
97
  ```bash
98
98
  yuanflow-cli knowledge docs --format agent-json
99
- yuanflow-cli knowledge entry --task-intent generate_script --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --format agent-json
100
- yuanflow-cli knowledge rules --pack-code topic_pain_point_pack --task-intent generate_script --content-goal "写一个创业者短视频选题" --format agent-json
101
- yuanflow-cli knowledge rule-detail --rule-code some_rule_code --task-intent generate_script --content-goal "写一个创业者短视频选题" --format agent-json
99
+ yuanflow-cli knowledge entry --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
100
+ yuanflow-cli knowledge rules --pack-code topic_pain_point_pack --output-format short_video_script --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
101
+ yuanflow-cli knowledge rule-detail --rule-code some_rule_code --output-format short_video_script --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
102
102
  ```
103
103
 
104
- Agent 应先查看 `knowledge docs`,再把用户需求整理成 `task_frame`,按接口返回的 `next_actions` 渐进查询。
104
+ Agent 应先查看 `knowledge docs`,再把用户需求整理成 `task_frame`,按接口返回的 `next_actions` 渐进查询。v2 接口不要求 `task_intent`;旧字段仅保留兼容。`packs`、`rules` 的 `data` 可能直接是数组,Agent 不应假设固定对象结构,应优先读取 `next_actions` 继续下钻。
105
105
 
106
106
  ### OSS 原子能力
107
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuanflow-cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "YuanFlow API CLI and skill installer for supported AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -50,10 +50,10 @@ yuanflow-cli knowledge docs --format agent-json
50
50
 
51
51
  ```bash
52
52
  yuanflow-cli knowledge entry \
53
- --task-intent generate_script \
54
53
  --output-format short_video_script \
55
54
  --domain 自媒体运营 \
56
55
  --content-goal "写一个面向创业者的短视频选题" \
56
+ --target-audience 创业者 \
57
57
  --tone 清晰直接 \
58
58
  --communication-mode 口播 \
59
59
  --format agent-json
@@ -64,8 +64,9 @@ yuanflow-cli knowledge entry \
64
64
  ```bash
65
65
  yuanflow-cli knowledge rules \
66
66
  --pack-code topic_pain_point_pack \
67
- --task-intent generate_script \
67
+ --output-format short_video_script \
68
68
  --content-goal "写一个面向创业者的短视频选题" \
69
+ --target-audience 创业者 \
69
70
  --format agent-json
70
71
  ```
71
72
 
@@ -80,14 +81,14 @@ yuanflow-cli knowledge rules \
80
81
  "args": [
81
82
  "knowledge",
82
83
  "entry",
83
- "--task-intent",
84
- "generate_script",
85
84
  "--output-format",
86
85
  "short_video_script",
87
86
  "--domain",
88
87
  "自媒体运营",
89
88
  "--content-goal",
90
89
  "写一个面向创业者的短视频选题",
90
+ "--target-audience",
91
+ "创业者",
91
92
  "--format",
92
93
  "agent-json"
93
94
  ]
@@ -100,4 +101,6 @@ yuanflow-cli knowledge rules \
100
101
  - 不要使用 `/atomic/aliyun-db/query` 查询知识库内部表。
101
102
  - 不要把知识库核心方法论静态写入回复,只按接口返回内容使用。
102
103
  - 如果接口返回 `next_actions`,优先按它推荐的动作继续查询。
104
+ - v2 接口不要求 `task_intent`;旧字段只作为兼容字段,不要在新调用里默认填写。
105
+ - `packs`、`rules` 的 `data` 可能直接是数组,不要假设固定 `{ packs: [] }` 或 `{ rules: [] }` 结构。
103
106
  - 如果用户只是在问“有什么知识库方向”,先用 `knowledge docs`。
@@ -29,12 +29,27 @@ const ERROR_MAP = [
29
29
  retryable: false,
30
30
  test: (message) => message.includes('HTTP 401') || message.includes('HTTP 403'),
31
31
  },
32
+ {
33
+ code: 'UPSTREAM_CONFIG_ERROR',
34
+ exitCode: 4,
35
+ retryable: false,
36
+ test: (message) => message.includes('原子能力主站令牌未配置'),
37
+ },
32
38
  {
33
39
  code: 'UPSTREAM_ERROR',
34
40
  exitCode: 4,
35
41
  retryable: true,
36
42
  test: (message) => /HTTP 5\d\d/.test(message),
37
43
  },
44
+ {
45
+ code: 'UPSTREAM_ROUTE_MISSING',
46
+ exitCode: 4,
47
+ retryable: false,
48
+ test: (message) =>
49
+ message.includes('Invalid URL') ||
50
+ message.includes('接口返回了前端 HTML 页面') ||
51
+ message.includes('接口返回了非 JSON 内容'),
52
+ },
38
53
  {
39
54
  code: 'NETWORK_ERROR',
40
55
  exitCode: 5,
@@ -45,11 +45,22 @@ export async function callAtomic(path, options = {}) {
45
45
  });
46
46
 
47
47
  const text = await response.text();
48
+ const contentType = response.headers.get('content-type') || '';
48
49
  const payload = parseMaybeJson(text);
49
50
  if (!response.ok) {
50
51
  const message = typeof payload === 'object' ? JSON.stringify(payload) : text;
51
52
  throw new Error(`请求失败:HTTP ${response.status} ${message}`);
52
53
  }
54
+ if (options.expectJson !== false) {
55
+ assertJsonResponse({
56
+ contentType,
57
+ method,
58
+ path: normalizedPath,
59
+ status: response.status,
60
+ payload,
61
+ text,
62
+ });
63
+ }
53
64
  return payload;
54
65
  }
55
66
 
@@ -95,6 +106,34 @@ function parseMaybeJson(text) {
95
106
  }
96
107
  }
97
108
 
109
+ function assertJsonResponse({ contentType, method, path, status, payload, text }) {
110
+ if (payload !== null && typeof payload === 'object') {
111
+ return;
112
+ }
113
+ const preview = compactPreview(text);
114
+ const isHtml = looksLikeHtml(text) || contentType.toLowerCase().includes('text/html');
115
+ if (isHtml) {
116
+ throw new Error(
117
+ `接口返回了前端 HTML 页面,不是结构化 JSON。请检查云端接口是否已部署或路径是否正确:HTTP ${status} ${method} ${path}。返回预览:${preview}`,
118
+ );
119
+ }
120
+ throw new Error(
121
+ `接口返回了非 JSON 内容。请检查云端接口是否已部署或路径是否正确:HTTP ${status} ${method} ${path}。返回预览:${preview}`,
122
+ );
123
+ }
124
+
125
+ function looksLikeHtml(text) {
126
+ const normalized = String(text || '').trim().toLowerCase();
127
+ return normalized.startsWith('<!doctype') || normalized.startsWith('<html') || normalized.includes('<html');
128
+ }
129
+
130
+ function compactPreview(text) {
131
+ return String(text || '')
132
+ .replace(/\s+/g, ' ')
133
+ .trim()
134
+ .slice(0, 160);
135
+ }
136
+
98
137
  function maskToken(token) {
99
138
  if (!token) {
100
139
  return '';
package/src/cli.js CHANGED
@@ -539,7 +539,7 @@ function printHelp() {
539
539
  yuanflow-cli search content --platform xiaohongshu --keyword "美妆" --dry-run
540
540
  yuanflow-cli search users --platform instagram --keyword "nasa" --dry-run
541
541
  yuanflow-cli knowledge docs --dry-run
542
- yuanflow-cli knowledge entry --task-intent generate_script --content-goal "写一个创业者短视频选题" --format agent-json
542
+ yuanflow-cli knowledge entry --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
543
543
  yuanflow-cli oss signed-url --key path/to/file.png --ttl-seconds 1800 --format agent-json
544
544
  yuanflow-cli list douyin
545
545
 
@@ -92,10 +92,11 @@ function knowledgeCommand(action, description, extraOptions = []) {
92
92
 
93
93
  function taskFrameOptions() {
94
94
  return [
95
- { flag: '--task-intent', name: 'taskIntent', required: false, label: '任务意图,例如 generate_script、generate_copy、evaluate_content。' },
95
+ { flag: '--task-intent', name: 'taskIntent', required: false, label: '兼容旧字段;v2 接口不要求填写。' },
96
96
  { flag: '--output-format', name: 'outputFormat', required: false, label: '输出格式,例如 short_video_script、social_post、any。' },
97
97
  { flag: '--domain', name: 'domain', required: false, label: '内容领域。' },
98
98
  { flag: '--content-goal', name: 'contentGoal', required: false, label: '具体创作或评估目标。' },
99
+ { flag: '--target-audience', name: 'targetAudience', required: false, label: '目标受众,例如 创业者、宝妈、普通用户。' },
99
100
  { flag: '--tone', name: 'tone', required: false, label: '语气风格。' },
100
101
  { flag: '--communication-mode', name: 'communicationMode', required: false, label: '表达方式,例如 口播、图文、文章。' },
101
102
  { flag: '--task-frame-file', name: 'taskFrameFile', required: false, label: '读取 task_frame JSON 文件。' },
@@ -116,10 +117,11 @@ async function buildTaskFrame(options) {
116
117
  : {};
117
118
  return removeUndefined({
118
119
  ...fromFile,
119
- task_intent: pick(options, 'task-intent', fromFile.task_intent || 'general'),
120
+ task_intent: pick(options, 'task-intent', fromFile.task_intent),
120
121
  output_format: pick(options, 'output-format', fromFile.output_format || 'any'),
121
122
  domain: pick(options, 'domain', fromFile.domain || '自媒体运营'),
122
123
  content_goal: pick(options, 'content-goal', fromFile.content_goal || ''),
124
+ target_audience: pick(options, 'target-audience', fromFile.target_audience || ''),
123
125
  tone: pick(options, 'tone', fromFile.tone || ''),
124
126
  communication_mode: pick(options, 'communication-mode', fromFile.communication_mode || ''),
125
127
  });