yuanflow-cli 0.1.0

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.
@@ -0,0 +1,82 @@
1
+ import { apiPost } from "../core/http.js";
2
+ import { getToken } from "../core/token.js";
3
+ import {
4
+ getGlobalFlagsHelp,
5
+ parseFloatNumber,
6
+ parseInteger,
7
+ } from "./shared.js";
8
+
9
+ export function getKbHelp() {
10
+ return `Search knowledge base content
11
+
12
+ Usage:
13
+ ycloud kb search [flags]
14
+
15
+ Required Flags:
16
+ --query string search keyword
17
+
18
+ Optional Flags:
19
+ --top-k int number of results to return (default 5)
20
+ --min-score float minimum similarity score
21
+ --course-name string course name filter
22
+
23
+ ${getGlobalFlagsHelp()}
24
+
25
+ Examples:
26
+ ycloud kb search --query "短视频脚本结构" --top-k 5 --output json
27
+
28
+ Read on success:
29
+ data.hits
30
+
31
+ Read on error:
32
+ error.code
33
+ error.message
34
+ error.retryable`;
35
+ }
36
+
37
+ async function requireToken(context) {
38
+ const token = await getToken();
39
+ if (!token) {
40
+ return context.fail(
41
+ context.commandName,
42
+ "TOKEN_MISSING",
43
+ "缺少 YUANCHUANG_API_TOKEN",
44
+ 3,
45
+ false,
46
+ );
47
+ }
48
+ return token;
49
+ }
50
+
51
+ export async function executeKbSearch(context, tokens) {
52
+ const options = context.parseCommandFlags(tokens);
53
+ if (!options.query) {
54
+ return context.fail(
55
+ "kb search",
56
+ "BAD_ARGUMENT",
57
+ "缺少必填参数 --query",
58
+ 2,
59
+ false,
60
+ );
61
+ }
62
+
63
+ const token = await requireToken(context);
64
+ if (typeof token !== "string") {
65
+ return token;
66
+ }
67
+
68
+ const data = await apiPost("/v1/knowledge-base/search", {
69
+ token,
70
+ traceId: context.globalOptions.traceId,
71
+ timeout: context.globalOptions.timeout,
72
+ baseUrl: context.globalOptions.baseUrl,
73
+ body: {
74
+ query: options.query,
75
+ top_k: parseInteger(options["top-k"]) ?? 5,
76
+ min_score: parseFloatNumber(options["min-score"]),
77
+ course_name: options["course-name"],
78
+ },
79
+ });
80
+
81
+ return context.ok("kb search", data);
82
+ }
@@ -0,0 +1,229 @@
1
+ import { fetchOpenApiSpec } from "../core/http.js";
2
+ import { getGlobalFlagsHelp } from "./shared.js";
3
+
4
+ const COMMAND_SCHEMA_MAP = {
5
+ "auth.validate": {
6
+ command: "auth validate",
7
+ method: "GET",
8
+ path: "/v1/auth/validate",
9
+ cliFlags: [],
10
+ },
11
+ "auth.info": {
12
+ command: "auth info",
13
+ method: "GET",
14
+ path: "/v1/auth/info",
15
+ cliFlags: [],
16
+ },
17
+ "auth.charges": {
18
+ command: "auth charges",
19
+ method: "GET",
20
+ path: "/v1/auth/charges",
21
+ cliFlags: [
22
+ { flag: "--model-id", apiField: "model_id", required: false },
23
+ { flag: "--charge-type", apiField: "charge_type", required: false },
24
+ { flag: "--user-level", apiField: "user_level", required: false },
25
+ { flag: "--start-at", apiField: "start_at", required: false },
26
+ { flag: "--end-at", apiField: "end_at", required: false },
27
+ { flag: "--limit", apiField: "limit", required: false },
28
+ { flag: "--offset", apiField: "offset", required: false },
29
+ ],
30
+ },
31
+ "health.check": {
32
+ command: "health check",
33
+ method: "GET",
34
+ path: "/v1/health",
35
+ cliFlags: [],
36
+ },
37
+ "tool.catalog": {
38
+ command: "tool catalog",
39
+ method: "GET",
40
+ path: "/v1/tools/catalog",
41
+ cliFlags: [],
42
+ },
43
+ "version.check": {
44
+ command: "version check",
45
+ method: "GET",
46
+ path: "/v1/version/check",
47
+ cliFlags: [
48
+ { flag: "--app-id", apiField: "app_id", required: false },
49
+ { flag: "--platform", apiField: "platform", required: false },
50
+ { flag: "--channel", apiField: "channel", required: false },
51
+ {
52
+ flag: "--current-version",
53
+ apiField: "current_version",
54
+ required: false,
55
+ },
56
+ { flag: "--current-build", apiField: "current_build", required: false },
57
+ ],
58
+ },
59
+ "tool.execute": {
60
+ command: "tool execute",
61
+ method: "POST",
62
+ path: "/v1/tools/execute",
63
+ cliFlags: [
64
+ { flag: "--tool-id", apiField: "tool_id", required: true },
65
+ { flag: "--params", apiField: "params", required: false },
66
+ { flag: "--params-file", apiField: "params", required: false },
67
+ { flag: "--session-id", apiField: "session_id", required: false },
68
+ { flag: "--trace-id", apiField: "trace_id", required: false },
69
+ {
70
+ flag: "--idempotency-key",
71
+ apiField: "idempotency_key",
72
+ required: false,
73
+ },
74
+ ],
75
+ },
76
+ "kb.search": {
77
+ command: "kb search",
78
+ method: "POST",
79
+ path: "/v1/knowledge-base/search",
80
+ cliFlags: [
81
+ { flag: "--query", apiField: "query", required: true },
82
+ { flag: "--top-k", apiField: "top_k", required: false },
83
+ { flag: "--min-score", apiField: "min_score", required: false },
84
+ { flag: "--course-name", apiField: "course_name", required: false },
85
+ ],
86
+ },
87
+ "analysis.creator-profile": {
88
+ command: "analysis creator-profile",
89
+ method: "POST",
90
+ path: "/v1/analysis/douyin/creator-profile",
91
+ cliFlags: [
92
+ { flag: "--profile-url", apiField: "profile_url", required: true },
93
+ { flag: "--video-limit", apiField: "video_limit", required: false },
94
+ ],
95
+ },
96
+ "compliance.check": {
97
+ command: "compliance check",
98
+ method: "POST",
99
+ path: "/v1/compliance/check",
100
+ cliFlags: [
101
+ { flag: "--content", apiField: "content", required: false },
102
+ { flag: "--content-file", apiField: "content", required: false },
103
+ { flag: "--project-id", apiField: "project_id", required: false },
104
+ { flag: "--metadata", apiField: "metadata", required: false },
105
+ { flag: "--metadata-file", apiField: "metadata", required: false },
106
+ ],
107
+ },
108
+ };
109
+
110
+ export function getSchemaHelp() {
111
+ return `Inspect command parameter schema from OpenAPI
112
+
113
+ Usage:
114
+ ycloud schema <command> [flags]
115
+
116
+ Required Flags:
117
+ none
118
+
119
+ Optional Flags:
120
+ none
121
+
122
+ ${getGlobalFlagsHelp()}
123
+
124
+ Examples:
125
+ ycloud schema list --output json
126
+ ycloud schema kb.search --output json
127
+ ycloud schema tool.execute --output text
128
+
129
+ Read on success:
130
+ data.command
131
+ data.api.path
132
+ data.api.request_body
133
+
134
+ Read on error:
135
+ error.code
136
+ error.message
137
+ error.retryable`;
138
+ }
139
+
140
+ function dereferenceSchema(spec, schema) {
141
+ if (!schema) {
142
+ return {};
143
+ }
144
+ if (schema.$ref) {
145
+ const refName = schema.$ref.split("/").pop();
146
+ return spec.components?.schemas?.[refName] || {};
147
+ }
148
+ return schema;
149
+ }
150
+
151
+ function normalizeProperties(properties = {}) {
152
+ return Object.entries(properties).map(([name, value]) => ({
153
+ name,
154
+ type: value.type || value.$ref || "unknown",
155
+ }));
156
+ }
157
+
158
+ export async function executeSchema(context, tokens) {
159
+ const key = tokens[0];
160
+ if (!key) {
161
+ return context.fail(
162
+ "schema",
163
+ "BAD_ARGUMENT",
164
+ "缺少命令标识,例如 kb.search",
165
+ 2,
166
+ false,
167
+ );
168
+ }
169
+
170
+ if (key === "list") {
171
+ return context.ok("schema list", {
172
+ commands: Object.entries(COMMAND_SCHEMA_MAP).map(([itemKey, value]) => ({
173
+ key: itemKey,
174
+ command: value.command,
175
+ method: value.method,
176
+ path: value.path,
177
+ })),
178
+ });
179
+ }
180
+
181
+ const target = COMMAND_SCHEMA_MAP[key];
182
+ if (!target) {
183
+ return context.fail(
184
+ "schema",
185
+ "BAD_ARGUMENT",
186
+ `不支持的 schema 命令: ${key}`,
187
+ 2,
188
+ false,
189
+ );
190
+ }
191
+
192
+ const spec = await fetchOpenApiSpec({
193
+ timeout: context.globalOptions.timeout,
194
+ baseUrl: context.globalOptions.baseUrl,
195
+ });
196
+ const operation =
197
+ spec.paths?.[target.path]?.[target.method.toLowerCase()] || {};
198
+
199
+ let requestBody = {
200
+ required: [],
201
+ properties: [],
202
+ };
203
+
204
+ const bodySchema = dereferenceSchema(
205
+ spec,
206
+ operation.requestBody?.content?.["application/json"]?.schema,
207
+ );
208
+ if (bodySchema && Object.keys(bodySchema).length > 0) {
209
+ requestBody = {
210
+ required: bodySchema.required || [],
211
+ properties: normalizeProperties(bodySchema.properties),
212
+ };
213
+ }
214
+
215
+ const data = {
216
+ command: target.command,
217
+ cli: {
218
+ flags: target.cliFlags,
219
+ },
220
+ api: {
221
+ method: target.method,
222
+ path: target.path,
223
+ summary: operation.summary || null,
224
+ request_body: requestBody,
225
+ },
226
+ };
227
+
228
+ return context.ok(`schema ${key}`, data);
229
+ }
@@ -0,0 +1,30 @@
1
+ export function getGlobalFlagsHelp() {
2
+ return `Global Flags:
3
+ --output string output format: json (default) | text
4
+ --trace-id string optional request trace id
5
+ --timeout int request timeout in milliseconds
6
+ --base-url string override API base URL`;
7
+ }
8
+
9
+ export function parseInteger(value) {
10
+ if (value === undefined) {
11
+ return undefined;
12
+ }
13
+ const parsed = Number.parseInt(String(value), 10);
14
+ return Number.isNaN(parsed) ? undefined : parsed;
15
+ }
16
+
17
+ export function parseFloatNumber(value) {
18
+ if (value === undefined) {
19
+ return undefined;
20
+ }
21
+ const parsed = Number.parseFloat(String(value));
22
+ return Number.isNaN(parsed) ? undefined : parsed;
23
+ }
24
+
25
+ export function parseJsonString(value) {
26
+ if (!value) {
27
+ return undefined;
28
+ }
29
+ return JSON.parse(value);
30
+ }
@@ -0,0 +1,209 @@
1
+ const STATIC_TOOL_REGISTRY = {
2
+ content: {
3
+ title: "content creation tools",
4
+ commands: {
5
+ "knowledge-base-search": {
6
+ toolId: "knowledge_base_search",
7
+ commandName: "content knowledge-base-search",
8
+ description: "Search knowledge base content via wrapped tool",
9
+ requiredFlags: ["query"],
10
+ },
11
+ "copywriting-generate": {
12
+ toolId: "copywriting_generate",
13
+ commandName: "content copywriting-generate",
14
+ description: "Generate short video copywriting",
15
+ requiredFlags: ["task"],
16
+ },
17
+ "douyin-topic-generate": {
18
+ toolId: "douyin_topic_generate",
19
+ commandName: "content douyin-topic-generate",
20
+ description: "Generate Douyin topic ideas",
21
+ requiredFlags: ["prompt"],
22
+ },
23
+ "doubao-image-generate": {
24
+ toolId: "doubao_image_generate",
25
+ commandName: "content doubao-image-generate",
26
+ description: "Generate image assets with Doubao image model",
27
+ requiredFlags: ["prompt"],
28
+ },
29
+ "script-generate": {
30
+ toolId: "script_generate",
31
+ commandName: "content script-generate",
32
+ description: "Generate video script content",
33
+ requiredFlags: ["task", "duration-seconds"],
34
+ },
35
+ "xiaohongshu-content-generate": {
36
+ toolId: "xiaohongshu_content_generate",
37
+ commandName: "content xiaohongshu-content-generate",
38
+ description: "Generate Xiaohongshu content and images",
39
+ requiredFlags: ["prompt"],
40
+ },
41
+ },
42
+ },
43
+ oss: {
44
+ title: "oss tools",
45
+ commands: {
46
+ "temp-policy": {
47
+ toolId: "oss_temp_policy_get",
48
+ commandName: "oss temp-policy",
49
+ description: "Get OSS temporary object policy",
50
+ requiredFlags: [],
51
+ },
52
+ "upload-base64": {
53
+ toolId: "oss_upload_base64",
54
+ commandName: "oss upload-base64",
55
+ description: "Upload Base64 payload to OSS",
56
+ requiredFlags: ["filename", "content-base64"],
57
+ },
58
+ },
59
+ },
60
+ speech: {
61
+ title: "speech tools",
62
+ commands: {
63
+ recognition: {
64
+ toolId: "speech_recognition",
65
+ commandName: "speech recognition",
66
+ description: "Recognize speech from audio file",
67
+ requiredFlags: ["audio-url"],
68
+ },
69
+ synthesis: {
70
+ toolId: "speech_synthesis",
71
+ commandName: "speech synthesis",
72
+ description: "Synthesize speech audio from text",
73
+ requiredFlags: ["text"],
74
+ },
75
+ "disfluency-trim-plan": {
76
+ toolId: "speech_disfluency_trim_plan",
77
+ commandName: "speech disfluency-trim-plan",
78
+ description: "Generate disfluency trim plan for speech",
79
+ requiredFlags: ["audio-url", "script-text"],
80
+ },
81
+ },
82
+ },
83
+ social: {
84
+ title: "social media atomic tools",
85
+ commands: {
86
+ "xiaohongshu-search-notes": {
87
+ toolId: "social_search_notes_v3",
88
+ commandName: "social xiaohongshu-search-notes",
89
+ description: "Search Xiaohongshu notes",
90
+ requiredFlags: ["keyword"],
91
+ aliases: { keywords: "keyword" },
92
+ },
93
+ "xiaohongshu-search-users": {
94
+ toolId: "social_search_users_v1",
95
+ commandName: "social xiaohongshu-search-users",
96
+ description: "Search Xiaohongshu users",
97
+ requiredFlags: ["keyword"],
98
+ aliases: { keywords: "keyword" },
99
+ },
100
+ "xiaohongshu-note-info": {
101
+ toolId: "social_get_note_info_v2",
102
+ commandName: "social xiaohongshu-note-info",
103
+ description: "Get Xiaohongshu note detail",
104
+ requiredFlags: [],
105
+ },
106
+ "douyin-video-search": {
107
+ toolId: "social_get_video_search_results",
108
+ commandName: "social douyin-video-search",
109
+ description: "Search Douyin videos",
110
+ requiredFlags: ["keyword"],
111
+ },
112
+ "douyin-general-search": {
113
+ toolId: "social_get_general_search_results",
114
+ commandName: "social douyin-general-search",
115
+ description: "Run Douyin general search",
116
+ requiredFlags: ["keyword"],
117
+ },
118
+ "douyin-hot-search": {
119
+ toolId: "social_get_hot_search_list",
120
+ commandName: "social douyin-hot-search",
121
+ description: "Get Douyin hot search list",
122
+ requiredFlags: [],
123
+ },
124
+ "douyin-user-info": {
125
+ toolId: "social_get_user_info_by_sec_user_id",
126
+ commandName: "social douyin-user-info",
127
+ description: "Get Douyin user info by sec_user_id",
128
+ requiredFlags: ["sec-user-id"],
129
+ },
130
+ "douyin-homepage-videos": {
131
+ toolId: "social_get_user_homepage_video_data",
132
+ commandName: "social douyin-homepage-videos",
133
+ description: "Get Douyin user homepage videos",
134
+ requiredFlags: ["sec-user-id"],
135
+ },
136
+ "douyin-video-detail": {
137
+ toolId: "social_get_single_video_data_v2",
138
+ commandName: "social douyin-video-detail",
139
+ description: "Get Douyin single video detail",
140
+ requiredFlags: ["aweme-id"],
141
+ },
142
+ },
143
+ },
144
+ };
145
+
146
+ function toKebabCase(value) {
147
+ return value
148
+ .replace(/^social_/, "")
149
+ .replace(/_/g, "-");
150
+ }
151
+
152
+ function toFlagName(value) {
153
+ return value.replace(/_/g, "-");
154
+ }
155
+
156
+ function inferAliases(toolId, schemaProperties) {
157
+ const aliases = {};
158
+ if (toolId === "social_search_notes_v3" || toolId === "social_search_users_v1") {
159
+ aliases.keywords = "keyword";
160
+ }
161
+
162
+ if (schemaProperties.sec_uid && !schemaProperties.sec_user_id) {
163
+ aliases["sec-user-id"] = "sec-uid";
164
+ }
165
+
166
+ if (schemaProperties.url && schemaProperties.share_link) {
167
+ aliases.url = "share-link";
168
+ }
169
+
170
+ return Object.keys(aliases).length > 0 ? aliases : undefined;
171
+ }
172
+
173
+ function buildSocialCommandConfig(item) {
174
+ const properties = item.schema?.properties || {};
175
+ const required = (item.schema?.required || []).map(toFlagName);
176
+ return {
177
+ toolId: item.tool_id,
178
+ commandName: `social ${toKebabCase(item.tool_id)}`,
179
+ description: item.description || `Execute ${item.tool_id}`,
180
+ requiredFlags: required,
181
+ aliases: inferAliases(item.tool_id, properties),
182
+ };
183
+ }
184
+
185
+ export function buildToolRegistry(catalogMap) {
186
+ const registry = structuredClone(STATIC_TOOL_REGISTRY);
187
+ const socialCommands = { ...registry.social.commands };
188
+
189
+ for (const item of catalogMap.values()) {
190
+ if (!item.tool_id?.startsWith("social_")) {
191
+ continue;
192
+ }
193
+ const subcommand = toKebabCase(item.tool_id);
194
+ if (socialCommands[subcommand]) {
195
+ continue;
196
+ }
197
+ socialCommands[subcommand] = buildSocialCommandConfig(item);
198
+ }
199
+
200
+ registry.social.commands = Object.fromEntries(
201
+ Object.entries(socialCommands).sort(([left], [right]) =>
202
+ left.localeCompare(right),
203
+ ),
204
+ );
205
+
206
+ return registry;
207
+ }
208
+
209
+ export const TOOL_REGISTRY = STATIC_TOOL_REGISTRY;