@rishiqing/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.
package/dist/cli.js ADDED
@@ -0,0 +1,1823 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { Command, InvalidArgumentError, Option } from "commander";
5
+ import { readFile as readFile2 } from "fs/promises";
6
+ import readline from "readline/promises";
7
+ import { stdin, stdout } from "process";
8
+
9
+ // src/core/config.ts
10
+ import { existsSync } from "fs";
11
+ import { mkdir, readFile, writeFile } from "fs/promises";
12
+ import os from "os";
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ import { z } from "zod";
16
+ var profileSchema = z.object({
17
+ apiKey: z.string().min(1)
18
+ });
19
+ var configSchema = z.object({
20
+ activeProfile: z.string().min(1),
21
+ profiles: z.record(z.string(), profileSchema)
22
+ });
23
+ var appConfigSchema = z.object({
24
+ baseUrl: z.string().min(1)
25
+ });
26
+ var APP_CONFIG_FILE = "rsq-cli.config.json";
27
+ function getConfigDir() {
28
+ const xdg = process.env.XDG_CONFIG_HOME;
29
+ if (xdg) {
30
+ return path.join(xdg, "rsq-cli");
31
+ }
32
+ return path.join(os.homedir(), ".config", "rsq-cli");
33
+ }
34
+ function getConfigPath() {
35
+ return path.join(getConfigDir(), "config.json");
36
+ }
37
+ function getAppConfigPath() {
38
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
39
+ const candidates = [
40
+ path.resolve(moduleDir, "../", APP_CONFIG_FILE),
41
+ path.resolve(moduleDir, "../../", APP_CONFIG_FILE),
42
+ path.resolve(process.cwd(), APP_CONFIG_FILE)
43
+ ];
44
+ const found = candidates.find((candidate) => existsSync(candidate));
45
+ if (!found) {
46
+ throw new Error(`Missing ${APP_CONFIG_FILE}. Checked: ${candidates.join(", ")}`);
47
+ }
48
+ return found;
49
+ }
50
+ async function readAppConfig() {
51
+ const file = await readFile(getAppConfigPath(), "utf8");
52
+ return appConfigSchema.parse(JSON.parse(file));
53
+ }
54
+ async function readConfig() {
55
+ try {
56
+ const file = await readFile(getConfigPath(), "utf8");
57
+ return configSchema.parse(JSON.parse(file));
58
+ } catch (error) {
59
+ if (error.code === "ENOENT") {
60
+ return null;
61
+ }
62
+ throw error;
63
+ }
64
+ }
65
+ async function writeConfig(config) {
66
+ await mkdir(getConfigDir(), { recursive: true });
67
+ await writeFile(getConfigPath(), `${JSON.stringify(config, null, 2)}
68
+ `, "utf8");
69
+ }
70
+ async function initConfig(apiKey) {
71
+ const config = {
72
+ activeProfile: "default",
73
+ profiles: {
74
+ default: {
75
+ apiKey
76
+ }
77
+ }
78
+ };
79
+ await writeConfig(config);
80
+ return config;
81
+ }
82
+ async function requireActiveProfile() {
83
+ const config = await readConfig();
84
+ if (!config) {
85
+ throw new Error("Missing config. Run `rsq-cli config init` first.");
86
+ }
87
+ const profile = config.profiles[config.activeProfile];
88
+ if (!profile) {
89
+ throw new Error(`Active profile "${config.activeProfile}" is missing.`);
90
+ }
91
+ return profile;
92
+ }
93
+
94
+ // src/core/http.ts
95
+ async function executeRequest(shape) {
96
+ const profile = await requireActiveProfile();
97
+ const appConfig = await readAppConfig();
98
+ const url = new URL(shape.path, ensureTrailingSlash(appConfig.baseUrl));
99
+ for (const [key, value] of Object.entries(shape.query ?? {})) {
100
+ if (value !== void 0) {
101
+ url.searchParams.set(key, value);
102
+ }
103
+ }
104
+ const headers = {
105
+ "X-Rsq-Api-key": profile.apiKey
106
+ };
107
+ let body;
108
+ if (shape.body !== void 0) {
109
+ headers["Content-Type"] = "application/json";
110
+ body = JSON.stringify(shape.body);
111
+ }
112
+ const response = await fetch(url, {
113
+ method: shape.method,
114
+ headers,
115
+ body
116
+ });
117
+ const text = await response.text();
118
+ const payload = tryParseJson(text);
119
+ if (!response.ok) {
120
+ throw new Error(
121
+ `HTTP ${response.status} ${response.statusText}: ${typeof payload === "string" ? payload : JSON.stringify(payload)}`
122
+ );
123
+ }
124
+ return payload;
125
+ }
126
+ function ensureTrailingSlash(input) {
127
+ return input.endsWith("/") ? input : `${input}/`;
128
+ }
129
+ function tryParseJson(text) {
130
+ if (!text) {
131
+ return null;
132
+ }
133
+ try {
134
+ return JSON.parse(text);
135
+ } catch {
136
+ return text;
137
+ }
138
+ }
139
+
140
+ // src/core/output.ts
141
+ function resolveOutputMode(options) {
142
+ if (options.raw) {
143
+ return "raw";
144
+ }
145
+ if (options.table) {
146
+ return "table";
147
+ }
148
+ if (options.json) {
149
+ return "json";
150
+ }
151
+ return "text";
152
+ }
153
+ function printOutput(payload, options) {
154
+ const mode = resolveOutputMode(options);
155
+ if (mode === "json" || mode === "raw") {
156
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}
157
+ `);
158
+ return;
159
+ }
160
+ if (mode === "table") {
161
+ const rendered = renderTable(payload);
162
+ process.stdout.write(`${rendered}
163
+ `);
164
+ return;
165
+ }
166
+ if (typeof payload === "string") {
167
+ process.stdout.write(`${payload}
168
+ `);
169
+ return;
170
+ }
171
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}
172
+ `);
173
+ }
174
+ function renderTable(payload) {
175
+ if (!Array.isArray(payload) || payload.length === 0) {
176
+ return JSON.stringify(payload, null, 2);
177
+ }
178
+ if (payload.some((item) => typeof item !== "object" || item === null || Array.isArray(item))) {
179
+ return JSON.stringify(payload, null, 2);
180
+ }
181
+ const rows = payload;
182
+ const columns = Array.from(new Set(rows.flatMap((row) => Object.keys(row))));
183
+ const widths = /* @__PURE__ */ new Map();
184
+ for (const column of columns) {
185
+ widths.set(
186
+ column,
187
+ Math.max(column.length, ...rows.map((row) => stringifyCell(row[column]).length))
188
+ );
189
+ }
190
+ const separator = columns.map((column) => "-".repeat(widths.get(column) ?? column.length)).join(" ");
191
+ const header = columns.map((column) => column.padEnd(widths.get(column) ?? column.length)).join(" ");
192
+ const lines = rows.map(
193
+ (row) => columns.map((column) => stringifyCell(row[column]).padEnd(widths.get(column) ?? column.length)).join(" ")
194
+ );
195
+ return [header, separator, ...lines].join("\n");
196
+ }
197
+ function stringifyCell(value) {
198
+ if (value === null || value === void 0) {
199
+ return "";
200
+ }
201
+ if (typeof value === "string") {
202
+ return value;
203
+ }
204
+ return JSON.stringify(value);
205
+ }
206
+
207
+ // src/core/openapi.ts
208
+ import { existsSync as existsSync2, readFileSync } from "fs";
209
+ import path2 from "path";
210
+ import { fileURLToPath as fileURLToPath2 } from "url";
211
+ var operationIdOverrides = {
212
+ "GET /rsq/api/v1/workflow/process-infos/list/{flowApplicationId}": "listProcessInfosByFlowApplicationId",
213
+ "GET /rsq/api/v1/projects": "listProjects"
214
+ };
215
+ var cachedOperations = null;
216
+ function loadOperations() {
217
+ if (cachedOperations) {
218
+ return cachedOperations;
219
+ }
220
+ const documentPath = resolveOpenApiPath();
221
+ const document = JSON.parse(readFileSync(documentPath, "utf8"));
222
+ const operations = [];
223
+ for (const [apiPath, item] of Object.entries(document.paths ?? {})) {
224
+ for (const [method, rawOperation] of Object.entries(item)) {
225
+ const normalizedMethod = method.toUpperCase();
226
+ if (!["GET", "POST", "PUT", "DELETE", "PATCH"].includes(normalizedMethod)) {
227
+ continue;
228
+ }
229
+ const operation = rawOperation;
230
+ const operationId = resolveOperationId(apiPath, normalizedMethod, operation.operationId);
231
+ const resolvedParameters = (operation.parameters ?? []).filter((parameter) => parameter.name !== "X-Rsq-Api-key").map((parameter) => ({
232
+ name: parameter.name,
233
+ in: parameter.in,
234
+ required: Boolean(parameter.required),
235
+ description: parameter.description,
236
+ schemaType: parameter.schema?.type
237
+ }));
238
+ operations.push({
239
+ module: (operation.tags?.[0] ?? "unknown").toLowerCase(),
240
+ method: normalizedMethod,
241
+ path: apiPath,
242
+ operationId,
243
+ summary: operation.summary ?? "",
244
+ description: operation.description ?? operation.summary ?? "",
245
+ parameters: resolvedParameters,
246
+ hasRequestBody: Boolean(operation.requestBody),
247
+ requestBodySchema: extractRequestBodySchema(operation.requestBody)
248
+ });
249
+ }
250
+ }
251
+ cachedOperations = operations.sort((left, right) => left.operationId.localeCompare(right.operationId));
252
+ return cachedOperations;
253
+ }
254
+ function extractRequestBodySchema(requestBody) {
255
+ const schema = requestBody?.content?.["application/json"]?.schema;
256
+ if (!schema) {
257
+ return void 0;
258
+ }
259
+ return normalizeJsonSchema(schema);
260
+ }
261
+ function normalizeJsonSchema(schema) {
262
+ return {
263
+ type: schema.type,
264
+ description: schema.description,
265
+ required: schema.required,
266
+ properties: schema.properties ? Object.fromEntries(
267
+ Object.entries(schema.properties).map(([key, value]) => [key, normalizeJsonSchema(value)])
268
+ ) : void 0,
269
+ items: schema.items ? normalizeJsonSchema(schema.items) : void 0
270
+ };
271
+ }
272
+ function resolveOperationId(apiPath, method, operationId) {
273
+ const override = operationIdOverrides[`${method} ${apiPath}`];
274
+ if (override) {
275
+ return override;
276
+ }
277
+ return operationId ?? `${method}:${apiPath}`;
278
+ }
279
+ function resolveOpenApiPath() {
280
+ const moduleDir = path2.dirname(fileURLToPath2(import.meta.url));
281
+ const candidates = [
282
+ path2.resolve(moduleDir, "../rishiqing.openapi.json"),
283
+ path2.resolve(moduleDir, "../../rishiqing.openapi.json"),
284
+ path2.resolve(process.cwd(), "rishiqing.openapi.json")
285
+ ];
286
+ const found = candidates.find((candidate) => existsSync2(candidate));
287
+ if (!found) {
288
+ throw new Error(`Unable to locate rishiqing.openapi.json. Checked: ${candidates.join(", ")}`);
289
+ }
290
+ return found;
291
+ }
292
+
293
+ // src/registry/overrides.ts
294
+ var moduleOrder = ["datasheet", "workflow", "contacts", "project", "task"];
295
+ var moduleDescriptions = {
296
+ datasheet: "Datasheet APIs",
297
+ workflow: "Workflow APIs",
298
+ contacts: "Contacts APIs",
299
+ project: "Project APIs",
300
+ task: "Task APIs"
301
+ };
302
+ var operationOverrides = {
303
+ listTeamDatasheets: {
304
+ aliases: ["list-datasheets"],
305
+ keywords: ["datasheet", "table", "list team datasheets"]
306
+ },
307
+ getDatasheetFields: {
308
+ aliases: ["get-fields"],
309
+ keywords: ["datasheet fields", "column definitions"]
310
+ },
311
+ getDatasheetRecords: {
312
+ aliases: ["list-records"],
313
+ keywords: ["datasheet records", "rows", "query rows"]
314
+ },
315
+ createDatasheetRecords: {
316
+ aliases: ["create-records"],
317
+ keywords: ["create datasheet record", "insert rows"]
318
+ },
319
+ updateDatasheetRecords: {
320
+ aliases: ["update-records"],
321
+ keywords: ["update datasheet record", "modify rows"]
322
+ },
323
+ deleteDatasheetRecords: {
324
+ aliases: ["delete-records"],
325
+ keywords: ["delete datasheet record", "remove rows"]
326
+ },
327
+ listFlowApplicationTemplates: {
328
+ aliases: ["list-templates"],
329
+ keywords: ["workflow templates", "flow templates", "\u6D41\u7A0B\u6A21\u677F", "\u5BA1\u6279\u6A21\u677F", "\u6A21\u677F\u5217\u8868"]
330
+ },
331
+ listFlowApplications: {
332
+ aliases: ["list-apps"],
333
+ keywords: ["list workflow applications", "list flow apps", "\u6D41\u7A0B\u5E94\u7528\u5217\u8868", "\u5BA1\u6279\u6D41\u7A0B\u5217\u8868"]
334
+ },
335
+ listProcessInfosByFlowApplicationId: {
336
+ aliases: ["list-processes"],
337
+ keywords: ["\u83B7\u53D6\u5DF2\u53D1\u8D77\u7684\u6D41\u7A0B\u5217\u8868", "\u6D41\u7A0B\u5B9E\u4F8B\u5217\u8868", "\u5DF2\u53D1\u8D77\u6D41\u7A0B\u5217\u8868", "\u8FD0\u884C\u4E2D\u7684\u6D41\u7A0B\u5217\u8868", "list process infos"]
338
+ },
339
+ createFlowApplication: {
340
+ aliases: ["create-app"],
341
+ keywords: ["create workflow application", "new flow app", "\u521B\u5EFA\u6D41\u7A0B\u5E94\u7528", "\u65B0\u5EFA\u6D41\u7A0B\u5E94\u7528", "\u65B0\u5EFA\u5BA1\u6279\u6D41\u7A0B"]
342
+ },
343
+ createStepInfo: {
344
+ aliases: ["create-step"],
345
+ keywords: ["\u521B\u5EFA\u6B65\u9AA4\u914D\u7F6E", "\u65B0\u589E\u6B65\u9AA4", "\u65B0\u5EFA\u6B65\u9AA4", "\u6D41\u7A0B\u6B65\u9AA4", "\u5BA1\u6279\u6B65\u9AA4", "step config"]
346
+ },
347
+ updateStepInfo: {
348
+ aliases: ["update-step"],
349
+ keywords: ["\u66F4\u65B0\u6B65\u9AA4\u914D\u7F6E", "\u4FEE\u6539\u6B65\u9AA4\u914D\u7F6E", "\u7F16\u8F91\u6B65\u9AA4", "\u4FEE\u6539\u6B65\u9AA4\u540D\u79F0", "step config"]
350
+ },
351
+ createForm: {
352
+ aliases: ["create-form"],
353
+ keywords: ["\u521B\u5EFA\u8868\u5355\u914D\u7F6E", "\u65B0\u589E\u8868\u5355", "\u65B0\u5EFA\u8868\u5355", "\u8868\u5355\u7ED3\u6784", "\u8868\u5355\u5B57\u6BB5\u5B9A\u4E49", "workflow form"]
354
+ },
355
+ updateForm: {
356
+ aliases: ["update-form"],
357
+ keywords: ["\u66F4\u65B0\u8868\u5355\u914D\u7F6E", "\u4FEE\u6539\u8868\u5355\u914D\u7F6E", "\u7F16\u8F91\u8868\u5355", "\u4FEE\u6539\u8868\u5355\u5B57\u6BB5\u5B9A\u4E49", "workflow form"]
358
+ },
359
+ createAuditInfo: {
360
+ aliases: ["create-audit"],
361
+ keywords: ["\u521B\u5EFA\u5BA1\u6838\u9879\u914D\u7F6E", "\u65B0\u589E\u5BA1\u6838\u9879", "\u65B0\u5EFA\u5BA1\u6838\u8282\u70B9", "\u6DFB\u52A0\u5BA1\u6838\u4EBA\u914D\u7F6E", "audit config"]
362
+ },
363
+ updateAuditInfo: {
364
+ aliases: ["update-audit"],
365
+ keywords: ["\u66F4\u65B0\u5BA1\u6838\u9879\u914D\u7F6E", "\u4FEE\u6539\u5BA1\u6838\u9879\u914D\u7F6E", "\u7F16\u8F91\u5BA1\u6838\u8282\u70B9", "\u4FEE\u6539\u5BA1\u6838\u4EBA\u914D\u7F6E", "audit config"]
366
+ },
367
+ updateProcessInfoStep: {
368
+ aliases: ["update-running-step", "set-running-step-executor"],
369
+ keywords: [
370
+ "\u4FEE\u6539\u8FD0\u884C\u4E2D\u7684\u6B65\u9AA4\u5C5E\u6027",
371
+ "\u4FEE\u6539\u8FDB\u884C\u4E2D\u6B65\u9AA4\u7684\u6267\u884C\u4EBA",
372
+ "\u4FEE\u6539\u6B65\u9AA4\u6267\u884C\u4EBA",
373
+ "\u8C03\u6574\u6B65\u9AA4\u6267\u884C\u4EBA",
374
+ "\u8BBE\u7F6E\u6B65\u9AA4\u6267\u884C\u4EBA",
375
+ "\u8FD0\u884C\u4E2D\u6B65\u9AA4\u6267\u884C\u4EBA",
376
+ "\u6B65\u9AA4\u6267\u884C\u4EBA",
377
+ "\u6B65\u9AA4\u9650\u5B9A\u65F6\u95F4",
378
+ "deadTime",
379
+ "userIds"
380
+ ],
381
+ antiKeywords: ["\u5B57\u6BB5\u503C", "\u8868\u5355\u5B57\u6BB5", "fieldValue", "fieldId"]
382
+ },
383
+ updateProcessInfoAudit: {
384
+ aliases: ["update-running-audit", "set-running-audit-users"],
385
+ keywords: ["\u4FEE\u6539\u8FD0\u884C\u4E2D\u7684\u5BA1\u6838\u9879\u5C5E\u6027", "\u4FEE\u6539\u5BA1\u6838\u4EBA", "\u8C03\u6574\u5BA1\u6838\u4EBA", "\u8FD0\u884C\u4E2D\u5BA1\u6838\u4EBA", "audit user"],
386
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u5B57\u6BB5\u503C", "\u8868\u5355\u5B57\u6BB5", "\u6B65\u9AA4\u72B6\u6001"],
387
+ description: "\u8C03\u6574\u8FD0\u884C\u4E2D\u5BA1\u6838\u9879\u7684\u5BA1\u6838\u4EBA\u3002\u8FD9\u4E2A\u63A5\u53E3\u7528\u4E8E\u5BA1\u6838\u9879\uFF0C\u4E0D\u7528\u4E8E\u6B65\u9AA4\u72B6\u6001\u6D41\u8F6C\u3002",
388
+ exposure: "internal"
389
+ },
390
+ updateProcessInfoStepField: {
391
+ aliases: ["update-step-field-value", "set-step-field-value"],
392
+ keywords: [
393
+ "\u4FEE\u6539\u6B65\u9AA4\u8868\u5355\u4E2D\u7684\u5B57\u6BB5\u503C",
394
+ "\u4FEE\u6539\u5B57\u6BB5\u503C",
395
+ "\u8BBE\u7F6E\u5B57\u6BB5\u503C",
396
+ "\u586B\u5199\u5B57\u6BB5",
397
+ "\u66F4\u65B0\u8868\u5355\u5B57\u6BB5\u503C",
398
+ "\u6B65\u9AA4\u5B57\u6BB5\u503C",
399
+ "\u8868\u5355\u5B57\u6BB5\u503C",
400
+ "fieldValue"
401
+ ],
402
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u5BA1\u6838\u4EBA", "\u8D1F\u8D23\u4EBA", "\u53C2\u4E0E\u4EBA", "\u6210\u5458", "deadTime", "\u9650\u5B9A\u65F6\u95F4"],
403
+ exposure: "internal"
404
+ },
405
+ getProcessInfoStep: {
406
+ aliases: ["get-running-step"],
407
+ keywords: ["\u67E5\u8BE2\u8FD0\u884C\u4E2D\u6B65\u9AA4", "\u67E5\u770B\u6B65\u9AA4\u8BE6\u60C5", "\u67E5\u770B\u8FDB\u884C\u4E2D\u6B65\u9AA4", "running step detail"]
408
+ },
409
+ listStepInfosByFlowApplicationId: {
410
+ aliases: ["list-steps"],
411
+ keywords: ["\u67E5\u8BE2\u6B65\u9AA4\u5217\u8868", "\u6D41\u7A0B\u6B65\u9AA4\u5217\u8868", "\u5BA1\u6279\u6B65\u9AA4\u5217\u8868", "list workflow steps"]
412
+ },
413
+ startProcessInfosBatch: {
414
+ aliases: ["start-process-batch"],
415
+ keywords: ["\u6279\u91CF\u53D1\u8D77\u6D41\u7A0B", "\u6279\u91CF\u542F\u52A8\u6D41\u7A0B\u5B9E\u4F8B", "\u6279\u91CF\u63D0\u4EA4\u6D41\u7A0B", "start workflow process batch"]
416
+ },
417
+ getProcessInfo: {
418
+ aliases: ["get-process"],
419
+ keywords: ["process info", "workflow instance", "\u6D41\u7A0B\u5B9E\u4F8B", "\u5BA1\u6279\u5B9E\u4F8B", "\u67E5\u770B\u6D41\u7A0B\u5B9E\u4F8B"]
420
+ },
421
+ listAllUsers: {
422
+ aliases: ["list-users"],
423
+ keywords: ["contacts", "users", "employees"]
424
+ },
425
+ createProject: {
426
+ aliases: ["create"],
427
+ keywords: ["create project", "new project", "\u521B\u5EFA\u9879\u76EE", "\u65B0\u5EFA\u9879\u76EE"]
428
+ },
429
+ listProjects: {
430
+ aliases: ["list"],
431
+ keywords: ["\u83B7\u53D6\u9879\u76EE\u5217\u8868", "\u9879\u76EE\u5217\u8868", "\u5168\u90E8\u9879\u76EE", "\u67E5\u8BE2\u9879\u76EE\u5217\u8868", "list projects"]
432
+ },
433
+ getProjectById: {
434
+ aliases: ["get"],
435
+ keywords: ["project detail", "get project", "\u9879\u76EE\u8BE6\u60C5", "\u67E5\u770B\u9879\u76EE"]
436
+ },
437
+ listPlansByProjectIds: {
438
+ aliases: ["list-plans"],
439
+ keywords: ["plans", "project plans", "\u6A21\u5757\u5217\u8868", "\u9879\u76EE\u6A21\u5757", "\u67E5\u8BE2\u6A21\u5757"]
440
+ },
441
+ createPlan: {
442
+ aliases: ["create-plan"],
443
+ keywords: ["\u521B\u5EFA\u6A21\u5757", "\u65B0\u589E\u6A21\u5757", "\u65B0\u5EFA\u6A21\u5757", "\u9879\u76EE\u6A21\u5757", "project plan", "task", "statistic", "overview", "folder", "risk", "workflow", "datasheet", "deliverable"],
444
+ description: "\u521B\u5EFA\u9879\u76EE\u4E0B\u7684\u6A21\u5757\u3002type \u4EC5\u652F\u6301\u8FD9\u4E9B\u503C\uFF1Atask\u3001statistic\u3001overview\u3001folder\u3001risk\u3001workflow\u3001datasheet\u3001deliverable\u3002"
445
+ },
446
+ updatePlan: {
447
+ aliases: ["update-plan"],
448
+ keywords: ["\u4FEE\u6539\u6A21\u5757", "\u66F4\u65B0\u6A21\u5757", "\u7F16\u8F91\u6A21\u5757", "project plan"]
449
+ },
450
+ createCard: {
451
+ aliases: ["create-card"],
452
+ keywords: ["\u521B\u5EFA\u5361\u7247", "\u65B0\u589E\u5361\u7247", "\u65B0\u5EFA\u5361\u7247", "\u6A21\u5757\u5361\u7247", "project card"]
453
+ },
454
+ updateCard: {
455
+ aliases: ["update-card"],
456
+ keywords: ["\u4FEE\u6539\u5361\u7247", "\u66F4\u65B0\u5361\u7247", "\u7F16\u8F91\u5361\u7247", "project card"]
457
+ },
458
+ updateProject: {
459
+ aliases: ["update-project"],
460
+ keywords: ["\u4FEE\u6539\u9879\u76EE", "\u66F4\u65B0\u9879\u76EE", "\u7F16\u8F91\u9879\u76EE", "project info"]
461
+ },
462
+ createTask: {
463
+ aliases: ["create"],
464
+ keywords: ["create task", "new task", "\u521B\u5EFA\u4EFB\u52A1", "\u65B0\u5EFA\u4EFB\u52A1"]
465
+ },
466
+ getTaskById: {
467
+ aliases: ["get"],
468
+ keywords: ["task detail", "get task", "\u4EFB\u52A1\u8BE6\u60C5", "\u67E5\u770B\u4EFB\u52A1"]
469
+ },
470
+ updateTask: {
471
+ aliases: ["update"],
472
+ keywords: ["update task", "edit task", "\u4FEE\u6539\u4EFB\u52A1", "\u7F16\u8F91\u4EFB\u52A1", "\u66F4\u65B0\u4EFB\u52A1\u4FE1\u606F"]
473
+ },
474
+ deleteTask: {
475
+ aliases: ["delete"],
476
+ keywords: ["delete task", "remove task", "\u5220\u9664\u4EFB\u52A1", "\u79FB\u9664\u4EFB\u52A1"]
477
+ },
478
+ getTaskComments: {
479
+ aliases: ["comments"],
480
+ keywords: ["task comments", "list task comments", "\u4EFB\u52A1\u8BC4\u8BBA", "\u8BC4\u8BBA\u5217\u8868", "\u67E5\u770B\u8BC4\u8BBA"],
481
+ antiKeywords: ["\u53D1\u8868\u8BC4\u8BBA", "\u63D0\u4EA4\u8BC4\u8BBA", "\u56DE\u590D\u4EFB\u52A1"]
482
+ },
483
+ createTaskComment: {
484
+ aliases: ["comment"],
485
+ keywords: ["create task comment", "add task comment", "\u53D1\u8868\u8BC4\u8BBA", "\u63D0\u4EA4\u8BC4\u8BBA", "\u56DE\u590D\u4EFB\u52A1"],
486
+ antiKeywords: ["\u8BC4\u8BBA\u5217\u8868", "\u67E5\u770B\u8BC4\u8BBA"]
487
+ },
488
+ completeTask: {
489
+ aliases: ["complete"],
490
+ keywords: ["\u5B8C\u6210\u4EFB\u52A1", "\u6253\u94A9\u4EFB\u52A1", "\u6807\u8BB0\u5B8C\u6210", "close task"],
491
+ antiKeywords: ["\u521B\u5EFA\u4EFB\u52A1", "\u4FEE\u6539\u4EFB\u52A1", "\u53D1\u8868\u8BC4\u8BBA", "\u8BC4\u8BBA\u5217\u8868"]
492
+ },
493
+ updateProjectMember: {
494
+ aliases: ["update-project-member"],
495
+ keywords: ["\u4FEE\u6539\u9879\u76EE\u6210\u5458", "\u8BBE\u7F6E\u9879\u76EE\u6210\u5458", "\u6DFB\u52A0\u9879\u76EE\u6210\u5458", "\u9879\u76EE\u6210\u5458", "project member"],
496
+ antiKeywords: ["\u521B\u5EFA\u9879\u76EE", "\u9879\u76EE\u4FE1\u606F", "\u9879\u76EE\u6A21\u5757", "\u5361\u7247"],
497
+ exposure: "internal"
498
+ },
499
+ updateProcessInfoStepStatus: {
500
+ aliases: ["update-running-step-status"],
501
+ keywords: [
502
+ "\u6D41\u8F6C\u6D41\u7A0B\u6B65\u9AA4\u72B6\u6001",
503
+ "\u63D0\u4EA4\u6B65\u9AA4",
504
+ "\u5B8C\u6210\u6B65\u9AA4",
505
+ "\u901A\u8FC7\u6B65\u9AA4",
506
+ "\u62D2\u7EDD\u6B65\u9AA4",
507
+ "\u8DF3\u8FC7\u6B65\u9AA4",
508
+ "\u6B65\u9AA4\u72B6\u6001",
509
+ "\u5BA1\u6279\u901A\u8FC7",
510
+ "\u5BA1\u6279\u62D2\u7EDD"
511
+ ],
512
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u5BA1\u6838\u9879id", "auditInfoId", "processInfoAuditId", "\u5BA1\u6838\u4EBA", "\u5B57\u6BB5\u503C", "\u8868\u5355\u5B57\u6BB5"],
513
+ description: "\u5BF9\u8FD0\u884C\u4E2D\u7684\u6B65\u9AA4\u6267\u884C\u72B6\u6001\u52A8\u4F5C\u3002\u65E0\u8BBA action \u662F\u4EC0\u4E48\uFF0Cpath \u53C2\u6570 processInfoStepId \u59CB\u7EC8\u4F20\u6B65\u9AA4 id\uFF0C\u4E0D\u8981\u4F20\u5BA1\u6838\u9879 id\u3002",
514
+ exposure: "internal"
515
+ },
516
+ updateTaskExecutor: {
517
+ aliases: ["update-executor"],
518
+ keywords: ["\u4FEE\u6539\u4EFB\u52A1\u6267\u884C\u4EBA", "\u8BBE\u7F6E\u6267\u884C\u4EBA", "\u8C03\u6574\u6267\u884C\u4EBA", "task executor"],
519
+ antiKeywords: ["\u8D1F\u8D23\u4EBA", "\u53C2\u4E0E\u4EBA", "\u8BC4\u8BBA"],
520
+ exposure: "composed"
521
+ },
522
+ updateTaskResponsible: {
523
+ aliases: ["update-responsible"],
524
+ keywords: ["\u4FEE\u6539\u4EFB\u52A1\u8D1F\u8D23\u4EBA", "\u8BBE\u7F6E\u8D1F\u8D23\u4EBA", "\u8C03\u6574\u8D1F\u8D23\u4EBA", "task responsible"],
525
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u53C2\u4E0E\u4EBA", "\u8BC4\u8BBA"],
526
+ exposure: "composed"
527
+ },
528
+ addTaskParticipant: {
529
+ aliases: ["add-participant"],
530
+ keywords: ["\u6DFB\u52A0\u4EFB\u52A1\u53C2\u4E0E\u4EBA", "\u589E\u52A0\u53C2\u4E0E\u4EBA", "\u8BBE\u7F6E\u53C2\u4E0E\u4EBA", "task participant"],
531
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u8D1F\u8D23\u4EBA", "\u8BC4\u8BBA"],
532
+ exposure: "composed"
533
+ }
534
+ };
535
+
536
+ // src/registry/index.ts
537
+ var cachedRegistry = null;
538
+ function getRegistry() {
539
+ if (cachedRegistry) {
540
+ return cachedRegistry;
541
+ }
542
+ const openApiCommands = loadOperations().filter((operation) => moduleOrder.includes(operation.module)).map((operation) => {
543
+ const override = operationOverrides[operation.operationId] ?? {};
544
+ const exposure = override.exposure ?? defaultExposure(operation.operationId);
545
+ return {
546
+ module: operation.module,
547
+ name: operation.operationId,
548
+ aliases: override.aliases ?? [],
549
+ summary: operation.summary || operation.operationId,
550
+ description: override.description ?? operation.description ?? operation.summary ?? operation.operationId,
551
+ api: {
552
+ method: operation.method,
553
+ path: operation.path
554
+ },
555
+ parameters: operation.parameters.map(annotateParameterDescription),
556
+ hasRequestBody: operation.hasRequestBody,
557
+ requestBodySchema: annotateRequestBodySchema(operation.requestBodySchema),
558
+ keywords: dedupe([
559
+ operation.operationId,
560
+ operation.summary,
561
+ operation.path,
562
+ ...pathKeywords(operation.path),
563
+ ...override.keywords ?? []
564
+ ]),
565
+ antiKeywords: dedupe(override.antiKeywords ?? []),
566
+ examples: buildExamples(operation.module, operation.operationId, override.aliases ?? []),
567
+ exposure,
568
+ idempotent: operation.method === "GET",
569
+ outputMode: "json"
570
+ };
571
+ });
572
+ cachedRegistry = [...openApiCommands, ...buildCustomCommands()];
573
+ return cachedRegistry;
574
+ }
575
+ function getModuleDefinitions() {
576
+ return moduleOrder.map((module) => ({
577
+ name: module,
578
+ description: moduleDescriptions[module]
579
+ }));
580
+ }
581
+ function buildExamples(module, name, aliases) {
582
+ const examples = [`rsq-cli ${module} ${name} --json`];
583
+ if (aliases[0]) {
584
+ examples.push(`rsq-cli ${module} ${aliases[0]} --json`);
585
+ }
586
+ return examples;
587
+ }
588
+ function annotateParameterDescription(parameter) {
589
+ if (!looksLikeIdField(parameter.name)) {
590
+ return parameter;
591
+ }
592
+ return {
593
+ ...parameter,
594
+ description: appendIdRule(parameter.description)
595
+ };
596
+ }
597
+ function annotateRequestBodySchema(schema) {
598
+ if (!schema) {
599
+ return void 0;
600
+ }
601
+ return {
602
+ ...schema,
603
+ description: schema.description,
604
+ properties: schema.properties ? Object.fromEntries(
605
+ Object.entries(schema.properties).map(([key, value]) => [
606
+ key,
607
+ {
608
+ ...annotateRequestBodySchema(value),
609
+ description: looksLikeIdField(key) ? appendIdRule(value.description) : value.description
610
+ }
611
+ ])
612
+ ) : void 0,
613
+ items: schema.items ? annotateRequestBodySchema(schema.items) : void 0
614
+ };
615
+ }
616
+ function appendIdRule(description) {
617
+ const rule = "\u5FC5\u987B\u4F20\u524D\u7F00+uuid\u98CE\u683C\u7684\u5B57\u7B26\u4E32 id\uFF0C\u4E0D\u8981\u4F20\u6570\u5B57\u7C7B\u578B id\uFF0C\u4E5F\u4E0D\u8981\u4F20\u7EAF\u6570\u5B57\u5B57\u7B26\u4E32\u3002";
618
+ if (!description) {
619
+ return rule;
620
+ }
621
+ return description.includes(rule) ? description : `${description} | ${rule}`;
622
+ }
623
+ function looksLikeIdField(name) {
624
+ return /ids?$/i.test(name);
625
+ }
626
+ function defaultExposure(operationId) {
627
+ if (operationId.startsWith("list") || operationId.startsWith("get") || operationId.startsWith("create") || operationId.startsWith("update") || operationId.startsWith("delete")) {
628
+ return "public";
629
+ }
630
+ return "composed";
631
+ }
632
+ function pathKeywords(apiPath) {
633
+ return apiPath.split("/").filter(Boolean).flatMap((segment) => [segment, segment.replace(/[{}]/g, ""), segment.replace(/-/g, " ")]);
634
+ }
635
+ function dedupe(values) {
636
+ return Array.from(new Set(values.filter((value) => Boolean(value))));
637
+ }
638
+ function buildCustomCommands() {
639
+ return [
640
+ {
641
+ module: "contacts",
642
+ name: "resolveUser",
643
+ aliases: ["resolve-user"],
644
+ summary: "\u6839\u636E\u7528\u6237\u540D\u79F0\u89E3\u6790 userId",
645
+ description: "\u5148\u67E5\u8BE2\u5168\u90E8\u7528\u6237\uFF0C\u518D\u6309 userName \u5339\u914D\uFF0C\u8FD4\u56DE\u552F\u4E00\u547D\u4E2D\u7684 userId\u3002",
646
+ api: {
647
+ method: "GET",
648
+ path: "/rsq/api/v1/contacts/list-all-user"
649
+ },
650
+ parameters: [
651
+ {
652
+ name: "userName",
653
+ in: "query",
654
+ required: true,
655
+ description: "\u9700\u8981\u89E3\u6790\u7684\u4EBA\u5458\u540D\u79F0",
656
+ schemaType: "string"
657
+ }
658
+ ],
659
+ hasRequestBody: false,
660
+ keywords: [
661
+ "resolve user",
662
+ "userName to userId",
663
+ "\u6839\u636E\u59D3\u540D\u627E userId",
664
+ "resolve-user",
665
+ "contacts"
666
+ ],
667
+ antiKeywords: ["\u5217\u8868", "\u5168\u90E8\u7528\u6237"],
668
+ examples: [
669
+ "rsq-cli contacts resolveUser --user-name \u5F20\u4E09 --json",
670
+ "rsq-cli contacts resolve-user --user-name \u5F20\u4E09 --json"
671
+ ],
672
+ exposure: "composed",
673
+ idempotent: true,
674
+ outputMode: "json"
675
+ },
676
+ {
677
+ module: "task",
678
+ name: "setTaskExecutorByName",
679
+ aliases: ["set-executor-by-name"],
680
+ summary: "\u6839\u636E\u4EBA\u5458\u540D\u79F0\u8BBE\u7F6E\u4EFB\u52A1\u6267\u884C\u4EBA",
681
+ description: "\u5148\u6309 userName \u89E3\u6790 userId\uFF0C\u518D\u66F4\u65B0\u4EFB\u52A1\u6267\u884C\u4EBA\u3002",
682
+ api: {
683
+ method: "PUT",
684
+ path: "/rsq/api/v1/tasks/{taskId}/executor"
685
+ },
686
+ parameters: [
687
+ {
688
+ name: "taskId",
689
+ in: "path",
690
+ required: true,
691
+ description: "taskId",
692
+ schemaType: "string"
693
+ },
694
+ {
695
+ name: "userName",
696
+ in: "query",
697
+ required: true,
698
+ description: "\u6267\u884C\u4EBA\u59D3\u540D",
699
+ schemaType: "string"
700
+ }
701
+ ],
702
+ hasRequestBody: false,
703
+ keywords: ["task executor by name", "set executor by name", "\u6839\u636E\u59D3\u540D\u8BBE\u7F6E\u6267\u884C\u4EBA"],
704
+ antiKeywords: ["\u8D1F\u8D23\u4EBA", "\u53C2\u4E0E\u4EBA", "\u8BC4\u8BBA"],
705
+ examples: [
706
+ "rsq-cli task setTaskExecutorByName --task-id task_demo --user-name \u5F20\u4E09 --json",
707
+ "rsq-cli task set-executor-by-name --task-id task_demo --user-name \u5F20\u4E09 --json"
708
+ ],
709
+ exposure: "composed",
710
+ idempotent: false,
711
+ outputMode: "json"
712
+ },
713
+ {
714
+ module: "task",
715
+ name: "setTaskResponsibleByName",
716
+ aliases: ["set-responsible-by-name"],
717
+ summary: "\u6839\u636E\u4EBA\u5458\u540D\u79F0\u8BBE\u7F6E\u4EFB\u52A1\u8D1F\u8D23\u4EBA",
718
+ description: "\u5148\u6309 userName \u89E3\u6790 userId\uFF0C\u518D\u66F4\u65B0\u4EFB\u52A1\u8D1F\u8D23\u4EBA\u3002",
719
+ api: {
720
+ method: "PUT",
721
+ path: "/rsq/api/v1/tasks/{taskId}/responsible"
722
+ },
723
+ parameters: [
724
+ {
725
+ name: "taskId",
726
+ in: "path",
727
+ required: true,
728
+ description: "taskId",
729
+ schemaType: "string"
730
+ },
731
+ {
732
+ name: "userName",
733
+ in: "query",
734
+ required: true,
735
+ description: "\u8D1F\u8D23\u4EBA\u59D3\u540D",
736
+ schemaType: "string"
737
+ }
738
+ ],
739
+ hasRequestBody: false,
740
+ keywords: ["task responsible by name", "set responsible by name", "\u6839\u636E\u59D3\u540D\u8BBE\u7F6E\u8D1F\u8D23\u4EBA"],
741
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u53C2\u4E0E\u4EBA", "\u8BC4\u8BBA"],
742
+ examples: [
743
+ "rsq-cli task setTaskResponsibleByName --task-id task_demo --user-name \u674E\u56DB --json",
744
+ "rsq-cli task set-responsible-by-name --task-id task_demo --user-name \u674E\u56DB --json"
745
+ ],
746
+ exposure: "composed",
747
+ idempotent: false,
748
+ outputMode: "json"
749
+ },
750
+ {
751
+ module: "task",
752
+ name: "addTaskParticipantByName",
753
+ aliases: ["add-participant-by-name"],
754
+ summary: "\u6839\u636E\u4EBA\u5458\u540D\u79F0\u6DFB\u52A0\u4EFB\u52A1\u53C2\u4E0E\u4EBA",
755
+ description: "\u5148\u6309 userName \u89E3\u6790 userId\uFF0C\u518D\u6DFB\u52A0\u4EFB\u52A1\u53C2\u4E0E\u4EBA\u3002",
756
+ api: {
757
+ method: "POST",
758
+ path: "/rsq/api/v1/tasks/{taskId}/participants"
759
+ },
760
+ parameters: [
761
+ {
762
+ name: "taskId",
763
+ in: "path",
764
+ required: true,
765
+ description: "taskId",
766
+ schemaType: "string"
767
+ },
768
+ {
769
+ name: "userName",
770
+ in: "query",
771
+ required: true,
772
+ description: "\u53C2\u4E0E\u4EBA\u59D3\u540D",
773
+ schemaType: "string"
774
+ }
775
+ ],
776
+ hasRequestBody: false,
777
+ keywords: ["task participant by name", "add participant by name", "\u6839\u636E\u59D3\u540D\u6DFB\u52A0\u53C2\u4E0E\u4EBA"],
778
+ antiKeywords: ["\u6267\u884C\u4EBA", "\u8D1F\u8D23\u4EBA", "\u8BC4\u8BBA"],
779
+ examples: [
780
+ "rsq-cli task addTaskParticipantByName --task-id task_demo --user-name \u738B\u4E94 --json",
781
+ "rsq-cli task add-participant-by-name --task-id task_demo --user-name \u738B\u4E94 --json"
782
+ ],
783
+ exposure: "composed",
784
+ idempotent: false,
785
+ outputMode: "json"
786
+ },
787
+ {
788
+ module: "project",
789
+ name: "setProjectMemberByName",
790
+ aliases: ["set-member-by-name"],
791
+ summary: "\u6839\u636E\u4EBA\u5458\u540D\u79F0\u8BBE\u7F6E\u9879\u76EE\u6210\u5458",
792
+ description: "\u5148\u6309 userName \u89E3\u6790 userId\uFF0C\u518D\u66F4\u65B0\u9879\u76EE\u6210\u5458\u3002",
793
+ api: {
794
+ method: "POST",
795
+ path: "/rsq/api/v1/projects/{projectId}/members"
796
+ },
797
+ parameters: [
798
+ {
799
+ name: "projectId",
800
+ in: "path",
801
+ required: true,
802
+ description: "projectId",
803
+ schemaType: "string"
804
+ },
805
+ {
806
+ name: "userName",
807
+ in: "query",
808
+ required: true,
809
+ description: "\u9879\u76EE\u6210\u5458\u59D3\u540D",
810
+ schemaType: "string"
811
+ }
812
+ ],
813
+ hasRequestBody: false,
814
+ keywords: ["project member by name", "set project member by name", "\u6839\u636E\u59D3\u540D\u8BBE\u7F6E\u9879\u76EE\u6210\u5458"],
815
+ examples: [
816
+ "rsq-cli project setProjectMemberByName --project-id proj_demo --user-name \u5F20\u4E09 --json",
817
+ "rsq-cli project set-member-by-name --project-id proj_demo --user-name \u5F20\u4E09 --json"
818
+ ],
819
+ exposure: "composed",
820
+ idempotent: false,
821
+ outputMode: "json"
822
+ },
823
+ {
824
+ module: "workflow",
825
+ name: "createAuditInfoByNames",
826
+ aliases: ["create-audit-by-names"],
827
+ summary: "\u6839\u636E\u4EBA\u5458\u540D\u79F0\u521B\u5EFA\u5BA1\u6838\u9879\u914D\u7F6E",
828
+ description: "\u5148\u6309 userName \u89E3\u6790 userId\uFF0C\u518D\u521B\u5EFA\u5BA1\u6838\u9879\u914D\u7F6E\u3002",
829
+ api: {
830
+ method: "POST",
831
+ path: "/rsq/api/v1/workflow/audit-infos"
832
+ },
833
+ parameters: [
834
+ { name: "flowApplicationId", in: "query", required: true, description: "\u6D41\u7A0B\u5E94\u7528id", schemaType: "string" },
835
+ { name: "stepInfoId", in: "query", required: true, description: "\u6B65\u9AA4\u914D\u7F6Eid", schemaType: "string" },
836
+ { name: "auditInfoName", in: "query", required: true, description: "\u5BA1\u6838\u9879\u540D\u79F0", schemaType: "string" },
837
+ { name: "userNames", in: "query", required: true, description: "\u5BA1\u6838\u4EBA\u540D\u79F0\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694", schemaType: "string" },
838
+ { name: "frontAuditInfoId", in: "query", required: false, description: "\u524D\u4E00\u4E2A\u5BA1\u6838\u9879id", schemaType: "string" }
839
+ ],
840
+ hasRequestBody: false,
841
+ keywords: ["create audit by names", "workflow audit users by name", "\u6839\u636E\u59D3\u540D\u521B\u5EFA\u5BA1\u6838\u9879"],
842
+ examples: [
843
+ "rsq-cli workflow createAuditInfoByNames --flow-application-id flow_demo --step-info-id step_demo --audit-info-name \u90E8\u95E8\u5BA1\u6279 --user-names \u5F20\u4E09,\u674E\u56DB --json"
844
+ ],
845
+ exposure: "composed",
846
+ idempotent: false,
847
+ outputMode: "json"
848
+ },
849
+ {
850
+ module: "workflow",
851
+ name: "updateAuditInfoByNames",
852
+ aliases: ["update-audit-by-names"],
853
+ summary: "\u6839\u636E\u4EBA\u5458\u540D\u79F0\u66F4\u65B0\u5BA1\u6838\u9879\u914D\u7F6E",
854
+ description: "\u5148\u6309 userName \u89E3\u6790 userId\uFF0C\u518D\u66F4\u65B0\u5BA1\u6838\u9879\u914D\u7F6E\u3002",
855
+ api: {
856
+ method: "PUT",
857
+ path: "/rsq/api/v1/workflow/audit-infos/{auditInfoId}"
858
+ },
859
+ parameters: [
860
+ { name: "auditInfoId", in: "path", required: true, description: "\u5BA1\u6838\u9879\u914D\u7F6Eid", schemaType: "string" },
861
+ { name: "auditInfoName", in: "query", required: true, description: "\u5BA1\u6838\u9879\u540D\u79F0", schemaType: "string" },
862
+ { name: "userNames", in: "query", required: true, description: "\u5BA1\u6838\u4EBA\u540D\u79F0\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694", schemaType: "string" }
863
+ ],
864
+ hasRequestBody: false,
865
+ keywords: ["update audit by names", "workflow audit users by name", "\u6839\u636E\u59D3\u540D\u66F4\u65B0\u5BA1\u6838\u9879"],
866
+ examples: [
867
+ "rsq-cli workflow updateAuditInfoByNames --audit-info-id audit_demo --audit-info-name \u90E8\u95E8\u5BA1\u6279 --user-names \u5F20\u4E09,\u674E\u56DB --json"
868
+ ],
869
+ exposure: "composed",
870
+ idempotent: false,
871
+ outputMode: "json"
872
+ }
873
+ ];
874
+ }
875
+
876
+ // src/registry/skill-contract.ts
877
+ function getSkillContract() {
878
+ return {
879
+ skill: {
880
+ name: "rishiqing",
881
+ summary: "\u8BC6\u522B\u65E5\u4E8B\u6E05\u9886\u57DF\u81EA\u7136\u8BED\u8A00\u8BF7\u6C42\uFF0C\u5E76\u8DEF\u7531\u5230 rsq-cli \u7684\u5BF9\u5E94\u6A21\u5757\u4E0E\u547D\u4EE4\u3002"
882
+ },
883
+ trigger: {
884
+ domain: "rishiqing",
885
+ when: [
886
+ "\u5F53\u7528\u6237\u5728\u5BF9\u8BDD\u4E2D\u63D0\u5230\u65E5\u4E8B\u6E05\u3001rsq\u3001rishiqing \u65F6\uFF0C\u9ED8\u8BA4\u8FD9\u662F\u65E5\u4E8B\u6E05\u64CD\u4F5C\u8BF7\u6C42\u3002",
887
+ "\u5F53\u7528\u6237\u63D0\u5230\u9879\u76EE\u3001\u6A21\u5757\u3001\u5361\u7247\u3001\u4EFB\u52A1\u3001\u6D41\u7A0B\u3001\u591A\u7EF4\u8868\u683C\u3001\u6570\u636E\u3001\u6D41\u7A0B\u5E94\u7528\u3001\u6D41\u7A0B\u5B9E\u4F8B\u3001\u6B65\u9AA4\u3001\u8868\u5355\u3001\u5B57\u6BB5\u3001\u5BA1\u6838\u9879\u3001\u5BA1\u6279\u3001\u6210\u5458\u3001\u8D1F\u8D23\u4EBA\u3001\u6267\u884C\u4EBA\u3001\u53C2\u4E0E\u4EBA\u7B49\u9886\u57DF\u8BCD\u65F6\uFF0C\u4E5F\u4F18\u5148\u6309\u65E5\u4E8B\u6E05\u8BF7\u6C42\u5904\u7406\u3002"
888
+ ],
889
+ keywords: [
890
+ "\u65E5\u4E8B\u6E05",
891
+ "rsq",
892
+ "rishiqing",
893
+ "\u9879\u76EE",
894
+ "\u6A21\u5757",
895
+ "\u5361\u7247",
896
+ "\u4EFB\u52A1",
897
+ "\u6D41\u7A0B",
898
+ "\u6D41\u7A0B\u5E94\u7528",
899
+ "\u6D41\u7A0B\u5B9E\u4F8B",
900
+ "\u6B65\u9AA4",
901
+ "\u8868\u5355",
902
+ "\u5B57\u6BB5",
903
+ "\u5BA1\u6838\u9879",
904
+ "\u5BA1\u6279",
905
+ "\u591A\u7EF4\u8868\u683C",
906
+ "\u6570\u636E",
907
+ "\u6210\u5458",
908
+ "\u8D1F\u8D23\u4EBA",
909
+ "\u6267\u884C\u4EBA",
910
+ "\u53C2\u4E0E\u4EBA"
911
+ ]
912
+ },
913
+ routing: {
914
+ moduleHints: [
915
+ {
916
+ module: "project",
917
+ intents: ["\u9879\u76EE", "\u6A21\u5757", "\u9879\u76EE\u6210\u5458", "\u9879\u76EE\u6982\u89C8", "\u9879\u76EE\u4E0B\u8BA1\u5212"]
918
+ },
919
+ {
920
+ module: "task",
921
+ intents: ["\u4EFB\u52A1", "\u5361\u7247", "\u6267\u884C\u4EBA", "\u8D1F\u8D23\u4EBA", "\u53C2\u4E0E\u4EBA", "\u4EFB\u52A1\u72B6\u6001"]
922
+ },
923
+ {
924
+ module: "workflow",
925
+ intents: ["\u6D41\u7A0B\u5E94\u7528", "\u6D41\u7A0B\u5B9E\u4F8B", "\u6B65\u9AA4", "\u8868\u5355", "\u5B57\u6BB5", "\u5BA1\u6838\u9879", "\u5BA1\u6279\u6D41"]
926
+ },
927
+ {
928
+ module: "datasheet",
929
+ intents: ["\u591A\u7EF4\u8868\u683C", "\u6570\u636E\u8868", "\u8BB0\u5F55", "\u5B57\u6BB5", "\u6570\u636E"]
930
+ },
931
+ {
932
+ module: "contacts",
933
+ intents: ["\u6309\u59D3\u540D\u89E3\u6790\u7528\u6237", "\u67E5\u6210\u5458", "\u67E5\u7528\u6237"]
934
+ }
935
+ ]
936
+ },
937
+ globalRules: [
938
+ "\u9047\u5230\u65E5\u4E8B\u6E05\u9886\u57DF\u77ED\u8BED\u65F6\uFF0C\u5148\u8BC6\u522B\u4E3A rsq-cli \u64CD\u4F5C\u610F\u56FE\uFF0C\u4E0D\u8981\u628A\u5B83\u5F53\u6210\u6CDB\u5316\u9879\u76EE\u7BA1\u7406\u8BF7\u6C42\u3002",
939
+ "\u4E0D\u786E\u5B9A\u5177\u4F53\u547D\u4EE4\u65F6\uFF0C\u5148\u8C03\u7528 rsq-cli search\uFF1B\u786E\u8BA4\u5019\u9009\u547D\u4EE4\u540E\u518D\u8C03\u7528 rsq-cli describe \u9605\u8BFB\u53C2\u6570\u548C requestBodySchema\u3002",
940
+ "\u6D89\u53CA\u521B\u5EFA\u3001\u66F4\u65B0\u3001\u7ED1\u5B9A\u3001\u5206\u914D\u7B49\u5199\u64CD\u4F5C\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 --dry-run --json \u9884\u89C8\u3002",
941
+ "\u6240\u6709 id \u5B57\u6BB5\u90FD\u5FC5\u987B\u4F20\u524D\u7F00+uuid\u98CE\u683C\u7684\u5B57\u7B26\u4E32\uFF0C\u4F8B\u5982 flowApp_019d6c0c2f03770b8b789ec313076e64\u3002",
942
+ "\u4E0D\u8981\u4F20\u6570\u5B57\u7C7B\u578B id\uFF0C\u4E5F\u4E0D\u8981\u4F20\u7EAF\u6570\u5B57\u5B57\u7B26\u4E32 id\u3002",
943
+ "\u8C03\u7528\u524D\u4F18\u5148\u9605\u8BFB\u53C2\u6570\u8BF4\u660E\u548C requestBodySchema \u4E2D\u7684\u5B57\u6BB5\u8BF4\u660E\uFF0C\u518D\u7EC4\u88C5\u53C2\u6570\u3002"
944
+ ],
945
+ tools: getRegistry().map((command) => ({
946
+ tool: `${command.module}.${command.aliases[0] ?? command.name}`,
947
+ canonical: `${command.module}.${command.name}`,
948
+ summary: command.summary,
949
+ exposure: command.exposure,
950
+ keywords: command.keywords,
951
+ examples: command.examples,
952
+ input: {
953
+ parameters: command.parameters.map((parameter) => ({
954
+ name: parameter.name,
955
+ location: parameter.in,
956
+ required: parameter.required,
957
+ type: parameter.schemaType ?? "string",
958
+ description: parameter.description
959
+ })),
960
+ acceptsBody: command.hasRequestBody,
961
+ requestBodySchema: command.requestBodySchema
962
+ }
963
+ }))
964
+ };
965
+ }
966
+
967
+ // src/cli.ts
968
+ var createPlanTypes = /* @__PURE__ */ new Set([
969
+ "task",
970
+ "statistic",
971
+ "overview",
972
+ "folder",
973
+ "risk",
974
+ "workflow",
975
+ "datasheet",
976
+ "deliverable"
977
+ ]);
978
+ async function main() {
979
+ const program = new Command();
980
+ program.name("rsq-cli").description("Agent-oriented CLI for Rishiqing APIs").version("0.1.0");
981
+ registerGlobalHelp(program);
982
+ registerConfig(program);
983
+ registerDescribe(program);
984
+ registerSearch(program);
985
+ for (const moduleDefinition of getModuleDefinitions()) {
986
+ const moduleCommand = program.command(moduleDefinition.name).description(moduleDefinition.description);
987
+ for (const definition of getRegistry().filter((entry) => entry.module === moduleDefinition.name)) {
988
+ if (definition.module === "contacts" && definition.name === "resolveUser") {
989
+ registerResolveUserCommand(moduleCommand, definition);
990
+ continue;
991
+ }
992
+ if (definition.module === "task" && ["setTaskExecutorByName", "setTaskResponsibleByName", "addTaskParticipantByName"].includes(definition.name)) {
993
+ registerTaskUserByNameCommand(moduleCommand, definition);
994
+ continue;
995
+ }
996
+ if (definition.module === "project" && definition.name === "setProjectMemberByName") {
997
+ registerProjectUserByNameCommand(moduleCommand, definition);
998
+ continue;
999
+ }
1000
+ if (definition.module === "workflow" && ["createAuditInfoByNames", "updateAuditInfoByNames"].includes(definition.name)) {
1001
+ registerWorkflowAuditByNamesCommand(moduleCommand, definition);
1002
+ continue;
1003
+ }
1004
+ registerApiCommand(moduleCommand, definition);
1005
+ }
1006
+ }
1007
+ await program.parseAsync(process.argv);
1008
+ }
1009
+ function registerGlobalHelp(program) {
1010
+ program.showHelpAfterError("(run with --help for usage)");
1011
+ }
1012
+ function registerConfig(program) {
1013
+ const config = program.command("config").description("Manage local rsq-cli configuration");
1014
+ config.command("init").description("Initialize local configuration").action(async () => {
1015
+ const rl = readline.createInterface({ input: stdin, output: stdout });
1016
+ try {
1017
+ const apiKey = (await rl.question("X-Rsq-Api-key: ")).trim();
1018
+ if (!apiKey) {
1019
+ throw new Error("X-Rsq-Api-key is required.");
1020
+ }
1021
+ const saved = await initConfig(apiKey);
1022
+ const appConfig = await readAppConfig();
1023
+ printOutput(
1024
+ {
1025
+ message: "Configuration saved.",
1026
+ configPath: getConfigPath(),
1027
+ appConfigPath: getAppConfigPath(),
1028
+ activeProfile: saved.activeProfile,
1029
+ baseUrl: appConfig.baseUrl
1030
+ },
1031
+ { json: true }
1032
+ );
1033
+ } finally {
1034
+ rl.close();
1035
+ }
1036
+ });
1037
+ config.command("get").description("Show current config without exposing the API key").option("--json", "Print JSON output").action(async (options) => {
1038
+ const current = await readConfig();
1039
+ const appConfig = await readAppConfig();
1040
+ if (!current) {
1041
+ throw new Error("Missing config. Run `rsq-cli config init` first.");
1042
+ }
1043
+ const profile = current.profiles[current.activeProfile];
1044
+ printOutput(
1045
+ {
1046
+ configPath: getConfigPath(),
1047
+ appConfigPath: getAppConfigPath(),
1048
+ baseUrl: appConfig.baseUrl,
1049
+ activeProfile: current.activeProfile,
1050
+ profiles: {
1051
+ [current.activeProfile]: {
1052
+ apiKeyConfigured: Boolean(profile?.apiKey)
1053
+ }
1054
+ }
1055
+ },
1056
+ options
1057
+ );
1058
+ });
1059
+ config.command("path").description("Show config file path").action(() => {
1060
+ stdout.write(`${getConfigPath()}
1061
+ `);
1062
+ });
1063
+ config.command("app-path").description("Show bundled app config path").action(() => {
1064
+ stdout.write(`${getAppConfigPath()}
1065
+ `);
1066
+ });
1067
+ }
1068
+ function registerDescribe(program) {
1069
+ program.command("describe").description("Describe commands in machine-readable form").argument("[module]", "Module name").argument("[command]", "Command name").addOption(new Option("--json", "Print JSON output")).action((moduleName, commandName, options) => {
1070
+ const source = getRegistry();
1071
+ if (!moduleName) {
1072
+ printOutput(source, { json: true });
1073
+ return;
1074
+ }
1075
+ if (!commandName) {
1076
+ printOutput(source.filter((entry) => entry.module === moduleName), { json: true });
1077
+ return;
1078
+ }
1079
+ const command = source.find(
1080
+ (entry) => entry.module === moduleName && (entry.name === commandName || entry.aliases.includes(commandName))
1081
+ );
1082
+ if (!command) {
1083
+ throw new Error(`Unknown command: ${moduleName} ${commandName}`);
1084
+ }
1085
+ printOutput(command, { json: true });
1086
+ });
1087
+ program.command("describe-agent-contract").description("Describe the built-in agent contract derived from the registry").action(() => {
1088
+ printOutput(getSkillContract(), { json: true });
1089
+ });
1090
+ }
1091
+ function registerSearch(program) {
1092
+ program.command("search").description("Search all registered commands by natural language intent").argument("<query>", "Search query").option("--limit <count>", "Maximum results", parsePositiveInteger, 10).option("--json", "Print JSON output").action((query, options) => {
1093
+ const keywords = tokenize(query);
1094
+ const ranked = getRegistry().map((command) => ({
1095
+ command,
1096
+ score: scoreCommand(command, query, keywords)
1097
+ })).filter((entry) => entry.score > 0).sort((left, right) => {
1098
+ if (right.score !== left.score) {
1099
+ return right.score - left.score;
1100
+ }
1101
+ return exposurePriority(right.command) - exposurePriority(left.command);
1102
+ }).slice(0, options.limit).map(({ command, score }) => ({
1103
+ score,
1104
+ module: command.module,
1105
+ name: command.name,
1106
+ aliases: command.aliases,
1107
+ exposure: command.exposure,
1108
+ summary: command.summary,
1109
+ api: command.api
1110
+ }));
1111
+ printOutput(ranked, options);
1112
+ });
1113
+ }
1114
+ function registerApiCommand(parent, definition) {
1115
+ if (!definition.api) {
1116
+ throw new Error(`Missing API mapping for command: ${definition.module} ${definition.name}`);
1117
+ }
1118
+ const command = parent.command(definition.name).description(definition.summary);
1119
+ for (const alias of definition.aliases) {
1120
+ command.alias(alias);
1121
+ }
1122
+ addSharedOutputOptions(command);
1123
+ for (const parameter of definition.parameters) {
1124
+ command.addOption(
1125
+ new Option(optionName(parameter.name), parameter.description ?? `${parameter.in} parameter: ${parameter.name}`).makeOptionMandatory(Boolean(parameter.required))
1126
+ );
1127
+ }
1128
+ if (definition.hasRequestBody) {
1129
+ command.addOption(new Option("--body <json>", "Inline JSON request body"));
1130
+ command.addOption(new Option("--body-file <path>", "Read JSON request body from a file"));
1131
+ }
1132
+ command.action(async (options) => {
1133
+ const body = sanitizeRequestBody(definition, await readBody(options));
1134
+ validateRequestBody(definition, body);
1135
+ const request = buildRequest(definition, options, body);
1136
+ if (options.dryRun) {
1137
+ const appConfig = await readAppConfig();
1138
+ printOutput(
1139
+ {
1140
+ command: `${definition.module} ${definition.name}`,
1141
+ aliases: definition.aliases,
1142
+ api: definition.api,
1143
+ baseUrl: appConfig.baseUrl,
1144
+ url: new URL(request.path, ensureTrailingSlash2(appConfig.baseUrl)).toString(),
1145
+ request
1146
+ },
1147
+ { json: true }
1148
+ );
1149
+ return;
1150
+ }
1151
+ const result = await executeRequest(request);
1152
+ if (!options.quiet) {
1153
+ printOutput(result, options);
1154
+ }
1155
+ });
1156
+ }
1157
+ function registerResolveUserCommand(parent, definition) {
1158
+ const command = parent.command(definition.name).description(definition.summary);
1159
+ for (const alias of definition.aliases) {
1160
+ command.alias(alias);
1161
+ }
1162
+ command.addOption(new Option("--user-name <value>", "\u9700\u8981\u89E3\u6790\u7684\u4EBA\u5458\u540D\u79F0").makeOptionMandatory(true));
1163
+ command.addOption(new Option("--json", "Print JSON output"));
1164
+ command.addOption(new Option("--table", "Print array responses in a basic table"));
1165
+ command.action(async (options) => {
1166
+ const userName = asOptionalString(options.userName);
1167
+ if (!userName) {
1168
+ throw new Error("Missing required option: --user-name");
1169
+ }
1170
+ printOutput(await resolveUserByName(userName), { json: true });
1171
+ });
1172
+ }
1173
+ function registerTaskUserByNameCommand(parent, definition) {
1174
+ const command = parent.command(definition.name).description(definition.summary);
1175
+ for (const alias of definition.aliases) {
1176
+ command.alias(alias);
1177
+ }
1178
+ command.addOption(new Option("--task-id <value>", "taskId").makeOptionMandatory(true));
1179
+ command.addOption(new Option("--user-name <value>", "\u4EBA\u5458\u540D\u79F0").makeOptionMandatory(true));
1180
+ command.addOption(new Option("--json", "Print JSON output"));
1181
+ command.addOption(new Option("--dry-run", "Resolve request details without calling the API"));
1182
+ command.action(async (options) => {
1183
+ const taskId = asOptionalString(options.taskId);
1184
+ const userName = asOptionalString(options.userName);
1185
+ if (!taskId || !userName) {
1186
+ throw new Error("Missing required options: --task-id and --user-name");
1187
+ }
1188
+ if (options.dryRun) {
1189
+ const appConfig = await readAppConfig();
1190
+ printOutput(
1191
+ {
1192
+ command: `${definition.module} ${definition.name}`,
1193
+ aliases: definition.aliases,
1194
+ baseUrl: appConfig.baseUrl,
1195
+ steps: [
1196
+ {
1197
+ action: "resolveUser",
1198
+ command: `rsq-cli contacts resolveUser --user-name ${userName} --json`
1199
+ },
1200
+ {
1201
+ action: "callTargetApi",
1202
+ api: definition.api,
1203
+ pathTemplate: definition.api.path,
1204
+ taskId,
1205
+ userName
1206
+ }
1207
+ ]
1208
+ },
1209
+ { json: true }
1210
+ );
1211
+ return;
1212
+ }
1213
+ const resolved = await resolveUserByName(userName);
1214
+ const request = buildTaskUserByNameRequest(definition.name, taskId, resolved.userId);
1215
+ const result = await executeRequest(request);
1216
+ printOutput(
1217
+ {
1218
+ resolvedUser: resolved,
1219
+ result
1220
+ },
1221
+ { json: true }
1222
+ );
1223
+ });
1224
+ }
1225
+ function registerProjectUserByNameCommand(parent, definition) {
1226
+ const command = parent.command(definition.name).description(definition.summary);
1227
+ for (const alias of definition.aliases) command.alias(alias);
1228
+ command.addOption(new Option("--project-id <value>", "projectId").makeOptionMandatory(true));
1229
+ command.addOption(new Option("--user-name <value>", "\u4EBA\u5458\u540D\u79F0").makeOptionMandatory(true));
1230
+ command.addOption(new Option("--json", "Print JSON output"));
1231
+ command.addOption(new Option("--dry-run", "Resolve request details without calling the API"));
1232
+ command.action(async (options) => {
1233
+ const projectId = asOptionalString(options.projectId);
1234
+ const userName = asOptionalString(options.userName);
1235
+ if (!projectId || !userName) throw new Error("Missing required options: --project-id and --user-name");
1236
+ if (options.dryRun) {
1237
+ const appConfig = await readAppConfig();
1238
+ printOutput(
1239
+ {
1240
+ command: `${definition.module} ${definition.name}`,
1241
+ aliases: definition.aliases,
1242
+ baseUrl: appConfig.baseUrl,
1243
+ steps: [
1244
+ { action: "resolveUser", command: `rsq-cli contacts resolveUser --user-name ${userName} --json` },
1245
+ {
1246
+ action: "callTargetApi",
1247
+ api: definition.api,
1248
+ pathTemplate: definition.api.path,
1249
+ projectId,
1250
+ userName
1251
+ }
1252
+ ]
1253
+ },
1254
+ { json: true }
1255
+ );
1256
+ return;
1257
+ }
1258
+ const resolved = await resolveUserByName(userName);
1259
+ const result = await executeRequest({
1260
+ method: "POST",
1261
+ path: `/rsq/api/v1/projects/${encodeURIComponent(projectId)}/members`,
1262
+ body: { userMemberIds: [resolved.userId], deptMemberIds: [] }
1263
+ });
1264
+ printOutput({ resolvedUser: resolved, result }, { json: true });
1265
+ });
1266
+ }
1267
+ function registerWorkflowAuditByNamesCommand(parent, definition) {
1268
+ const command = parent.command(definition.name).description(definition.summary);
1269
+ for (const alias of definition.aliases) command.alias(alias);
1270
+ if (definition.name === "createAuditInfoByNames") {
1271
+ command.addOption(new Option("--flow-application-id <value>", "flowApplicationId").makeOptionMandatory(true));
1272
+ command.addOption(new Option("--step-info-id <value>", "stepInfoId").makeOptionMandatory(true));
1273
+ command.addOption(new Option("--audit-info-name <value>", "auditInfoName").makeOptionMandatory(true));
1274
+ command.addOption(new Option("--user-names <value>", "\u5BA1\u6838\u4EBA\u540D\u79F0\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694").makeOptionMandatory(true));
1275
+ command.addOption(new Option("--front-audit-info-id <value>", "frontAuditInfoId"));
1276
+ } else {
1277
+ command.addOption(new Option("--audit-info-id <value>", "auditInfoId").makeOptionMandatory(true));
1278
+ command.addOption(new Option("--audit-info-name <value>", "auditInfoName").makeOptionMandatory(true));
1279
+ command.addOption(new Option("--user-names <value>", "\u5BA1\u6838\u4EBA\u540D\u79F0\u5217\u8868\uFF0C\u9017\u53F7\u5206\u9694").makeOptionMandatory(true));
1280
+ }
1281
+ command.addOption(new Option("--json", "Print JSON output"));
1282
+ command.addOption(new Option("--dry-run", "Resolve request details without calling the API"));
1283
+ command.action(async (options) => {
1284
+ const userNamesValue = asOptionalString(options.userNames);
1285
+ const userNames = parseNameList(userNamesValue);
1286
+ if (userNames.length === 0) throw new Error("Missing required option: --user-names");
1287
+ if (options.dryRun) {
1288
+ const appConfig = await readAppConfig();
1289
+ printOutput(
1290
+ {
1291
+ command: `${definition.module} ${definition.name}`,
1292
+ aliases: definition.aliases,
1293
+ baseUrl: appConfig.baseUrl,
1294
+ steps: [
1295
+ ...userNames.map((name) => ({
1296
+ action: "resolveUser",
1297
+ command: `rsq-cli contacts resolveUser --user-name ${name} --json`
1298
+ })),
1299
+ {
1300
+ action: "callTargetApi",
1301
+ api: definition.api,
1302
+ pathTemplate: definition.api.path,
1303
+ options: {
1304
+ flowApplicationId: asOptionalString(options.flowApplicationId),
1305
+ stepInfoId: asOptionalString(options.stepInfoId),
1306
+ auditInfoId: asOptionalString(options.auditInfoId),
1307
+ auditInfoName: asOptionalString(options.auditInfoName),
1308
+ frontAuditInfoId: asOptionalString(options.frontAuditInfoId),
1309
+ userNames
1310
+ }
1311
+ }
1312
+ ]
1313
+ },
1314
+ { json: true }
1315
+ );
1316
+ return;
1317
+ }
1318
+ const resolvedUsers = [];
1319
+ for (const userName of userNames) {
1320
+ resolvedUsers.push(await resolveUserByName(userName));
1321
+ }
1322
+ const userIds = resolvedUsers.map((user) => user.userId);
1323
+ let request;
1324
+ if (definition.name === "createAuditInfoByNames") {
1325
+ const flowApplicationId = asOptionalString(options.flowApplicationId);
1326
+ const stepInfoId = asOptionalString(options.stepInfoId);
1327
+ const auditInfoName = asOptionalString(options.auditInfoName);
1328
+ if (!flowApplicationId || !stepInfoId || !auditInfoName) {
1329
+ throw new Error("Missing required options for createAuditInfoByNames");
1330
+ }
1331
+ request = {
1332
+ method: "POST",
1333
+ path: "/rsq/api/v1/workflow/audit-infos",
1334
+ body: {
1335
+ flowApplicationId,
1336
+ stepInfoId,
1337
+ auditInfoName,
1338
+ frontAuditInfoId: asOptionalString(options.frontAuditInfoId),
1339
+ auditInfoUsers: userIds
1340
+ }
1341
+ };
1342
+ } else {
1343
+ const auditInfoId = asOptionalString(options.auditInfoId);
1344
+ const auditInfoName = asOptionalString(options.auditInfoName);
1345
+ if (!auditInfoId || !auditInfoName) {
1346
+ throw new Error("Missing required options for updateAuditInfoByNames");
1347
+ }
1348
+ request = {
1349
+ method: "PUT",
1350
+ path: `/rsq/api/v1/workflow/audit-infos/${encodeURIComponent(auditInfoId)}`,
1351
+ body: {
1352
+ auditInfoName,
1353
+ auditInfoUsers: userIds
1354
+ }
1355
+ };
1356
+ }
1357
+ const result = await executeRequest(request);
1358
+ printOutput({ resolvedUsers, result }, { json: true });
1359
+ });
1360
+ }
1361
+ function addSharedOutputOptions(command) {
1362
+ command.addOption(new Option("--json", "Print JSON output"));
1363
+ command.addOption(new Option("--raw", "Print the raw response payload"));
1364
+ command.addOption(new Option("--table", "Print array responses in a basic table"));
1365
+ command.addOption(new Option("--quiet", "Suppress stdout output"));
1366
+ command.addOption(new Option("--dry-run", "Resolve request details without calling the API"));
1367
+ }
1368
+ function buildRequest(definition, options, body) {
1369
+ let resolvedPath = definition.api.path;
1370
+ const query = {};
1371
+ for (const parameter of definition.parameters) {
1372
+ const value = asOptionalString(options[toOptionKey(parameter.name)]);
1373
+ validateIdLikeInput(parameter.name, value, `parameter ${parameter.name}`);
1374
+ if (parameter.in === "path") {
1375
+ if (!value) {
1376
+ throw new Error(`Missing required path parameter: ${parameter.name}`);
1377
+ }
1378
+ resolvedPath = resolvedPath.replace(`{${parameter.name}}`, encodeURIComponent(value));
1379
+ continue;
1380
+ }
1381
+ if (parameter.in === "query") {
1382
+ query[parameter.name] = value;
1383
+ }
1384
+ }
1385
+ return {
1386
+ method: definition.api.method,
1387
+ path: resolvedPath,
1388
+ query,
1389
+ body
1390
+ };
1391
+ }
1392
+ function sanitizeRequestBody(definition, body) {
1393
+ if (body === void 0) {
1394
+ return void 0;
1395
+ }
1396
+ if (definition.module === "workflow" && definition.name === "createForm") {
1397
+ return sanitizeWorkflowCreateFormBody(body);
1398
+ }
1399
+ return body;
1400
+ }
1401
+ function sanitizeWorkflowCreateFormBody(body) {
1402
+ if (!isRecord(body)) {
1403
+ return body;
1404
+ }
1405
+ const sanitized = { ...body };
1406
+ delete sanitized.formId;
1407
+ if (Array.isArray(body.fields)) {
1408
+ sanitized.fields = body.fields.map((field) => {
1409
+ if (!isRecord(field)) {
1410
+ return field;
1411
+ }
1412
+ const sanitizedField = { ...field };
1413
+ delete sanitizedField.fieldId;
1414
+ return sanitizedField;
1415
+ });
1416
+ }
1417
+ return sanitized;
1418
+ }
1419
+ function validateRequestBody(definition, body) {
1420
+ if (!definition.hasRequestBody || body === void 0 || !definition.requestBodySchema) {
1421
+ return;
1422
+ }
1423
+ const issues = [];
1424
+ validateValueAgainstSchema(body, definition.requestBodySchema, "$", issues);
1425
+ validateIdLikeBodyValues(body, "$", issues);
1426
+ validateCustomBodyRules(definition, body, issues);
1427
+ if (issues.length > 0) {
1428
+ throw new Error(`Request body validation failed for ${definition.module} ${definition.name}:
1429
+ ${issues.join("\n")}`);
1430
+ }
1431
+ }
1432
+ function validateCustomBodyRules(definition, body, issues) {
1433
+ if (!isRecord(body)) {
1434
+ return;
1435
+ }
1436
+ if (definition.module === "project" && definition.name === "createPlan") {
1437
+ const type = body.type;
1438
+ if (typeof type !== "string" || !createPlanTypes.has(type)) {
1439
+ issues.push(
1440
+ `$.type: invalid module type ${JSON.stringify(type)}. Allowed values: ${Array.from(createPlanTypes).join(", ")}`
1441
+ );
1442
+ }
1443
+ }
1444
+ }
1445
+ function validateValueAgainstSchema(value, schema, path3, issues) {
1446
+ if (value === void 0 || value === null) {
1447
+ return;
1448
+ }
1449
+ if (schema.type === "object" && isRecord(value)) {
1450
+ for (const requiredKey of schema.required ?? []) {
1451
+ if (!(requiredKey in value)) {
1452
+ issues.push(`${path3}.${requiredKey}: missing required field`);
1453
+ }
1454
+ }
1455
+ for (const [propertyName, propertySchema] of Object.entries(schema.properties ?? {})) {
1456
+ if (propertyName in value) {
1457
+ validateValueAgainstSchema(value[propertyName], propertySchema, `${path3}.${propertyName}`, issues);
1458
+ }
1459
+ }
1460
+ return;
1461
+ }
1462
+ if (schema.type === "array" && Array.isArray(value)) {
1463
+ value.forEach((item, index) => {
1464
+ if (schema.items) {
1465
+ validateValueAgainstSchema(item, schema.items, `${path3}[${index}]`, issues);
1466
+ }
1467
+ });
1468
+ return;
1469
+ }
1470
+ if (schema.type === "string" && typeof value === "string") {
1471
+ validateStringByDescription(value, schema.description, path3, issues);
1472
+ }
1473
+ }
1474
+ function validateIdLikeBodyValues(value, path3, issues) {
1475
+ if (Array.isArray(value)) {
1476
+ value.forEach((item, index) => {
1477
+ validateIdLikeBodyValues(item, `${path3}[${index}]`, issues);
1478
+ });
1479
+ return;
1480
+ }
1481
+ if (!isRecord(value)) {
1482
+ return;
1483
+ }
1484
+ for (const [key, fieldValue] of Object.entries(value)) {
1485
+ const nextPath = `${path3}.${key}`;
1486
+ if (looksLikeIdField2(key)) {
1487
+ const issue = getIdLikeValidationIssue(fieldValue, nextPath);
1488
+ if (issue) {
1489
+ issues.push(issue);
1490
+ }
1491
+ }
1492
+ validateIdLikeBodyValues(fieldValue, nextPath, issues);
1493
+ }
1494
+ }
1495
+ function validateIdLikeInput(name, value, label) {
1496
+ if (!looksLikeIdField2(name) || value === void 0) {
1497
+ return;
1498
+ }
1499
+ const issue = getIdLikeValidationIssue(value, label);
1500
+ if (issue) {
1501
+ throw new Error(issue);
1502
+ }
1503
+ }
1504
+ function looksLikeIdField2(name) {
1505
+ return /ids?$/i.test(name);
1506
+ }
1507
+ function getIdLikeValidationIssue(value, label) {
1508
+ if (typeof value === "number") {
1509
+ return `${label}: invalid id value ${value}. Id fields must use string ids, not numbers.`;
1510
+ }
1511
+ if (typeof value === "string") {
1512
+ const trimmed = value.trim();
1513
+ if (trimmed.length === 0) {
1514
+ return void 0;
1515
+ }
1516
+ if (/^\d+$/.test(trimmed)) {
1517
+ return `${label}: invalid id value "${value}". Id fields must not be pure digits.`;
1518
+ }
1519
+ if (/^\d+(,\d+)+$/.test(trimmed)) {
1520
+ return `${label}: invalid id value "${value}". Id lists must not contain pure numeric ids.`;
1521
+ }
1522
+ return void 0;
1523
+ }
1524
+ if (Array.isArray(value)) {
1525
+ for (let index = 0; index < value.length; index += 1) {
1526
+ const nestedIssue = getIdLikeValidationIssue(value[index], `${label}[${index}]`);
1527
+ if (nestedIssue) {
1528
+ return nestedIssue;
1529
+ }
1530
+ }
1531
+ }
1532
+ return void 0;
1533
+ }
1534
+ function validateStringByDescription(value, description, path3, issues) {
1535
+ if (!description) {
1536
+ return;
1537
+ }
1538
+ if (description.includes("\u4EC5\u652F\u6301yyyy-MM-dd\u3001yyyy-MM-dd'T'HH:mm:ssXXX\u4E24\u79CD\u683C\u5F0F")) {
1539
+ const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(value);
1540
+ const isOffsetDateTime = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$/.test(value);
1541
+ if (!isDateOnly && !isOffsetDateTime) {
1542
+ issues.push(`${path3}: invalid format "${value}". Allowed formats: yyyy-MM-dd or yyyy-MM-dd'T'HH:mm:ssXXX`);
1543
+ }
1544
+ }
1545
+ }
1546
+ async function readBody(options) {
1547
+ if (options.body && options.bodyFile) {
1548
+ throw new Error("Use either --body or --body-file, not both.");
1549
+ }
1550
+ if (options.body) {
1551
+ return JSON.parse(options.body);
1552
+ }
1553
+ if (options.bodyFile) {
1554
+ return JSON.parse(await readFile2(options.bodyFile, "utf8"));
1555
+ }
1556
+ return void 0;
1557
+ }
1558
+ function optionName(parameterName) {
1559
+ return `--${toKebabCase(parameterName)} <value>`;
1560
+ }
1561
+ function toOptionKey(parameterName) {
1562
+ return toCamelCase(toKebabCase(parameterName));
1563
+ }
1564
+ function toKebabCase(value) {
1565
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
1566
+ }
1567
+ function toCamelCase(value) {
1568
+ return value.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
1569
+ }
1570
+ function tokenize(input) {
1571
+ const normalized = normalizeText(input);
1572
+ const tokens = normalized.split(/[\s/_-]+/).filter(Boolean);
1573
+ const cjkRuns = normalized.match(/[\p{Script=Han}]{2,}/gu) ?? [];
1574
+ for (const run of cjkRuns) {
1575
+ tokens.push(run);
1576
+ for (let index = 0; index < run.length - 1; index += 1) {
1577
+ tokens.push(run.slice(index, index + 2));
1578
+ }
1579
+ }
1580
+ return Array.from(new Set(tokens));
1581
+ }
1582
+ function scoreCommand(command, query, keywords) {
1583
+ let score = 0;
1584
+ const haystack = normalizeText([
1585
+ command.name,
1586
+ ...command.aliases,
1587
+ command.summary,
1588
+ command.description,
1589
+ command.api?.path ?? "",
1590
+ ...command.keywords,
1591
+ ...command.parameters.map((parameter) => parameter.name),
1592
+ ...command.parameters.map((parameter) => parameter.description ?? "")
1593
+ ].join(" "));
1594
+ const normalizedQuery = normalizeText(query);
1595
+ const matchedIntentKeywords = keywords.filter((keyword) => haystack.includes(keyword));
1596
+ if (normalizedQuery && haystack.includes(normalizedQuery)) {
1597
+ score += Math.max(8, normalizedQuery.length);
1598
+ }
1599
+ for (const keyword of matchedIntentKeywords) {
1600
+ score += keyword.length > 6 ? 6 : keyword.length > 3 ? 3 : 1;
1601
+ }
1602
+ score += scoreRsqIntentBoost(command, normalizedQuery, matchedIntentKeywords);
1603
+ for (const antiKeyword of command.antiKeywords ?? []) {
1604
+ const normalizedAntiKeyword = normalizeText(antiKeyword);
1605
+ if (!normalizedAntiKeyword) {
1606
+ continue;
1607
+ }
1608
+ if (normalizedQuery.includes(normalizedAntiKeyword) || haystack.includes(normalizedAntiKeyword)) {
1609
+ score -= normalizedAntiKeyword.length > 3 ? 4 : 2;
1610
+ }
1611
+ }
1612
+ return score;
1613
+ }
1614
+ var rsqDomainKeywords = [
1615
+ "\u65E5\u4E8B\u6E05",
1616
+ "rsq",
1617
+ "rishiqing",
1618
+ "\u9879\u76EE",
1619
+ "\u6A21\u5757",
1620
+ "\u5361\u7247",
1621
+ "\u4EFB\u52A1",
1622
+ "\u6D41\u7A0B",
1623
+ "\u6D41\u7A0B\u5E94\u7528",
1624
+ "\u6D41\u7A0B\u5B9E\u4F8B",
1625
+ "\u6B65\u9AA4",
1626
+ "\u8868\u5355",
1627
+ "\u5B57\u6BB5",
1628
+ "\u5BA1\u6838\u9879",
1629
+ "\u5BA1\u6279",
1630
+ "\u591A\u7EF4\u8868\u683C",
1631
+ "\u6570\u636E\u8868",
1632
+ "\u6570\u636E",
1633
+ "\u6210\u5458",
1634
+ "\u8D1F\u8D23\u4EBA",
1635
+ "\u6267\u884C\u4EBA",
1636
+ "\u53C2\u4E0E\u4EBA"
1637
+ ].map(normalizeText);
1638
+ var rsqModuleIntentKeywords = {
1639
+ project: ["\u9879\u76EE", "\u6A21\u5757", "\u5361\u7247", "\u9879\u76EE\u6210\u5458", "\u9879\u76EE\u4E0B\u8BA1\u5212", "\u9879\u76EE\u6982\u89C8"],
1640
+ task: ["\u4EFB\u52A1", "\u5361\u7247", "\u6267\u884C\u4EBA", "\u8D1F\u8D23\u4EBA", "\u53C2\u4E0E\u4EBA", "\u8BC4\u8BBA", "\u5B8C\u6210\u4EFB\u52A1"],
1641
+ workflow: ["\u6D41\u7A0B", "\u6D41\u7A0B\u5E94\u7528", "\u6D41\u7A0B\u5B9E\u4F8B", "\u6B65\u9AA4", "\u8868\u5355", "\u5B57\u6BB5", "\u5BA1\u6838\u9879", "\u5BA1\u6279"],
1642
+ datasheet: ["\u591A\u7EF4\u8868\u683C", "\u6570\u636E\u8868", "\u8BB0\u5F55", "\u5B57\u6BB5", "\u6570\u636E"],
1643
+ contacts: ["\u6210\u5458", "\u7528\u6237", "\u901A\u8BAF\u5F55", "\u5458\u5DE5", "\u59D3\u540D\u89E3\u6790"]
1644
+ };
1645
+ var actionIntentKeywords = [
1646
+ { action: "create", keywords: ["\u521B\u5EFA", "\u65B0\u5EFA", "\u65B0\u589E", "\u6DFB\u52A0", "\u5EFA\u4E00\u4E2A", "\u5EFA"] },
1647
+ { action: "get", keywords: ["\u67E5\u8BE2", "\u67E5\u770B", "\u83B7\u53D6", "\u8BE6\u60C5", "\u67E5\u67D0\u4E2A", "\u67E5\u4E00\u4E0B", "\u67E5"] },
1648
+ { action: "list", keywords: ["\u5217\u8868", "\u5168\u90E8", "\u6279\u91CF\u83B7\u53D6", "\u6709\u54EA\u4E9B", "\u5217\u51FA"] },
1649
+ { action: "update", keywords: ["\u66F4\u65B0", "\u4FEE\u6539", "\u7F16\u8F91", "\u8BBE\u7F6E", "\u8C03\u6574", "\u66F4\u6362"] },
1650
+ { action: "delete", keywords: ["\u5220\u9664", "\u79FB\u9664"] },
1651
+ { action: "complete", keywords: ["\u5B8C\u6210", "\u63D0\u4EA4", "\u901A\u8FC7", "\u62D2\u7EDD", "\u5173\u95ED"] }
1652
+ ];
1653
+ function scoreRsqIntentBoost(command, normalizedQuery, matchedIntentKeywords) {
1654
+ let score = 0;
1655
+ if (includesAny(normalizedQuery, rsqDomainKeywords)) {
1656
+ score += 2;
1657
+ }
1658
+ const moduleIntentKeywords = rsqModuleIntentKeywords[command.module] ?? [];
1659
+ const matchedModuleIntents = moduleIntentKeywords.filter((keyword) => normalizedQuery.includes(normalizeText(keyword)));
1660
+ if (matchedModuleIntents.length > 0) {
1661
+ score += 6 + matchedModuleIntents.length * 2;
1662
+ }
1663
+ if (command.module === "workflow" && matchedIntentKeywords.some((keyword) => keyword.includes("\u8868\u5355\u5B57\u6BB5") || keyword.includes("\u5B57\u6BB5\u503C"))) {
1664
+ score += 3;
1665
+ }
1666
+ if (command.module === "task" && matchedIntentKeywords.some((keyword) => keyword.includes("\u8D1F\u8D23\u4EBA") || keyword.includes("\u6267\u884C\u4EBA") || keyword.includes("\u53C2\u4E0E\u4EBA"))) {
1667
+ score += 3;
1668
+ }
1669
+ if (command.module === "project" && matchedIntentKeywords.some((keyword) => keyword.includes("\u6A21\u5757") || keyword.includes("\u5361\u7247"))) {
1670
+ score += 3;
1671
+ }
1672
+ if (command.module === "datasheet" && matchedIntentKeywords.some((keyword) => keyword.includes("\u591A\u7EF4\u8868\u683C") || keyword.includes("\u6570\u636E\u8868") || keyword === "\u6570\u636E")) {
1673
+ score += 3;
1674
+ }
1675
+ score += scoreActionIntentBoost(command, normalizedQuery);
1676
+ return score;
1677
+ }
1678
+ function scoreActionIntentBoost(command, normalizedQuery) {
1679
+ const matchedActions = actionIntentKeywords.filter(({ keywords }) => keywords.some((keyword) => normalizedQuery.includes(normalizeText(keyword)))).map(({ action }) => action);
1680
+ if (matchedActions.length === 0) {
1681
+ return 0;
1682
+ }
1683
+ const commandName = command.name.toLowerCase();
1684
+ let score = 0;
1685
+ for (const action of matchedActions) {
1686
+ if (commandName.startsWith(action)) {
1687
+ score += 5;
1688
+ continue;
1689
+ }
1690
+ if (action === "get" && commandName.startsWith("list")) {
1691
+ score += 2;
1692
+ continue;
1693
+ }
1694
+ if (action === "list" && commandName.startsWith("get")) {
1695
+ score += 1;
1696
+ continue;
1697
+ }
1698
+ }
1699
+ return score;
1700
+ }
1701
+ function normalizeText(value) {
1702
+ return value.toLowerCase().trim();
1703
+ }
1704
+ function includesAny(value, patterns) {
1705
+ return patterns.some((pattern) => value.includes(pattern));
1706
+ }
1707
+ function exposurePriority(command) {
1708
+ switch (command.exposure) {
1709
+ case "public":
1710
+ return 3;
1711
+ case "composed":
1712
+ return 2;
1713
+ case "internal":
1714
+ return 1;
1715
+ default:
1716
+ return 0;
1717
+ }
1718
+ }
1719
+ function parsePositiveInteger(value) {
1720
+ const parsed = Number.parseInt(value, 10);
1721
+ if (!Number.isFinite(parsed) || parsed <= 0) {
1722
+ throw new InvalidArgumentError("Expected a positive integer.");
1723
+ }
1724
+ return parsed;
1725
+ }
1726
+ function asOptionalString(value) {
1727
+ if (typeof value === "string" && value.length > 0) {
1728
+ return value;
1729
+ }
1730
+ return void 0;
1731
+ }
1732
+ function isRecord(value) {
1733
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1734
+ }
1735
+ function ensureTrailingSlash2(input) {
1736
+ return input.endsWith("/") ? input : `${input}/`;
1737
+ }
1738
+ function extractUserRows(payload) {
1739
+ if (typeof payload !== "object" || payload === null || !("data" in payload) || !Array.isArray(payload.data)) {
1740
+ throw new Error("Unexpected contacts response shape.");
1741
+ }
1742
+ return payload.data.filter(
1743
+ (row) => typeof row === "object" && row !== null && typeof row.userId === "string" && typeof row.userName === "string"
1744
+ ).map((row) => ({
1745
+ userId: row.userId,
1746
+ userName: row.userName
1747
+ }));
1748
+ }
1749
+ async function resolveUserByName(userName) {
1750
+ const payload = await executeRequest({
1751
+ method: "GET",
1752
+ path: "/rsq/api/v1/contacts/list-all-user"
1753
+ });
1754
+ const users = extractUserRows(payload);
1755
+ const exactMatches = users.filter((user) => user.userName === userName);
1756
+ if (exactMatches.length === 1) {
1757
+ return {
1758
+ userName,
1759
+ userId: exactMatches[0].userId,
1760
+ matchType: "exact"
1761
+ };
1762
+ }
1763
+ if (exactMatches.length > 1) {
1764
+ throw new Error(
1765
+ `Ambiguous userName "${userName}". Matched users: ${exactMatches.map((user) => `${user.userName}(${user.userId})`).join(", ")}`
1766
+ );
1767
+ }
1768
+ const fuzzyMatches = users.filter((user) => user.userName.includes(userName));
1769
+ if (fuzzyMatches.length === 1) {
1770
+ return {
1771
+ userName,
1772
+ userId: fuzzyMatches[0].userId,
1773
+ matchType: "fuzzy",
1774
+ matchedUserName: fuzzyMatches[0].userName
1775
+ };
1776
+ }
1777
+ if (fuzzyMatches.length > 1) {
1778
+ throw new Error(
1779
+ `No exact match for "${userName}". Fuzzy matches: ${fuzzyMatches.map((user) => `${user.userName}(${user.userId})`).join(", ")}`
1780
+ );
1781
+ }
1782
+ throw new Error(`No user matched userName "${userName}".`);
1783
+ }
1784
+ function buildTaskUserByNameRequest(commandName, taskId, userId) {
1785
+ if (commandName === "setTaskExecutorByName") {
1786
+ return {
1787
+ method: "PUT",
1788
+ path: `/rsq/api/v1/tasks/${encodeURIComponent(taskId)}/executor`,
1789
+ body: {
1790
+ executorUserIds: [userId]
1791
+ }
1792
+ };
1793
+ }
1794
+ if (commandName === "setTaskResponsibleByName") {
1795
+ return {
1796
+ method: "PUT",
1797
+ path: `/rsq/api/v1/tasks/${encodeURIComponent(taskId)}/responsible`,
1798
+ body: {
1799
+ responsibleId: userId
1800
+ }
1801
+ };
1802
+ }
1803
+ if (commandName === "addTaskParticipantByName") {
1804
+ return {
1805
+ method: "POST",
1806
+ path: `/rsq/api/v1/tasks/${encodeURIComponent(taskId)}/participants`,
1807
+ body: {
1808
+ participantUserIds: [userId]
1809
+ }
1810
+ };
1811
+ }
1812
+ throw new Error(`Unsupported composed task command: ${commandName}`);
1813
+ }
1814
+ function parseNameList(value) {
1815
+ if (!value) return [];
1816
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
1817
+ }
1818
+ main().catch((error) => {
1819
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}
1820
+ `);
1821
+ process.exitCode = 1;
1822
+ });
1823
+ //# sourceMappingURL=cli.js.map