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,146 @@
1
+ import { apiPost } from "../core/http.js";
2
+ import { getToken } from "../core/token.js";
3
+ import { getGlobalFlagsHelp, parseJsonString } from "./shared.js";
4
+
5
+ export function getComplianceHelp() {
6
+ return `Check content compliance risk
7
+
8
+ Usage:
9
+ ycloud compliance check [flags]
10
+
11
+ Required Flags:
12
+ --content string inline content text
13
+ --content-file string UTF-8 text file path
14
+
15
+ Optional Flags:
16
+ --project-id string project id
17
+ --metadata string JSON object string
18
+ --metadata-file string local JSON file path
19
+
20
+ ${getGlobalFlagsHelp()}
21
+
22
+ Examples:
23
+ ycloud compliance check --content "这是一个短视频口播文案测试文本" --output json
24
+ ycloud compliance check --content-file content.txt --output json
25
+
26
+ Read on success:
27
+ data.passed
28
+ data.issues
29
+
30
+ Read on error:
31
+ error.code
32
+ error.message
33
+ error.retryable`;
34
+ }
35
+
36
+ async function requireToken(context) {
37
+ const token = await getToken();
38
+ if (!token) {
39
+ return context.fail(
40
+ context.commandName,
41
+ "TOKEN_MISSING",
42
+ "缺少 YUANCHUANG_API_TOKEN",
43
+ 3,
44
+ false,
45
+ );
46
+ }
47
+ return token;
48
+ }
49
+
50
+ export async function executeComplianceCheck(context, tokens) {
51
+ const options = context.parseCommandFlags(tokens);
52
+ if (!options.content && !options["content-file"]) {
53
+ return context.fail(
54
+ "compliance check",
55
+ "BAD_ARGUMENT",
56
+ "必须提供 --content 或 --content-file",
57
+ 2,
58
+ false,
59
+ );
60
+ }
61
+ if (options.content && options["content-file"]) {
62
+ return context.fail(
63
+ "compliance check",
64
+ "BAD_ARGUMENT",
65
+ "--content 和 --content-file 只能二选一",
66
+ 2,
67
+ false,
68
+ );
69
+ }
70
+ if (options.metadata && options["metadata-file"]) {
71
+ return context.fail(
72
+ "compliance check",
73
+ "BAD_ARGUMENT",
74
+ "--metadata 和 --metadata-file 只能二选一",
75
+ 2,
76
+ false,
77
+ );
78
+ }
79
+
80
+ const token = await requireToken(context);
81
+ if (typeof token !== "string") {
82
+ return token;
83
+ }
84
+
85
+ let content = options.content;
86
+ if (options["content-file"]) {
87
+ const { readFile } = await import("node:fs/promises");
88
+ try {
89
+ content = await readFile(options["content-file"], { encoding: "utf-8" });
90
+ } catch {
91
+ return context.fail(
92
+ "compliance check",
93
+ "BAD_ARGUMENT",
94
+ "--content-file 必须是合法 UTF-8 文本文件",
95
+ 2,
96
+ false,
97
+ );
98
+ }
99
+ }
100
+
101
+ let metadata = undefined;
102
+ if (options.metadata) {
103
+ try {
104
+ metadata = parseJsonString(options.metadata);
105
+ } catch {
106
+ return context.fail(
107
+ "compliance check",
108
+ "BAD_ARGUMENT",
109
+ "--metadata 必须是合法 JSON",
110
+ 2,
111
+ false,
112
+ );
113
+ }
114
+ }
115
+
116
+ if (options["metadata-file"]) {
117
+ const { readFile } = await import("node:fs/promises");
118
+ try {
119
+ metadata = JSON.parse(
120
+ await readFile(options["metadata-file"], { encoding: "utf-8" }),
121
+ );
122
+ } catch {
123
+ return context.fail(
124
+ "compliance check",
125
+ "BAD_ARGUMENT",
126
+ "--metadata-file 必须是合法 JSON 文件",
127
+ 2,
128
+ false,
129
+ );
130
+ }
131
+ }
132
+
133
+ const data = await apiPost("/v1/compliance/check", {
134
+ token,
135
+ traceId: context.globalOptions.traceId,
136
+ timeout: context.globalOptions.timeout,
137
+ baseUrl: context.globalOptions.baseUrl,
138
+ body: {
139
+ project_id: options["project-id"],
140
+ content,
141
+ metadata,
142
+ },
143
+ });
144
+
145
+ return context.ok("compliance check", data);
146
+ }
@@ -0,0 +1,103 @@
1
+ import {
2
+ clearConfigKey,
3
+ getConfigPath,
4
+ maskToken,
5
+ readConfig,
6
+ updateConfig,
7
+ } from "../core/config.js";
8
+ import { getTokenWithSource } from "../core/token.js";
9
+ import { getGlobalFlagsHelp } from "./shared.js";
10
+
11
+ export function getConfigHelp(subcommand) {
12
+ if (subcommand === "set-token") {
13
+ return `Store API token into local CLI config file
14
+
15
+ Usage:
16
+ ycloud config set-token <token>
17
+
18
+ Optional Flags:
19
+ none
20
+
21
+ ${getGlobalFlagsHelp()}
22
+
23
+ Examples:
24
+ ycloud config set-token yc-xxxx
25
+ `;
26
+ }
27
+
28
+ if (subcommand === "get") {
29
+ return `Read current local CLI config
30
+
31
+ Usage:
32
+ ycloud config get
33
+
34
+ Optional Flags:
35
+ none
36
+
37
+ ${getGlobalFlagsHelp()}
38
+ `;
39
+ }
40
+
41
+ if (subcommand === "clear-token") {
42
+ return `Clear stored API token from local CLI config
43
+
44
+ Usage:
45
+ ycloud config clear-token
46
+
47
+ Optional Flags:
48
+ none
49
+
50
+ ${getGlobalFlagsHelp()}
51
+ `;
52
+ }
53
+
54
+ return `Config commands
55
+
56
+ Usage:
57
+ ycloud config <set-token|get|clear-token> [flags]`;
58
+ }
59
+
60
+ export async function executeConfigSetToken(context, tokens) {
61
+ const token = (tokens[0] || "").trim();
62
+ if (!token) {
63
+ return context.fail(
64
+ "config set-token",
65
+ "BAD_ARGUMENT",
66
+ "缺少 token 参数",
67
+ 2,
68
+ false,
69
+ );
70
+ }
71
+
72
+ await updateConfig({ token });
73
+ return context.ok("config set-token", {
74
+ token_present: true,
75
+ token_masked: maskToken(token),
76
+ token_source: "config",
77
+ config_path: getConfigPath(),
78
+ });
79
+ }
80
+
81
+ export async function executeConfigGet(context) {
82
+ const config = await readConfig();
83
+ const { token, source } = await getTokenWithSource();
84
+ return context.ok("config get", {
85
+ config_path: getConfigPath(),
86
+ token_present: Boolean(token),
87
+ token_source: source,
88
+ token_masked: maskToken(token),
89
+ base_url: config.base_url || null,
90
+ default_output: config.default_output || null,
91
+ timeout: config.timeout || null,
92
+ });
93
+ }
94
+
95
+ export async function executeConfigClearToken(context) {
96
+ await clearConfigKey("token");
97
+ return context.ok("config clear-token", {
98
+ token_present: false,
99
+ token_source: null,
100
+ token_masked: null,
101
+ config_path: getConfigPath(),
102
+ });
103
+ }
@@ -0,0 +1,35 @@
1
+ import { apiGet } from "../core/http.js";
2
+ import { getGlobalFlagsHelp } from "./shared.js";
3
+
4
+ export function getHealthHelp() {
5
+ return `Check cloud API health
6
+
7
+ Usage:
8
+ ycloud health check [flags]
9
+
10
+ Optional Flags:
11
+ none
12
+
13
+ ${getGlobalFlagsHelp()}
14
+
15
+ Examples:
16
+ ycloud health check --output json
17
+
18
+ Read on success:
19
+ success
20
+ data
21
+
22
+ Read on error:
23
+ error.code
24
+ error.message
25
+ error.retryable`;
26
+ }
27
+
28
+ export async function executeHealthCheck(context) {
29
+ const data = await apiGet("/v1/health", {
30
+ traceId: context.globalOptions.traceId,
31
+ timeout: context.globalOptions.timeout,
32
+ baseUrl: context.globalOptions.baseUrl,
33
+ });
34
+ return context.ok("health check", data);
35
+ }
@@ -0,0 +1,381 @@
1
+ import {
2
+ executeAuthCharges,
3
+ executeAuthInfo,
4
+ executeAuthStatus,
5
+ executeAuthValidate,
6
+ getAuthHelp,
7
+ } from "./auth.js";
8
+ import {
9
+ executeAnalysisCreatorProfile,
10
+ getAnalysisHelp,
11
+ } from "./analysis.js";
12
+ import {
13
+ executeComplianceCheck,
14
+ getComplianceHelp,
15
+ } from "./compliance.js";
16
+ import {
17
+ buildExposedCommands,
18
+ executeCommandsDescribe,
19
+ executeCommandsList,
20
+ getCommandsHelp,
21
+ } from "./commands.js";
22
+ import {
23
+ executeConfigClearToken,
24
+ executeConfigGet,
25
+ executeConfigSetToken,
26
+ getConfigHelp,
27
+ } from "./config.js";
28
+ import { executeHealthCheck, getHealthHelp } from "./health.js";
29
+ import { executeKbSearch, getKbHelp } from "./kb.js";
30
+ import { executeSchema, getSchemaHelp } from "./schema.js";
31
+ import { buildToolRegistry } from "./tool-registry.js";
32
+ import {
33
+ createDomainHelp,
34
+ createToolCommandHelp,
35
+ executeRegisteredTool,
36
+ } from "./tool-runner.js";
37
+ import { executeToolCatalog, executeToolExecute, getToolHelp } from "./tool.js";
38
+ import { executeVersionCheck, getVersionHelp } from "./version.js";
39
+
40
+ const TOP_LEVEL_HELP = `YuanFlow CLI local command tool for Agent-friendly cloud API access.
41
+
42
+ USAGE:
43
+ ycloud <command> [subcommand] [flags]
44
+
45
+ EXAMPLES:
46
+ ycloud auth validate --output json
47
+ ycloud tool catalog --output json
48
+ ycloud kb search --query "短视频脚本结构" --top-k 5 --output json
49
+
50
+ GLOBAL FLAGS:
51
+ --output <string> output format: json (default) | text
52
+ --trace-id <string> optional request trace id
53
+ --timeout <int> request timeout in milliseconds
54
+ --base-url <string> override API base URL
55
+ -h, --help show help
56
+
57
+ COMMANDS:
58
+ auth token validation and account info
59
+ health service health check
60
+ version local CLI version info
61
+ tool tool catalog and tool execution
62
+ kb knowledge base search
63
+ analysis creator analysis commands
64
+ compliance compliance checking commands
65
+ config local CLI config management
66
+ commands list and describe exposed commands
67
+ schema inspect command parameter schema from OpenAPI
68
+ content wrapped content generation tools
69
+ social social media atomic tools
70
+ oss oss helper tools
71
+ speech speech helper tools
72
+ `;
73
+
74
+ function parseArgs(rawArgs) {
75
+ const tokens = [...rawArgs];
76
+ const options = {
77
+ output: "json",
78
+ traceId: undefined,
79
+ timeout: undefined,
80
+ baseUrl: undefined,
81
+ help: false,
82
+ };
83
+ const positional = [];
84
+
85
+ for (let index = 0; index < tokens.length; index += 1) {
86
+ const token = tokens[index];
87
+ if (token === "-h" || token === "--help") {
88
+ options.help = true;
89
+ continue;
90
+ }
91
+ if (token === "--output") {
92
+ options.output = tokens[index + 1] || "json";
93
+ index += 1;
94
+ continue;
95
+ }
96
+ if (token === "--trace-id") {
97
+ options.traceId = tokens[index + 1];
98
+ index += 1;
99
+ continue;
100
+ }
101
+ if (token === "--timeout") {
102
+ options.timeout = tokens[index + 1];
103
+ index += 1;
104
+ continue;
105
+ }
106
+ if (token === "--base-url") {
107
+ options.baseUrl = tokens[index + 1];
108
+ index += 1;
109
+ continue;
110
+ }
111
+ positional.push(token);
112
+ }
113
+
114
+ return { positional, options };
115
+ }
116
+
117
+ function getCommandName(positional) {
118
+ return `${positional[0] || ""} ${positional[1] || ""}`.trim() || "unknown";
119
+ }
120
+
121
+ function asJson(command, success, data, error = null, meta = {}, warnings = []) {
122
+ return {
123
+ success,
124
+ command,
125
+ data,
126
+ warnings,
127
+ error,
128
+ meta,
129
+ };
130
+ }
131
+
132
+ function renderJson(payload) {
133
+ return `${JSON.stringify(payload)}\n`;
134
+ }
135
+
136
+ function renderTextPayload(payload) {
137
+ const lines = [
138
+ `command: ${payload.command}`,
139
+ `success: ${payload.success}`,
140
+ ];
141
+
142
+ if (payload.error) {
143
+ lines.push(`error.code: ${payload.error.code}`);
144
+ lines.push(`error.message: ${payload.error.message}`);
145
+ lines.push(`error.retryable: ${payload.error.retryable}`);
146
+ }
147
+
148
+ if (payload.data !== null && payload.data !== undefined) {
149
+ lines.push("data:");
150
+ lines.push(JSON.stringify(payload.data, null, 2));
151
+ }
152
+
153
+ if (payload.meta && Object.keys(payload.meta).length > 0) {
154
+ lines.push("meta:");
155
+ lines.push(JSON.stringify(payload.meta, null, 2));
156
+ }
157
+
158
+ return `${lines.join("\n")}\n`;
159
+ }
160
+
161
+ function renderOutput(payload, globalOptions) {
162
+ if (globalOptions.output === "text") {
163
+ return renderTextPayload(payload);
164
+ }
165
+ return renderJson(payload);
166
+ }
167
+
168
+ function fail(command, code, message, exitCode = 1, retryable = false) {
169
+ const payload = asJson(command, false, null, { code, message, retryable }, {});
170
+ return {
171
+ code: exitCode,
172
+ output: renderOutput(payload, this?.globalOptions || { output: "json" }),
173
+ };
174
+ }
175
+
176
+ function ok(command, data, meta = {}) {
177
+ const payload = asJson(command, true, data, null, meta);
178
+ return {
179
+ code: 0,
180
+ output: renderOutput(payload, this?.globalOptions || { output: "json" }),
181
+ };
182
+ }
183
+
184
+ function text(commandHelp) {
185
+ return {
186
+ code: 0,
187
+ output: `${commandHelp}\n`,
188
+ };
189
+ }
190
+
191
+ function parseCommandFlags(tokens) {
192
+ const options = {};
193
+ for (let index = 0; index < tokens.length; index += 1) {
194
+ const token = tokens[index];
195
+ if (!token.startsWith("--")) {
196
+ continue;
197
+ }
198
+ const name = token.slice(2);
199
+ const next = tokens[index + 1];
200
+ if (!next || next.startsWith("--")) {
201
+ options[name] = true;
202
+ continue;
203
+ }
204
+ options[name] = next;
205
+ index += 1;
206
+ }
207
+ return options;
208
+ }
209
+
210
+ let cachedToolCatalogMap = null;
211
+ let cachedToolRegistry = null;
212
+
213
+ async function getToolCatalogMap() {
214
+ if (cachedToolCatalogMap) {
215
+ return cachedToolCatalogMap;
216
+ }
217
+ const { readFile } = await import("node:fs/promises");
218
+ const { join } = await import("node:path");
219
+ const { fileURLToPath } = await import("node:url");
220
+ const currentDir = fileURLToPath(new URL(".", import.meta.url));
221
+ const jsonText = await readFile(
222
+ join(currentDir, "..", "resources", "tool_catalog_full.json"),
223
+ "utf8",
224
+ );
225
+ const raw = JSON.parse(jsonText);
226
+ const items = raw.data?.data?.items || [];
227
+ cachedToolCatalogMap = new Map(items.map((item) => [item.tool_id, item]));
228
+ return cachedToolCatalogMap;
229
+ }
230
+
231
+ async function getToolRegistry() {
232
+ if (cachedToolRegistry) {
233
+ return cachedToolRegistry;
234
+ }
235
+ const catalogMap = await getToolCatalogMap();
236
+ cachedToolRegistry = buildToolRegistry(catalogMap);
237
+ return cachedToolRegistry;
238
+ }
239
+
240
+ function buildContext(globalOptions, commandName) {
241
+ return {
242
+ globalOptions,
243
+ commandName,
244
+ ok: ok.bind({ globalOptions }),
245
+ fail: fail.bind({ globalOptions }),
246
+ parseCommandFlags,
247
+ };
248
+ }
249
+
250
+ export async function executeCommand(rawArgs) {
251
+ const { positional, options } = parseArgs(rawArgs);
252
+ const commandName = getCommandName(positional);
253
+ const context = buildContext(options, commandName);
254
+ const toolRegistry = await getToolRegistry();
255
+ const catalogMap = await getToolCatalogMap();
256
+ const exposedCommands = buildExposedCommands(toolRegistry, catalogMap);
257
+ const allowedToolIds = new Set(
258
+ exposedCommands
259
+ .filter((item) => item.kind === "tool")
260
+ .map((item) => item.tool_id),
261
+ );
262
+
263
+ if (positional.length === 0 || options.help) {
264
+ if (positional.length === 0) {
265
+ return text(TOP_LEVEL_HELP);
266
+ }
267
+ if (positional[0] === "auth") {
268
+ return text(getAuthHelp(positional[1]));
269
+ }
270
+ if (positional[0] === "health") {
271
+ return text(getHealthHelp(positional[1]));
272
+ }
273
+ if (positional[0] === "version") {
274
+ return text(getVersionHelp(positional[1]));
275
+ }
276
+ if (positional[0] === "tool") {
277
+ return text(getToolHelp(positional[1]));
278
+ }
279
+ if (positional[0] === "kb") {
280
+ return text(getKbHelp(positional[1]));
281
+ }
282
+ if (positional[0] === "analysis") {
283
+ return text(getAnalysisHelp(positional[1]));
284
+ }
285
+ if (positional[0] === "compliance") {
286
+ return text(getComplianceHelp(positional[1]));
287
+ }
288
+ if (positional[0] === "config") {
289
+ return text(getConfigHelp(positional[1]));
290
+ }
291
+ if (positional[0] === "commands") {
292
+ return text(getCommandsHelp(positional[1]));
293
+ }
294
+ if (positional[0] === "schema") {
295
+ return text(getSchemaHelp());
296
+ }
297
+ if (toolRegistry[positional[0]]) {
298
+ if (positional[1]) {
299
+ const config = toolRegistry[positional[0]].commands[positional[1]];
300
+ if (config) {
301
+ return text(
302
+ createToolCommandHelp(
303
+ positional[0],
304
+ positional[1],
305
+ config,
306
+ catalogMap.get(config.toolId),
307
+ ),
308
+ );
309
+ }
310
+ }
311
+ return text(createDomainHelp(positional[0], toolRegistry[positional[0]], catalogMap));
312
+ }
313
+ return text(TOP_LEVEL_HELP);
314
+ }
315
+
316
+ if (positional[0] === "auth" && positional[1] === "info") {
317
+ return executeAuthInfo(context, positional.slice(2));
318
+ }
319
+ if (positional[0] === "auth" && positional[1] === "validate") {
320
+ return executeAuthValidate(context, positional.slice(2));
321
+ }
322
+ if (positional[0] === "auth" && positional[1] === "charges") {
323
+ return executeAuthCharges(context, positional.slice(2));
324
+ }
325
+ if (positional[0] === "auth" && positional[1] === "status") {
326
+ return executeAuthStatus(context, positional.slice(2));
327
+ }
328
+ if (positional[0] === "health" && positional[1] === "check") {
329
+ return executeHealthCheck(context, positional.slice(2));
330
+ }
331
+ if (positional[0] === "version" && positional[1] === "check") {
332
+ return executeVersionCheck(context, positional.slice(2));
333
+ }
334
+ if (positional[0] === "tool" && positional[1] === "catalog") {
335
+ return executeToolCatalog(context, positional.slice(2));
336
+ }
337
+ if (positional[0] === "tool" && positional[1] === "execute") {
338
+ return executeToolExecute(context, positional.slice(2), allowedToolIds);
339
+ }
340
+ if (positional[0] === "kb" && positional[1] === "search") {
341
+ return executeKbSearch(context, positional.slice(2));
342
+ }
343
+ if (
344
+ positional[0] === "analysis" &&
345
+ positional[1] === "creator-profile"
346
+ ) {
347
+ return executeAnalysisCreatorProfile(context, positional.slice(2));
348
+ }
349
+ if (positional[0] === "compliance" && positional[1] === "check") {
350
+ return executeComplianceCheck(context, positional.slice(2));
351
+ }
352
+ if (positional[0] === "config" && positional[1] === "set-token") {
353
+ return executeConfigSetToken(context, positional.slice(2));
354
+ }
355
+ if (positional[0] === "config" && positional[1] === "get") {
356
+ return executeConfigGet(context, positional.slice(2));
357
+ }
358
+ if (positional[0] === "config" && positional[1] === "clear-token") {
359
+ return executeConfigClearToken(context, positional.slice(2));
360
+ }
361
+ if (positional[0] === "commands" && positional[1] === "list") {
362
+ return executeCommandsList(context, exposedCommands);
363
+ }
364
+ if (positional[0] === "commands" && positional[1] === "describe") {
365
+ return executeCommandsDescribe(context, positional.slice(2), exposedCommands);
366
+ }
367
+ if (positional[0] === "schema") {
368
+ return executeSchema(context, positional.slice(1));
369
+ }
370
+ if (toolRegistry[positional[0]]) {
371
+ return executeRegisteredTool(
372
+ context,
373
+ positional.slice(1),
374
+ positional[0],
375
+ toolRegistry[positional[0]],
376
+ catalogMap,
377
+ );
378
+ }
379
+
380
+ return fail(commandName, "UNKNOWN_COMMAND", "未知命令", 1, false);
381
+ }