@ai-setting/roy-agent-cli 1.5.84 → 1.5.86

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.
@@ -7320,7 +7320,7 @@ var require_dist = __commonJS((exports) => {
7320
7320
  var require_package = __commonJS((exports, module) => {
7321
7321
  module.exports = {
7322
7322
  name: "@ai-setting/roy-agent-cli",
7323
- version: "1.5.84",
7323
+ version: "1.5.86",
7324
7324
  type: "module",
7325
7325
  description: "CLI for roy-agent - Non-interactive command execution",
7326
7326
  main: "./dist/index.js",
@@ -9000,16 +9000,13 @@ class EventMessageFormatter {
9000
9000
  }
9001
9001
  formatTaskProgress(payload) {
9002
9002
  const desc = payload.description || "未命名任务";
9003
- const progress = payload.progress !== undefined ? `${payload.progress}%` : "";
9004
9003
  const message = payload.progressMessage || "";
9005
9004
  const taskId = payload.associatedTaskId;
9006
9005
  let result = `${this.prefix} \uD83D\uDD04 后台任务「${desc}」`;
9007
9006
  if (taskId !== undefined) {
9008
9007
  result += `[Task #${taskId}] `;
9009
9008
  }
9010
- result += `进度更新`;
9011
- if (progress)
9012
- result += `: ${progress}`;
9009
+ result += `还在进行中,请耐心等待。需查询具体进度可调用 task_get / task_operation_list 等工具`;
9013
9010
  if (message)
9014
9011
  result += ` - ${message}`;
9015
9012
  return result;
@@ -9255,8 +9252,16 @@ class InputHandler {
9255
9252
  buffer = [];
9256
9253
  static USER_PROMPT = "❯ ";
9257
9254
  static CONTINUATION_PROMPT = "... ";
9255
+ static filterControlChars(input) {
9256
+ if (!input)
9257
+ return "";
9258
+ return input.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
9259
+ }
9258
9260
  pushLine(line) {
9259
- this.buffer.push(line);
9261
+ if (line == null) {
9262
+ return;
9263
+ }
9264
+ this.buffer.push(InputHandler.filterControlChars(line));
9260
9265
  }
9261
9266
  getBuffer() {
9262
9267
  return this.buffer.join(`
@@ -9341,6 +9346,7 @@ class REPL {
9341
9346
  isShuttingDown = false;
9342
9347
  env = null;
9343
9348
  inputHandler;
9349
+ keypressHandler;
9344
9350
  constructor(options, env) {
9345
9351
  this.options = options;
9346
9352
  this.env = env ?? null;
@@ -9361,6 +9367,17 @@ class REPL {
9361
9367
  return;
9362
9368
  }
9363
9369
  this.isShuttingDown = true;
9370
+ if (process.stdin.isTTY) {
9371
+ try {
9372
+ process.stdin.setRawMode?.(false);
9373
+ } catch {}
9374
+ }
9375
+ if (this.keypressHandler) {
9376
+ try {
9377
+ process.stdin.removeListener("keypress", this.keypressHandler);
9378
+ } catch {}
9379
+ this.keypressHandler = undefined;
9380
+ }
9364
9381
  this.rl.close();
9365
9382
  await this.options.onShutdown();
9366
9383
  }
@@ -9428,7 +9445,9 @@ ${COLORS.system("[没有正在执行的请求]")}`);
9428
9445
  if (process.stdin.isTTY) {
9429
9446
  process.stdin.setRawMode?.(true);
9430
9447
  }
9431
- process.stdin.on("keypress", (str, key) => {
9448
+ this.keypressHandler = (str, key) => {
9449
+ if (this.isShuttingDown)
9450
+ return;
9432
9451
  if (key.meta && key.name === "return") {
9433
9452
  readline2.moveCursor(process.stdout, 0, -1);
9434
9453
  readline2.clearLine(process.stdout, 0);
@@ -9438,7 +9457,8 @@ ${COLORS.system("[没有正在执行的请求]")}`);
9438
9457
  if (key.name === "escape") {
9439
9458
  this.handleEscKey();
9440
9459
  }
9441
- });
9460
+ };
9461
+ process.stdin.on("keypress", this.keypressHandler);
9442
9462
  return new Promise((resolve) => {
9443
9463
  this.rl.on("line", async (line) => {
9444
9464
  await this.handleLine(line.trim());
@@ -9452,6 +9472,9 @@ ${COLORS.system("[没有正在执行的请求]")}`);
9452
9472
  });
9453
9473
  }
9454
9474
  async handleLine(line) {
9475
+ if (this.isShuttingDown) {
9476
+ return;
9477
+ }
9455
9478
  if (line.startsWith("/") && this.inputHandler.getBuffer() === "") {
9456
9479
  await this.handleCommand(line.slice(1));
9457
9480
  this.inputHandler.reset();
@@ -17002,7 +17025,7 @@ var WorkflowListCommand = {
17002
17025
  describe: "列出所有已注册的 Workflow",
17003
17026
  builder: (yargs) => yargs.option("tag", {
17004
17027
  alias: "t",
17005
- describe: "按标签筛选",
17028
+ describe: "按标签筛选(可多次 --tag ai --tag cli 表示 AND)",
17006
17029
  type: "string"
17007
17030
  }).option("task-id", {
17008
17031
  alias: "i",
@@ -17022,6 +17045,27 @@ var WorkflowListCommand = {
17022
17045
  describe: "分页偏移",
17023
17046
  type: "number",
17024
17047
  default: 0
17048
+ }).option("page", {
17049
+ describe: "页码(1-based,与 --page-size 配合;与 --offset 互斥)",
17050
+ type: "number",
17051
+ default: 1
17052
+ }).option("page-size", {
17053
+ describe: "每页大小(与 --page 配合;等价于 --limit)",
17054
+ type: "number"
17055
+ }).check((argv) => {
17056
+ if (typeof argv.page === "number" && argv.page < 1) {
17057
+ throw new Error("--page must be >= 1");
17058
+ }
17059
+ if (typeof argv.pageSize === "number" && argv.pageSize < 1) {
17060
+ throw new Error("--page-size must be >= 1");
17061
+ }
17062
+ if (typeof argv.limit === "number" && argv.limit < 1) {
17063
+ throw new Error("--limit must be >= 1");
17064
+ }
17065
+ if (typeof argv.offset === "number" && argv.offset < 0) {
17066
+ throw new Error("--offset must be >= 0");
17067
+ }
17068
+ return true;
17025
17069
  }).option("json", {
17026
17070
  alias: "j",
17027
17071
  describe: "JSON 格式输出",
@@ -17056,12 +17100,20 @@ var WorkflowListCommand = {
17056
17100
  output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
17057
17101
  process.exit(1);
17058
17102
  }
17103
+ const tagsArg = args.tag;
17104
+ const tagsList = Array.isArray(tagsArg) ? tagsArg : tagsArg ? [tagsArg] : [];
17105
+ const pageSize = typeof args.pageSize === "number" ? args.pageSize : typeof args.limit === "number" ? args.limit : 50;
17106
+ const page = typeof args.page === "number" ? args.page : 1;
17107
+ const effectiveOffset = (page - 1) * pageSize;
17108
+ const userOffset = typeof args.offset === "number" ? args.offset : 0;
17109
+ const finalOffset = page > 1 ? effectiveOffset : userOffset;
17059
17110
  const tool = createWorkflowListTool(service);
17060
17111
  const result = await tool.execute({
17061
- tag: args.tag,
17062
- search: args.search,
17063
- limit: args.limit,
17064
- offset: args.offset
17112
+ tag: tagsList.length === 1 ? tagsList[0] : undefined,
17113
+ tags: tagsList.length > 1 ? tagsList : undefined,
17114
+ search: args.search !== undefined && typeof args.search === "string" ? args.search : undefined,
17115
+ limit: pageSize,
17116
+ offset: finalOffset
17065
17117
  }, {});
17066
17118
  if (!result.success) {
17067
17119
  output.error(result.error || "Failed to list workflows");
@@ -17069,6 +17121,8 @@ var WorkflowListCommand = {
17069
17121
  }
17070
17122
  const data = JSON.parse(result.output);
17071
17123
  const workflows = data.workflows || [];
17124
+ const availableTags = data.available_tags || [];
17125
+ const total = typeof data.total === "number" ? data.total : workflows.length;
17072
17126
  if (args.json) {
17073
17127
  output.json({
17074
17128
  workflows: workflows.map((w) => ({
@@ -17079,7 +17133,12 @@ var WorkflowListCommand = {
17079
17133
  createdAt: w.created_at,
17080
17134
  updatedAt: w.created_at
17081
17135
  })),
17082
- total: data.total || workflows.length
17136
+ available_tags: availableTags,
17137
+ total,
17138
+ count: workflows.length,
17139
+ limit: pageSize,
17140
+ offset: finalOffset,
17141
+ page: page > 1 ? page : Math.floor(finalOffset / pageSize) + 1
17083
17142
  });
17084
17143
  } else {
17085
17144
  const renderWorkflows = workflows.map((w) => ({
@@ -17094,8 +17153,25 @@ var WorkflowListCommand = {
17094
17153
  metadata: {}
17095
17154
  }));
17096
17155
  output.log(renderWorkflowList(renderWorkflows));
17097
- output.log(chalk61.green(`
17098
- ${workflows.length} Workflow`));
17156
+ if (total > workflows.length) {
17157
+ const start = finalOffset + 1;
17158
+ const end = finalOffset + workflows.length;
17159
+ output.log(chalk61.green(`
17160
+ ✅ 显示 ${start}-${end} / 共 ${total} 个 Workflow`));
17161
+ const totalPages = Math.ceil(total / pageSize);
17162
+ const currentPage = Math.floor(finalOffset / pageSize) + 1;
17163
+ if (totalPages > 1) {
17164
+ output.log(chalk61.cyan(`\uD83D\uDCC4 第 ${currentPage} / ${totalPages} 页`));
17165
+ }
17166
+ } else {
17167
+ output.log(chalk61.green(`
17168
+ ✅ 共 ${total} 个 Workflow`));
17169
+ }
17170
+ if (availableTags.length > 0) {
17171
+ const tagsLine = availableTags.map((t) => `${t.name}(${t.count})`).join(", ");
17172
+ output.log(chalk61.cyan(`
17173
+ \uD83C\uDFF7️ Available tags: ${tagsLine}`));
17174
+ }
17099
17175
  }
17100
17176
  } catch (error) {
17101
17177
  output.error(`Failed to list workflows: ${error}`);
@@ -17763,8 +17839,9 @@ var WorkflowAddCommand = {
17763
17839
  type: "string"
17764
17840
  }).option("tags", {
17765
17841
  alias: "t",
17766
- describe: "标签(逗号分隔)",
17767
- type: "string"
17842
+ describe: 'Tags (1-3 required). Select from existing tag pool when possible. Run "roy-agent workflow tag list" first to see available tags. Supports both --tags "a,b,c" and --tags a --tags b --tags c forms.',
17843
+ type: "array",
17844
+ string: true
17768
17845
  }).option("task-id", {
17769
17846
  alias: "i",
17770
17847
  describe: "关联任务 ID",
@@ -17791,6 +17868,29 @@ var WorkflowAddCommand = {
17791
17868
  const output = new OutputService;
17792
17869
  const envService = new EnvironmentService(output);
17793
17870
  try {
17871
+ let parseTags = function(input) {
17872
+ if (input === undefined || input === null)
17873
+ return;
17874
+ const list = [];
17875
+ if (Array.isArray(input)) {
17876
+ for (const item of input) {
17877
+ if (typeof item === "string") {
17878
+ for (const t of item.split(",")) {
17879
+ const trimmed = t.trim();
17880
+ if (trimmed)
17881
+ list.push(trimmed);
17882
+ }
17883
+ }
17884
+ }
17885
+ } else if (typeof input === "string") {
17886
+ for (const t of input.split(",")) {
17887
+ const trimmed = t.trim();
17888
+ if (trimmed)
17889
+ list.push(trimmed);
17890
+ }
17891
+ }
17892
+ return list.length > 0 ? list : undefined;
17893
+ };
17794
17894
  await envService.create({ configPath: a.config });
17795
17895
  const env = envService.getEnvironment();
17796
17896
  if (!env) {
@@ -17902,7 +18002,19 @@ Agent output:
17902
18002
  output.error('Usage: roy-agent workflow add --file <path> | --desc "<description>"');
17903
18003
  process.exit(1);
17904
18004
  }
17905
- const tags = a.tags ? a.tags.split(",").map((t) => t.trim()) : undefined;
18005
+ const tags = parseTags(a.tags);
18006
+ if (!tags || tags.length === 0) {
18007
+ output.error("❌ --tags is required.");
18008
+ output.error(' Please provide 1-3 tags using --tags "tag1,tag2,tag3"');
18009
+ output.error(' Example: roy-agent workflow add --file my.yaml --tags "ai,ml,etl"');
18010
+ process.exit(1);
18011
+ }
18012
+ if (tags.length > 3) {
18013
+ output.error(`❌ Too many tags: received ${tags.length}, maximum is 3.`);
18014
+ output.error(' Please reduce to 1-3 tags using --tags "tag1,tag2,tag3"');
18015
+ output.error(' Example: roy-agent workflow add --file my.yaml --tags "ai,ml,etl"');
18016
+ process.exit(1);
18017
+ }
17906
18018
  const workflow = await service.createWorkflow(definition, {
17907
18019
  tags,
17908
18020
  taskId: a.taskId,
@@ -19140,11 +19252,412 @@ Error: ${error.message}`));
19140
19252
  }
19141
19253
  };
19142
19254
 
19255
+ // src/commands/workflow/commands/search.ts
19256
+ import chalk71 from "chalk";
19257
+ import { wrapFunction as wrapFunction3 } from "@ai-setting/roy-agent-core";
19258
+ var WorkflowSearchCommand = {
19259
+ command: "search",
19260
+ describe: "Search workflows by keyword and/or tags (multiple tags use AND filter). v2: returns available_tags.",
19261
+ builder: (yargs) => yargs.option("keyword", {
19262
+ alias: "k",
19263
+ describe: "Search keyword in name and description",
19264
+ type: "string"
19265
+ }).option("tag", {
19266
+ alias: "t",
19267
+ describe: "Filter by tag (can be specified multiple times for AND filter)",
19268
+ type: "array",
19269
+ string: true
19270
+ }).option("limit", {
19271
+ alias: "l",
19272
+ describe: "Maximum number of results",
19273
+ type: "number",
19274
+ default: 50
19275
+ }).option("offset", {
19276
+ alias: "o",
19277
+ describe: "Pagination offset",
19278
+ type: "number",
19279
+ default: 0
19280
+ }).option("json", {
19281
+ alias: "j",
19282
+ describe: "Output as JSON",
19283
+ type: "boolean",
19284
+ default: false
19285
+ }).option("yaml", {
19286
+ alias: "y",
19287
+ describe: "Output as YAML",
19288
+ type: "boolean",
19289
+ default: false
19290
+ }).option("config", {
19291
+ describe: "Config file path",
19292
+ type: "string"
19293
+ }).check((argv) => {
19294
+ const hasKeyword = typeof argv.keyword === "string" && argv.keyword.length > 0;
19295
+ const hasTags = Array.isArray(argv.tag) && argv.tag.length > 0;
19296
+ if (!hasKeyword && !hasTags) {
19297
+ throw new Error("At least one of --keyword or --tag must be provided");
19298
+ }
19299
+ if (Array.isArray(argv.tag)) {
19300
+ for (const t of argv.tag) {
19301
+ if (typeof t !== "string" || t.length === 0) {
19302
+ throw new Error("Empty tag values are not allowed");
19303
+ }
19304
+ if (t.length > 50) {
19305
+ throw new Error(`Tag "${t}" exceeds 50 characters`);
19306
+ }
19307
+ if (t.includes(",")) {
19308
+ throw new Error(`Tag "${t}" contains comma; pass them as separate --tags`);
19309
+ }
19310
+ }
19311
+ }
19312
+ return true;
19313
+ }),
19314
+ async handler(args) {
19315
+ const a = args;
19316
+ const output = new OutputService;
19317
+ const envService = new EnvironmentService(output);
19318
+ try {
19319
+ await runWorkflowSearch({
19320
+ keyword: a.keyword,
19321
+ tags: a.tag,
19322
+ limit: a.limit,
19323
+ offset: a.offset,
19324
+ asJson: a.json,
19325
+ asYaml: a.yaml,
19326
+ configPath: a.config,
19327
+ output,
19328
+ envService
19329
+ });
19330
+ } catch (error) {
19331
+ output.error(`Failed to search workflows: ${error}`);
19332
+ process.exit(1);
19333
+ } finally {
19334
+ await envService.dispose();
19335
+ }
19336
+ }
19337
+ };
19338
+ var _runWorkflowSearchImpl = async function(opts) {
19339
+ const { keyword, tags, limit, offset, asJson, asYaml, configPath, output, envService } = opts;
19340
+ await envService.create({ configPath });
19341
+ const env = envService.getEnvironment();
19342
+ if (!env) {
19343
+ output.error("Failed to create environment");
19344
+ process.exit(1);
19345
+ }
19346
+ const workflowComponent = env.getComponent("workflow");
19347
+ if (!workflowComponent) {
19348
+ output.error("WorkflowComponent not available");
19349
+ process.exit(1);
19350
+ }
19351
+ const service = workflowComponent.getService();
19352
+ if (!service) {
19353
+ output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
19354
+ process.exit(1);
19355
+ }
19356
+ const useWithTags = typeof service.searchWorkflowsWithTags === "function";
19357
+ const result = useWithTags ? await service.searchWorkflowsWithTags({
19358
+ keyword,
19359
+ tags,
19360
+ limit,
19361
+ offset
19362
+ }) : {
19363
+ workflows: service.searchWorkflows({ keyword, tags, limit, offset }),
19364
+ available_tags: []
19365
+ };
19366
+ const workflows = result.workflows ?? [];
19367
+ const availableTags = result.available_tags ?? [];
19368
+ if (asJson) {
19369
+ output.json({
19370
+ workflows: workflows.map((w) => ({
19371
+ name: w.name,
19372
+ version: w.version,
19373
+ description: w.description,
19374
+ tags: w.tags,
19375
+ createdAt: w.createdAt,
19376
+ updatedAt: w.updatedAt
19377
+ })),
19378
+ available_tags: availableTags,
19379
+ total: workflows.length,
19380
+ keyword,
19381
+ tags
19382
+ });
19383
+ return;
19384
+ }
19385
+ if (asYaml) {
19386
+ const yaml = await Promise.resolve().then(() => __toESM(require_dist(), 1));
19387
+ output.log(yaml.stringify({
19388
+ workflows: workflows.map((w) => ({
19389
+ name: w.name,
19390
+ version: w.version,
19391
+ description: w.description,
19392
+ tags: w.tags
19393
+ })),
19394
+ available_tags: availableTags,
19395
+ total: workflows.length,
19396
+ keyword,
19397
+ tags
19398
+ }));
19399
+ return;
19400
+ }
19401
+ const renderWorkflows = workflows.map((w) => ({
19402
+ id: w.name,
19403
+ name: w.name,
19404
+ version: w.version,
19405
+ description: w.description,
19406
+ tags: w.tags || [],
19407
+ createdAt: w.createdAt ? new Date(w.createdAt) : new Date,
19408
+ updatedAt: w.updatedAt ? new Date(w.updatedAt) : new Date,
19409
+ definition: { nodes: [], config: {}, inputs: {} },
19410
+ metadata: {}
19411
+ }));
19412
+ if (renderWorkflows.length === 0) {
19413
+ output.log(chalk71.yellow("\uD83D\uDD0D No workflows matched the search criteria."));
19414
+ output.log(chalk71.gray(` keyword: ${keyword || "(none)"}`));
19415
+ output.log(chalk71.gray(` tags: ${tags && tags.length > 0 ? tags.join(", ") : "(none)"}`));
19416
+ } else {
19417
+ output.log(chalk71.bold(`\uD83D\uDD0D Search results (${renderWorkflows.length}):`));
19418
+ output.log(renderWorkflowList(renderWorkflows));
19419
+ output.log(chalk71.green(`
19420
+ ✅ Found ${renderWorkflows.length} workflow(s)`));
19421
+ }
19422
+ if (availableTags.length > 0) {
19423
+ output.log("");
19424
+ output.log(chalk71.bold("\uD83D\uDCDA Available tags (sorted by usage_count):"));
19425
+ for (const t of availableTags) {
19426
+ const name = t.name ?? t.tag;
19427
+ const count = t.count ?? t.usage_count;
19428
+ output.log(chalk71.gray(` - ${name} (${count})`));
19429
+ }
19430
+ }
19431
+ };
19432
+ var runWorkflowSearch = wrapFunction3(_runWorkflowSearchImpl, "workflow.cli.search", {
19433
+ recordParams: true,
19434
+ recordResult: false,
19435
+ log: false
19436
+ });
19437
+
19438
+ // src/commands/workflow/commands/tag.ts
19439
+ import { wrapFunction as wrapFunction4 } from "@ai-setting/roy-agent-core";
19440
+ import chalk72 from "chalk";
19441
+ var WorkflowTagListCommand = {
19442
+ command: "list",
19443
+ describe: "List all workflow tags from the workflow_tags table (sorted by usage_count DESC by default). Use this BEFORE `workflow add` to discover existing tags and reuse them for semantic consistency.",
19444
+ builder: (yargs) => yargs.option("sort-by", {
19445
+ describe: "Sort field",
19446
+ choices: ["usage_count", "name", "created_at"],
19447
+ default: "usage_count"
19448
+ }).option("order", {
19449
+ describe: "Sort order",
19450
+ choices: ["asc", "desc"],
19451
+ default: "desc"
19452
+ }).option("limit", {
19453
+ describe: "Max number of tags to return",
19454
+ type: "number",
19455
+ default: 100
19456
+ }).option("json", {
19457
+ alias: "j",
19458
+ describe: "Output as JSON",
19459
+ type: "boolean",
19460
+ default: false
19461
+ }).option("config", {
19462
+ describe: "Config file path",
19463
+ type: "string"
19464
+ }),
19465
+ async handler(args) {
19466
+ const a = args;
19467
+ const output = new OutputService;
19468
+ const envService = new EnvironmentService(output);
19469
+ try {
19470
+ await runWorkflowTagList({
19471
+ sortBy: a.sortBy,
19472
+ order: a.order,
19473
+ limit: a.limit,
19474
+ asJson: a.json,
19475
+ configPath: a.config,
19476
+ output,
19477
+ envService
19478
+ });
19479
+ } catch (error) {
19480
+ output.error(`Failed to list workflow tags: ${error}`);
19481
+ process.exit(1);
19482
+ } finally {
19483
+ await envService.dispose();
19484
+ }
19485
+ }
19486
+ };
19487
+ var WorkflowTagGetCommand = {
19488
+ command: "get <name>",
19489
+ describe: "Get details of a single workflow tag (usage_count, created_at, updated_at). Returns error if tag not in the workflow_tags table.",
19490
+ builder: (yargs) => yargs.positional("name", {
19491
+ describe: "Tag name",
19492
+ type: "string"
19493
+ }).option("json", {
19494
+ alias: "j",
19495
+ describe: "Output as JSON",
19496
+ type: "boolean",
19497
+ default: false
19498
+ }).option("config", {
19499
+ describe: "Config file path",
19500
+ type: "string"
19501
+ }).check((argv) => {
19502
+ if (typeof argv.name !== "string" || argv.name.length === 0) {
19503
+ throw new Error("Tag name is required");
19504
+ }
19505
+ if (argv.name.length > 50) {
19506
+ throw new Error(`Tag name exceeds 50 characters`);
19507
+ }
19508
+ return true;
19509
+ }),
19510
+ async handler(args) {
19511
+ const a = args;
19512
+ const output = new OutputService;
19513
+ const envService = new EnvironmentService(output);
19514
+ try {
19515
+ await runWorkflowTagGet({
19516
+ name: a.name,
19517
+ asJson: a.json,
19518
+ configPath: a.config,
19519
+ output,
19520
+ envService
19521
+ });
19522
+ } catch (error) {
19523
+ output.error(`Failed to get tag: ${error}`);
19524
+ process.exit(1);
19525
+ } finally {
19526
+ await envService.dispose();
19527
+ }
19528
+ }
19529
+ };
19530
+ var _runWorkflowTagListImpl = async function(opts) {
19531
+ const { output, envService } = opts;
19532
+ await envService.create({ configPath: opts.configPath });
19533
+ const env = envService.getEnvironment();
19534
+ if (!env) {
19535
+ output.error("Failed to create environment");
19536
+ process.exit(1);
19537
+ }
19538
+ const workflowComponent = env.getComponent("workflow");
19539
+ if (!workflowComponent) {
19540
+ output.error("WorkflowComponent not available");
19541
+ process.exit(1);
19542
+ }
19543
+ const service = workflowComponent.getService();
19544
+ if (!service) {
19545
+ output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
19546
+ process.exit(1);
19547
+ }
19548
+ const result = await callListTags(service, {
19549
+ sortBy: opts.sortBy ?? "usage_count",
19550
+ order: opts.order ?? "desc",
19551
+ limit: opts.limit ?? 100
19552
+ });
19553
+ if (opts.asJson) {
19554
+ output.json({
19555
+ tags: result.tags,
19556
+ total: result.total
19557
+ });
19558
+ return;
19559
+ }
19560
+ if (result.tags.length === 0) {
19561
+ output.log(chalk72.yellow("\uD83C\uDFF7️ No tags in the workflow_tags table yet."));
19562
+ output.log(chalk72.gray(" Tags are created automatically when you run `workflow add --tags <name>`."));
19563
+ return;
19564
+ }
19565
+ output.log(chalk72.bold(`\uD83C\uDFF7️ Workflow tags (${result.total}):`));
19566
+ output.log(chalk72.gray(` ${padRight("NAME", 24)}${padRight("USAGE", 8)}${padRight("CREATED", 22)}`));
19567
+ for (const t of result.tags) {
19568
+ const line = ` ${padRight(t.name, 24)}${padRight(String(t.usage_count), 8)}${padRight(formatDate2(t.created_at), 22)}`;
19569
+ output.log(line);
19570
+ }
19571
+ output.log(chalk72.gray(`
19572
+ \uD83D\uDCA1 Tip: when adding a new workflow, prefer tags from this list to ensure semantic consistency.`));
19573
+ };
19574
+ var _runWorkflowTagGetImpl = async function(opts) {
19575
+ const { output, envService } = opts;
19576
+ await envService.create({ configPath: opts.configPath });
19577
+ const env = envService.getEnvironment();
19578
+ if (!env) {
19579
+ output.error("Failed to create environment");
19580
+ process.exit(1);
19581
+ }
19582
+ const workflowComponent = env.getComponent("workflow");
19583
+ if (!workflowComponent) {
19584
+ output.error("WorkflowComponent not available");
19585
+ process.exit(1);
19586
+ }
19587
+ const service = workflowComponent.getService();
19588
+ if (!service) {
19589
+ output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
19590
+ process.exit(1);
19591
+ }
19592
+ const tag = callGetTag(service, opts.name);
19593
+ if (!tag) {
19594
+ if (opts.asJson) {
19595
+ output.json({ error: "Tag not found", name: opts.name });
19596
+ } else {
19597
+ output.error(`Tag "${opts.name}" not found in the workflow_tags table.`);
19598
+ output.log(chalk72.gray(`Tip: run \`roy-agent workflow tag list\` to see available tags.`));
19599
+ }
19600
+ process.exitCode = 1;
19601
+ return;
19602
+ }
19603
+ if (opts.asJson) {
19604
+ output.json(tag);
19605
+ return;
19606
+ }
19607
+ output.log(chalk72.bold(`\uD83C\uDFF7️ Tag: ${tag.name}`));
19608
+ output.log(chalk72.gray(` usage_count: ${tag.usage_count}`));
19609
+ output.log(chalk72.gray(` created_at: ${tag.created_at}`));
19610
+ output.log(chalk72.gray(` updated_at: ${tag.updated_at}`));
19611
+ };
19612
+ async function callListTags(service, options) {
19613
+ if (typeof service.listTags !== "function") {
19614
+ return { tags: [], total: 0 };
19615
+ }
19616
+ return await service.listTags(options);
19617
+ }
19618
+ function callGetTag(service, name) {
19619
+ if (typeof service.getTag !== "function") {
19620
+ return;
19621
+ }
19622
+ return service.getTag(name);
19623
+ }
19624
+ function padRight(str, width) {
19625
+ if (str.length >= width)
19626
+ return str.slice(0, width - 1) + " ";
19627
+ return str + " ".repeat(width - str.length);
19628
+ }
19629
+ function formatDate2(iso) {
19630
+ try {
19631
+ const d = new Date(iso);
19632
+ if (isNaN(d.getTime()))
19633
+ return iso;
19634
+ return d.toISOString().replace("T", " ").slice(0, 19);
19635
+ } catch {
19636
+ return iso;
19637
+ }
19638
+ }
19639
+ var runWorkflowTagList = wrapFunction4(_runWorkflowTagListImpl, "workflow.cli.tag.list", {
19640
+ recordParams: true,
19641
+ recordResult: false,
19642
+ log: false
19643
+ });
19644
+ var runWorkflowTagGet = wrapFunction4(_runWorkflowTagGetImpl, "workflow.cli.tag.get", {
19645
+ recordParams: true,
19646
+ recordResult: false,
19647
+ log: false
19648
+ });
19649
+
19143
19650
  // src/commands/workflow/index.ts
19651
+ var WorkflowTagCommand = {
19652
+ command: "tag",
19653
+ describe: "Manage workflow tags — list and get from the workflow_tags table. Use BEFORE `workflow add` to discover existing tags.",
19654
+ builder: (yargs) => yargs.command(WorkflowTagListCommand).command(WorkflowTagGetCommand).demandCommand(1, "请指定 tag 子命令:list 或 get <name>").help(),
19655
+ handler() {}
19656
+ };
19144
19657
  var WorkflowCommand = {
19145
19658
  command: "workflow",
19146
19659
  describe: "Workflow DAG management and execution - create, run, and monitor workflows",
19147
- builder: (yargs) => yargs.command(WorkflowListCommand).command(WorkflowAddCommand).command(WorkflowNodesCommand).command(WorkflowValidateCommand).command(WorkflowGetCommand).command(WorkflowUpdateCommand).command(WorkflowRemoveCommand).command(WorkflowRunCommand).command(WorkflowStopCommand).command(WorkflowStatusCommand).demandCommand(1, "请指定子命令:list、add、validate、nodes、get、update、remove、run、stop、status\n\nResume: 使用 `workflow run -s <sessionId>` 恢复暂停的工作流").help(),
19660
+ builder: (yargs) => yargs.command(WorkflowListCommand).command(WorkflowAddCommand).command(WorkflowSearchCommand).command(WorkflowTagCommand).command(WorkflowNodesCommand).command(WorkflowValidateCommand).command(WorkflowGetCommand).command(WorkflowUpdateCommand).command(WorkflowRemoveCommand).command(WorkflowRunCommand).command(WorkflowStopCommand).command(WorkflowStatusCommand).demandCommand(1, "请指定子命令:list、add、search、tag、validate、nodes、get、update、remove、run、stop、status\n\nResume: 使用 `workflow run -s <sessionId>` 恢复暂停的工作流").help(),
19148
19661
  handler() {}
19149
19662
  };
19150
19663
 
package/dist/index.js CHANGED
@@ -7319,7 +7319,7 @@ var require_dist = __commonJS((exports) => {
7319
7319
  var require_package = __commonJS((exports, module) => {
7320
7320
  module.exports = {
7321
7321
  name: "@ai-setting/roy-agent-cli",
7322
- version: "1.5.84",
7322
+ version: "1.5.86",
7323
7323
  type: "module",
7324
7324
  description: "CLI for roy-agent - Non-interactive command execution",
7325
7325
  main: "./dist/index.js",
@@ -8999,16 +8999,13 @@ class EventMessageFormatter {
8999
8999
  }
9000
9000
  formatTaskProgress(payload) {
9001
9001
  const desc = payload.description || "未命名任务";
9002
- const progress = payload.progress !== undefined ? `${payload.progress}%` : "";
9003
9002
  const message = payload.progressMessage || "";
9004
9003
  const taskId = payload.associatedTaskId;
9005
9004
  let result = `${this.prefix} \uD83D\uDD04 后台任务「${desc}」`;
9006
9005
  if (taskId !== undefined) {
9007
9006
  result += `[Task #${taskId}] `;
9008
9007
  }
9009
- result += `进度更新`;
9010
- if (progress)
9011
- result += `: ${progress}`;
9008
+ result += `还在进行中,请耐心等待。需查询具体进度可调用 task_get / task_operation_list 等工具`;
9012
9009
  if (message)
9013
9010
  result += ` - ${message}`;
9014
9011
  return result;
@@ -9254,8 +9251,16 @@ class InputHandler {
9254
9251
  buffer = [];
9255
9252
  static USER_PROMPT = "❯ ";
9256
9253
  static CONTINUATION_PROMPT = "... ";
9254
+ static filterControlChars(input) {
9255
+ if (!input)
9256
+ return "";
9257
+ return input.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
9258
+ }
9257
9259
  pushLine(line) {
9258
- this.buffer.push(line);
9260
+ if (line == null) {
9261
+ return;
9262
+ }
9263
+ this.buffer.push(InputHandler.filterControlChars(line));
9259
9264
  }
9260
9265
  getBuffer() {
9261
9266
  return this.buffer.join(`
@@ -9340,6 +9345,7 @@ class REPL {
9340
9345
  isShuttingDown = false;
9341
9346
  env = null;
9342
9347
  inputHandler;
9348
+ keypressHandler;
9343
9349
  constructor(options, env) {
9344
9350
  this.options = options;
9345
9351
  this.env = env ?? null;
@@ -9360,6 +9366,17 @@ class REPL {
9360
9366
  return;
9361
9367
  }
9362
9368
  this.isShuttingDown = true;
9369
+ if (process.stdin.isTTY) {
9370
+ try {
9371
+ process.stdin.setRawMode?.(false);
9372
+ } catch {}
9373
+ }
9374
+ if (this.keypressHandler) {
9375
+ try {
9376
+ process.stdin.removeListener("keypress", this.keypressHandler);
9377
+ } catch {}
9378
+ this.keypressHandler = undefined;
9379
+ }
9363
9380
  this.rl.close();
9364
9381
  await this.options.onShutdown();
9365
9382
  }
@@ -9427,7 +9444,9 @@ ${COLORS.system("[没有正在执行的请求]")}`);
9427
9444
  if (process.stdin.isTTY) {
9428
9445
  process.stdin.setRawMode?.(true);
9429
9446
  }
9430
- process.stdin.on("keypress", (str, key) => {
9447
+ this.keypressHandler = (str, key) => {
9448
+ if (this.isShuttingDown)
9449
+ return;
9431
9450
  if (key.meta && key.name === "return") {
9432
9451
  readline2.moveCursor(process.stdout, 0, -1);
9433
9452
  readline2.clearLine(process.stdout, 0);
@@ -9437,7 +9456,8 @@ ${COLORS.system("[没有正在执行的请求]")}`);
9437
9456
  if (key.name === "escape") {
9438
9457
  this.handleEscKey();
9439
9458
  }
9440
- });
9459
+ };
9460
+ process.stdin.on("keypress", this.keypressHandler);
9441
9461
  return new Promise((resolve) => {
9442
9462
  this.rl.on("line", async (line) => {
9443
9463
  await this.handleLine(line.trim());
@@ -9451,6 +9471,9 @@ ${COLORS.system("[没有正在执行的请求]")}`);
9451
9471
  });
9452
9472
  }
9453
9473
  async handleLine(line) {
9474
+ if (this.isShuttingDown) {
9475
+ return;
9476
+ }
9454
9477
  if (line.startsWith("/") && this.inputHandler.getBuffer() === "") {
9455
9478
  await this.handleCommand(line.slice(1));
9456
9479
  this.inputHandler.reset();
@@ -17001,7 +17024,7 @@ var WorkflowListCommand = {
17001
17024
  describe: "列出所有已注册的 Workflow",
17002
17025
  builder: (yargs) => yargs.option("tag", {
17003
17026
  alias: "t",
17004
- describe: "按标签筛选",
17027
+ describe: "按标签筛选(可多次 --tag ai --tag cli 表示 AND)",
17005
17028
  type: "string"
17006
17029
  }).option("task-id", {
17007
17030
  alias: "i",
@@ -17021,6 +17044,27 @@ var WorkflowListCommand = {
17021
17044
  describe: "分页偏移",
17022
17045
  type: "number",
17023
17046
  default: 0
17047
+ }).option("page", {
17048
+ describe: "页码(1-based,与 --page-size 配合;与 --offset 互斥)",
17049
+ type: "number",
17050
+ default: 1
17051
+ }).option("page-size", {
17052
+ describe: "每页大小(与 --page 配合;等价于 --limit)",
17053
+ type: "number"
17054
+ }).check((argv) => {
17055
+ if (typeof argv.page === "number" && argv.page < 1) {
17056
+ throw new Error("--page must be >= 1");
17057
+ }
17058
+ if (typeof argv.pageSize === "number" && argv.pageSize < 1) {
17059
+ throw new Error("--page-size must be >= 1");
17060
+ }
17061
+ if (typeof argv.limit === "number" && argv.limit < 1) {
17062
+ throw new Error("--limit must be >= 1");
17063
+ }
17064
+ if (typeof argv.offset === "number" && argv.offset < 0) {
17065
+ throw new Error("--offset must be >= 0");
17066
+ }
17067
+ return true;
17024
17068
  }).option("json", {
17025
17069
  alias: "j",
17026
17070
  describe: "JSON 格式输出",
@@ -17055,12 +17099,20 @@ var WorkflowListCommand = {
17055
17099
  output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
17056
17100
  process.exit(1);
17057
17101
  }
17102
+ const tagsArg = args.tag;
17103
+ const tagsList = Array.isArray(tagsArg) ? tagsArg : tagsArg ? [tagsArg] : [];
17104
+ const pageSize = typeof args.pageSize === "number" ? args.pageSize : typeof args.limit === "number" ? args.limit : 50;
17105
+ const page = typeof args.page === "number" ? args.page : 1;
17106
+ const effectiveOffset = (page - 1) * pageSize;
17107
+ const userOffset = typeof args.offset === "number" ? args.offset : 0;
17108
+ const finalOffset = page > 1 ? effectiveOffset : userOffset;
17058
17109
  const tool = createWorkflowListTool(service);
17059
17110
  const result = await tool.execute({
17060
- tag: args.tag,
17061
- search: args.search,
17062
- limit: args.limit,
17063
- offset: args.offset
17111
+ tag: tagsList.length === 1 ? tagsList[0] : undefined,
17112
+ tags: tagsList.length > 1 ? tagsList : undefined,
17113
+ search: args.search !== undefined && typeof args.search === "string" ? args.search : undefined,
17114
+ limit: pageSize,
17115
+ offset: finalOffset
17064
17116
  }, {});
17065
17117
  if (!result.success) {
17066
17118
  output.error(result.error || "Failed to list workflows");
@@ -17068,6 +17120,8 @@ var WorkflowListCommand = {
17068
17120
  }
17069
17121
  const data = JSON.parse(result.output);
17070
17122
  const workflows = data.workflows || [];
17123
+ const availableTags = data.available_tags || [];
17124
+ const total = typeof data.total === "number" ? data.total : workflows.length;
17071
17125
  if (args.json) {
17072
17126
  output.json({
17073
17127
  workflows: workflows.map((w) => ({
@@ -17078,7 +17132,12 @@ var WorkflowListCommand = {
17078
17132
  createdAt: w.created_at,
17079
17133
  updatedAt: w.created_at
17080
17134
  })),
17081
- total: data.total || workflows.length
17135
+ available_tags: availableTags,
17136
+ total,
17137
+ count: workflows.length,
17138
+ limit: pageSize,
17139
+ offset: finalOffset,
17140
+ page: page > 1 ? page : Math.floor(finalOffset / pageSize) + 1
17082
17141
  });
17083
17142
  } else {
17084
17143
  const renderWorkflows = workflows.map((w) => ({
@@ -17093,8 +17152,25 @@ var WorkflowListCommand = {
17093
17152
  metadata: {}
17094
17153
  }));
17095
17154
  output.log(renderWorkflowList(renderWorkflows));
17096
- output.log(chalk61.green(`
17097
- ${workflows.length} Workflow`));
17155
+ if (total > workflows.length) {
17156
+ const start = finalOffset + 1;
17157
+ const end = finalOffset + workflows.length;
17158
+ output.log(chalk61.green(`
17159
+ ✅ 显示 ${start}-${end} / 共 ${total} 个 Workflow`));
17160
+ const totalPages = Math.ceil(total / pageSize);
17161
+ const currentPage = Math.floor(finalOffset / pageSize) + 1;
17162
+ if (totalPages > 1) {
17163
+ output.log(chalk61.cyan(`\uD83D\uDCC4 第 ${currentPage} / ${totalPages} 页`));
17164
+ }
17165
+ } else {
17166
+ output.log(chalk61.green(`
17167
+ ✅ 共 ${total} 个 Workflow`));
17168
+ }
17169
+ if (availableTags.length > 0) {
17170
+ const tagsLine = availableTags.map((t) => `${t.name}(${t.count})`).join(", ");
17171
+ output.log(chalk61.cyan(`
17172
+ \uD83C\uDFF7️ Available tags: ${tagsLine}`));
17173
+ }
17098
17174
  }
17099
17175
  } catch (error) {
17100
17176
  output.error(`Failed to list workflows: ${error}`);
@@ -17762,8 +17838,9 @@ var WorkflowAddCommand = {
17762
17838
  type: "string"
17763
17839
  }).option("tags", {
17764
17840
  alias: "t",
17765
- describe: "标签(逗号分隔)",
17766
- type: "string"
17841
+ describe: 'Tags (1-3 required). Select from existing tag pool when possible. Run "roy-agent workflow tag list" first to see available tags. Supports both --tags "a,b,c" and --tags a --tags b --tags c forms.',
17842
+ type: "array",
17843
+ string: true
17767
17844
  }).option("task-id", {
17768
17845
  alias: "i",
17769
17846
  describe: "关联任务 ID",
@@ -17790,6 +17867,29 @@ var WorkflowAddCommand = {
17790
17867
  const output = new OutputService;
17791
17868
  const envService = new EnvironmentService(output);
17792
17869
  try {
17870
+ let parseTags = function(input) {
17871
+ if (input === undefined || input === null)
17872
+ return;
17873
+ const list = [];
17874
+ if (Array.isArray(input)) {
17875
+ for (const item of input) {
17876
+ if (typeof item === "string") {
17877
+ for (const t of item.split(",")) {
17878
+ const trimmed = t.trim();
17879
+ if (trimmed)
17880
+ list.push(trimmed);
17881
+ }
17882
+ }
17883
+ }
17884
+ } else if (typeof input === "string") {
17885
+ for (const t of input.split(",")) {
17886
+ const trimmed = t.trim();
17887
+ if (trimmed)
17888
+ list.push(trimmed);
17889
+ }
17890
+ }
17891
+ return list.length > 0 ? list : undefined;
17892
+ };
17793
17893
  await envService.create({ configPath: a.config });
17794
17894
  const env = envService.getEnvironment();
17795
17895
  if (!env) {
@@ -17901,7 +18001,19 @@ Agent output:
17901
18001
  output.error('Usage: roy-agent workflow add --file <path> | --desc "<description>"');
17902
18002
  process.exit(1);
17903
18003
  }
17904
- const tags = a.tags ? a.tags.split(",").map((t) => t.trim()) : undefined;
18004
+ const tags = parseTags(a.tags);
18005
+ if (!tags || tags.length === 0) {
18006
+ output.error("❌ --tags is required.");
18007
+ output.error(' Please provide 1-3 tags using --tags "tag1,tag2,tag3"');
18008
+ output.error(' Example: roy-agent workflow add --file my.yaml --tags "ai,ml,etl"');
18009
+ process.exit(1);
18010
+ }
18011
+ if (tags.length > 3) {
18012
+ output.error(`❌ Too many tags: received ${tags.length}, maximum is 3.`);
18013
+ output.error(' Please reduce to 1-3 tags using --tags "tag1,tag2,tag3"');
18014
+ output.error(' Example: roy-agent workflow add --file my.yaml --tags "ai,ml,etl"');
18015
+ process.exit(1);
18016
+ }
17905
18017
  const workflow = await service.createWorkflow(definition, {
17906
18018
  tags,
17907
18019
  taskId: a.taskId,
@@ -19139,11 +19251,412 @@ Error: ${error.message}`));
19139
19251
  }
19140
19252
  };
19141
19253
 
19254
+ // src/commands/workflow/commands/search.ts
19255
+ import chalk71 from "chalk";
19256
+ import { wrapFunction as wrapFunction3 } from "@ai-setting/roy-agent-core";
19257
+ var WorkflowSearchCommand = {
19258
+ command: "search",
19259
+ describe: "Search workflows by keyword and/or tags (multiple tags use AND filter). v2: returns available_tags.",
19260
+ builder: (yargs) => yargs.option("keyword", {
19261
+ alias: "k",
19262
+ describe: "Search keyword in name and description",
19263
+ type: "string"
19264
+ }).option("tag", {
19265
+ alias: "t",
19266
+ describe: "Filter by tag (can be specified multiple times for AND filter)",
19267
+ type: "array",
19268
+ string: true
19269
+ }).option("limit", {
19270
+ alias: "l",
19271
+ describe: "Maximum number of results",
19272
+ type: "number",
19273
+ default: 50
19274
+ }).option("offset", {
19275
+ alias: "o",
19276
+ describe: "Pagination offset",
19277
+ type: "number",
19278
+ default: 0
19279
+ }).option("json", {
19280
+ alias: "j",
19281
+ describe: "Output as JSON",
19282
+ type: "boolean",
19283
+ default: false
19284
+ }).option("yaml", {
19285
+ alias: "y",
19286
+ describe: "Output as YAML",
19287
+ type: "boolean",
19288
+ default: false
19289
+ }).option("config", {
19290
+ describe: "Config file path",
19291
+ type: "string"
19292
+ }).check((argv) => {
19293
+ const hasKeyword = typeof argv.keyword === "string" && argv.keyword.length > 0;
19294
+ const hasTags = Array.isArray(argv.tag) && argv.tag.length > 0;
19295
+ if (!hasKeyword && !hasTags) {
19296
+ throw new Error("At least one of --keyword or --tag must be provided");
19297
+ }
19298
+ if (Array.isArray(argv.tag)) {
19299
+ for (const t of argv.tag) {
19300
+ if (typeof t !== "string" || t.length === 0) {
19301
+ throw new Error("Empty tag values are not allowed");
19302
+ }
19303
+ if (t.length > 50) {
19304
+ throw new Error(`Tag "${t}" exceeds 50 characters`);
19305
+ }
19306
+ if (t.includes(",")) {
19307
+ throw new Error(`Tag "${t}" contains comma; pass them as separate --tags`);
19308
+ }
19309
+ }
19310
+ }
19311
+ return true;
19312
+ }),
19313
+ async handler(args) {
19314
+ const a = args;
19315
+ const output = new OutputService;
19316
+ const envService = new EnvironmentService(output);
19317
+ try {
19318
+ await runWorkflowSearch({
19319
+ keyword: a.keyword,
19320
+ tags: a.tag,
19321
+ limit: a.limit,
19322
+ offset: a.offset,
19323
+ asJson: a.json,
19324
+ asYaml: a.yaml,
19325
+ configPath: a.config,
19326
+ output,
19327
+ envService
19328
+ });
19329
+ } catch (error) {
19330
+ output.error(`Failed to search workflows: ${error}`);
19331
+ process.exit(1);
19332
+ } finally {
19333
+ await envService.dispose();
19334
+ }
19335
+ }
19336
+ };
19337
+ var _runWorkflowSearchImpl = async function(opts) {
19338
+ const { keyword, tags, limit, offset, asJson, asYaml, configPath, output, envService } = opts;
19339
+ await envService.create({ configPath });
19340
+ const env = envService.getEnvironment();
19341
+ if (!env) {
19342
+ output.error("Failed to create environment");
19343
+ process.exit(1);
19344
+ }
19345
+ const workflowComponent = env.getComponent("workflow");
19346
+ if (!workflowComponent) {
19347
+ output.error("WorkflowComponent not available");
19348
+ process.exit(1);
19349
+ }
19350
+ const service = workflowComponent.getService();
19351
+ if (!service) {
19352
+ output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
19353
+ process.exit(1);
19354
+ }
19355
+ const useWithTags = typeof service.searchWorkflowsWithTags === "function";
19356
+ const result = useWithTags ? await service.searchWorkflowsWithTags({
19357
+ keyword,
19358
+ tags,
19359
+ limit,
19360
+ offset
19361
+ }) : {
19362
+ workflows: service.searchWorkflows({ keyword, tags, limit, offset }),
19363
+ available_tags: []
19364
+ };
19365
+ const workflows = result.workflows ?? [];
19366
+ const availableTags = result.available_tags ?? [];
19367
+ if (asJson) {
19368
+ output.json({
19369
+ workflows: workflows.map((w) => ({
19370
+ name: w.name,
19371
+ version: w.version,
19372
+ description: w.description,
19373
+ tags: w.tags,
19374
+ createdAt: w.createdAt,
19375
+ updatedAt: w.updatedAt
19376
+ })),
19377
+ available_tags: availableTags,
19378
+ total: workflows.length,
19379
+ keyword,
19380
+ tags
19381
+ });
19382
+ return;
19383
+ }
19384
+ if (asYaml) {
19385
+ const yaml = await Promise.resolve().then(() => __toESM(require_dist(), 1));
19386
+ output.log(yaml.stringify({
19387
+ workflows: workflows.map((w) => ({
19388
+ name: w.name,
19389
+ version: w.version,
19390
+ description: w.description,
19391
+ tags: w.tags
19392
+ })),
19393
+ available_tags: availableTags,
19394
+ total: workflows.length,
19395
+ keyword,
19396
+ tags
19397
+ }));
19398
+ return;
19399
+ }
19400
+ const renderWorkflows = workflows.map((w) => ({
19401
+ id: w.name,
19402
+ name: w.name,
19403
+ version: w.version,
19404
+ description: w.description,
19405
+ tags: w.tags || [],
19406
+ createdAt: w.createdAt ? new Date(w.createdAt) : new Date,
19407
+ updatedAt: w.updatedAt ? new Date(w.updatedAt) : new Date,
19408
+ definition: { nodes: [], config: {}, inputs: {} },
19409
+ metadata: {}
19410
+ }));
19411
+ if (renderWorkflows.length === 0) {
19412
+ output.log(chalk71.yellow("\uD83D\uDD0D No workflows matched the search criteria."));
19413
+ output.log(chalk71.gray(` keyword: ${keyword || "(none)"}`));
19414
+ output.log(chalk71.gray(` tags: ${tags && tags.length > 0 ? tags.join(", ") : "(none)"}`));
19415
+ } else {
19416
+ output.log(chalk71.bold(`\uD83D\uDD0D Search results (${renderWorkflows.length}):`));
19417
+ output.log(renderWorkflowList(renderWorkflows));
19418
+ output.log(chalk71.green(`
19419
+ ✅ Found ${renderWorkflows.length} workflow(s)`));
19420
+ }
19421
+ if (availableTags.length > 0) {
19422
+ output.log("");
19423
+ output.log(chalk71.bold("\uD83D\uDCDA Available tags (sorted by usage_count):"));
19424
+ for (const t of availableTags) {
19425
+ const name = t.name ?? t.tag;
19426
+ const count = t.count ?? t.usage_count;
19427
+ output.log(chalk71.gray(` - ${name} (${count})`));
19428
+ }
19429
+ }
19430
+ };
19431
+ var runWorkflowSearch = wrapFunction3(_runWorkflowSearchImpl, "workflow.cli.search", {
19432
+ recordParams: true,
19433
+ recordResult: false,
19434
+ log: false
19435
+ });
19436
+
19437
+ // src/commands/workflow/commands/tag.ts
19438
+ import { wrapFunction as wrapFunction4 } from "@ai-setting/roy-agent-core";
19439
+ import chalk72 from "chalk";
19440
+ var WorkflowTagListCommand = {
19441
+ command: "list",
19442
+ describe: "List all workflow tags from the workflow_tags table (sorted by usage_count DESC by default). Use this BEFORE `workflow add` to discover existing tags and reuse them for semantic consistency.",
19443
+ builder: (yargs) => yargs.option("sort-by", {
19444
+ describe: "Sort field",
19445
+ choices: ["usage_count", "name", "created_at"],
19446
+ default: "usage_count"
19447
+ }).option("order", {
19448
+ describe: "Sort order",
19449
+ choices: ["asc", "desc"],
19450
+ default: "desc"
19451
+ }).option("limit", {
19452
+ describe: "Max number of tags to return",
19453
+ type: "number",
19454
+ default: 100
19455
+ }).option("json", {
19456
+ alias: "j",
19457
+ describe: "Output as JSON",
19458
+ type: "boolean",
19459
+ default: false
19460
+ }).option("config", {
19461
+ describe: "Config file path",
19462
+ type: "string"
19463
+ }),
19464
+ async handler(args) {
19465
+ const a = args;
19466
+ const output = new OutputService;
19467
+ const envService = new EnvironmentService(output);
19468
+ try {
19469
+ await runWorkflowTagList({
19470
+ sortBy: a.sortBy,
19471
+ order: a.order,
19472
+ limit: a.limit,
19473
+ asJson: a.json,
19474
+ configPath: a.config,
19475
+ output,
19476
+ envService
19477
+ });
19478
+ } catch (error) {
19479
+ output.error(`Failed to list workflow tags: ${error}`);
19480
+ process.exit(1);
19481
+ } finally {
19482
+ await envService.dispose();
19483
+ }
19484
+ }
19485
+ };
19486
+ var WorkflowTagGetCommand = {
19487
+ command: "get <name>",
19488
+ describe: "Get details of a single workflow tag (usage_count, created_at, updated_at). Returns error if tag not in the workflow_tags table.",
19489
+ builder: (yargs) => yargs.positional("name", {
19490
+ describe: "Tag name",
19491
+ type: "string"
19492
+ }).option("json", {
19493
+ alias: "j",
19494
+ describe: "Output as JSON",
19495
+ type: "boolean",
19496
+ default: false
19497
+ }).option("config", {
19498
+ describe: "Config file path",
19499
+ type: "string"
19500
+ }).check((argv) => {
19501
+ if (typeof argv.name !== "string" || argv.name.length === 0) {
19502
+ throw new Error("Tag name is required");
19503
+ }
19504
+ if (argv.name.length > 50) {
19505
+ throw new Error(`Tag name exceeds 50 characters`);
19506
+ }
19507
+ return true;
19508
+ }),
19509
+ async handler(args) {
19510
+ const a = args;
19511
+ const output = new OutputService;
19512
+ const envService = new EnvironmentService(output);
19513
+ try {
19514
+ await runWorkflowTagGet({
19515
+ name: a.name,
19516
+ asJson: a.json,
19517
+ configPath: a.config,
19518
+ output,
19519
+ envService
19520
+ });
19521
+ } catch (error) {
19522
+ output.error(`Failed to get tag: ${error}`);
19523
+ process.exit(1);
19524
+ } finally {
19525
+ await envService.dispose();
19526
+ }
19527
+ }
19528
+ };
19529
+ var _runWorkflowTagListImpl = async function(opts) {
19530
+ const { output, envService } = opts;
19531
+ await envService.create({ configPath: opts.configPath });
19532
+ const env = envService.getEnvironment();
19533
+ if (!env) {
19534
+ output.error("Failed to create environment");
19535
+ process.exit(1);
19536
+ }
19537
+ const workflowComponent = env.getComponent("workflow");
19538
+ if (!workflowComponent) {
19539
+ output.error("WorkflowComponent not available");
19540
+ process.exit(1);
19541
+ }
19542
+ const service = workflowComponent.getService();
19543
+ if (!service) {
19544
+ output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
19545
+ process.exit(1);
19546
+ }
19547
+ const result = await callListTags(service, {
19548
+ sortBy: opts.sortBy ?? "usage_count",
19549
+ order: opts.order ?? "desc",
19550
+ limit: opts.limit ?? 100
19551
+ });
19552
+ if (opts.asJson) {
19553
+ output.json({
19554
+ tags: result.tags,
19555
+ total: result.total
19556
+ });
19557
+ return;
19558
+ }
19559
+ if (result.tags.length === 0) {
19560
+ output.log(chalk72.yellow("\uD83C\uDFF7️ No tags in the workflow_tags table yet."));
19561
+ output.log(chalk72.gray(" Tags are created automatically when you run `workflow add --tags <name>`."));
19562
+ return;
19563
+ }
19564
+ output.log(chalk72.bold(`\uD83C\uDFF7️ Workflow tags (${result.total}):`));
19565
+ output.log(chalk72.gray(` ${padRight("NAME", 24)}${padRight("USAGE", 8)}${padRight("CREATED", 22)}`));
19566
+ for (const t of result.tags) {
19567
+ const line = ` ${padRight(t.name, 24)}${padRight(String(t.usage_count), 8)}${padRight(formatDate2(t.created_at), 22)}`;
19568
+ output.log(line);
19569
+ }
19570
+ output.log(chalk72.gray(`
19571
+ \uD83D\uDCA1 Tip: when adding a new workflow, prefer tags from this list to ensure semantic consistency.`));
19572
+ };
19573
+ var _runWorkflowTagGetImpl = async function(opts) {
19574
+ const { output, envService } = opts;
19575
+ await envService.create({ configPath: opts.configPath });
19576
+ const env = envService.getEnvironment();
19577
+ if (!env) {
19578
+ output.error("Failed to create environment");
19579
+ process.exit(1);
19580
+ }
19581
+ const workflowComponent = env.getComponent("workflow");
19582
+ if (!workflowComponent) {
19583
+ output.error("WorkflowComponent not available");
19584
+ process.exit(1);
19585
+ }
19586
+ const service = workflowComponent.getService();
19587
+ if (!service) {
19588
+ output.error('WorkflowService not initialized. Please run "roy-agent workflow init" first.');
19589
+ process.exit(1);
19590
+ }
19591
+ const tag = callGetTag(service, opts.name);
19592
+ if (!tag) {
19593
+ if (opts.asJson) {
19594
+ output.json({ error: "Tag not found", name: opts.name });
19595
+ } else {
19596
+ output.error(`Tag "${opts.name}" not found in the workflow_tags table.`);
19597
+ output.log(chalk72.gray(`Tip: run \`roy-agent workflow tag list\` to see available tags.`));
19598
+ }
19599
+ process.exitCode = 1;
19600
+ return;
19601
+ }
19602
+ if (opts.asJson) {
19603
+ output.json(tag);
19604
+ return;
19605
+ }
19606
+ output.log(chalk72.bold(`\uD83C\uDFF7️ Tag: ${tag.name}`));
19607
+ output.log(chalk72.gray(` usage_count: ${tag.usage_count}`));
19608
+ output.log(chalk72.gray(` created_at: ${tag.created_at}`));
19609
+ output.log(chalk72.gray(` updated_at: ${tag.updated_at}`));
19610
+ };
19611
+ async function callListTags(service, options) {
19612
+ if (typeof service.listTags !== "function") {
19613
+ return { tags: [], total: 0 };
19614
+ }
19615
+ return await service.listTags(options);
19616
+ }
19617
+ function callGetTag(service, name) {
19618
+ if (typeof service.getTag !== "function") {
19619
+ return;
19620
+ }
19621
+ return service.getTag(name);
19622
+ }
19623
+ function padRight(str, width) {
19624
+ if (str.length >= width)
19625
+ return str.slice(0, width - 1) + " ";
19626
+ return str + " ".repeat(width - str.length);
19627
+ }
19628
+ function formatDate2(iso) {
19629
+ try {
19630
+ const d = new Date(iso);
19631
+ if (isNaN(d.getTime()))
19632
+ return iso;
19633
+ return d.toISOString().replace("T", " ").slice(0, 19);
19634
+ } catch {
19635
+ return iso;
19636
+ }
19637
+ }
19638
+ var runWorkflowTagList = wrapFunction4(_runWorkflowTagListImpl, "workflow.cli.tag.list", {
19639
+ recordParams: true,
19640
+ recordResult: false,
19641
+ log: false
19642
+ });
19643
+ var runWorkflowTagGet = wrapFunction4(_runWorkflowTagGetImpl, "workflow.cli.tag.get", {
19644
+ recordParams: true,
19645
+ recordResult: false,
19646
+ log: false
19647
+ });
19648
+
19142
19649
  // src/commands/workflow/index.ts
19650
+ var WorkflowTagCommand = {
19651
+ command: "tag",
19652
+ describe: "Manage workflow tags — list and get from the workflow_tags table. Use BEFORE `workflow add` to discover existing tags.",
19653
+ builder: (yargs) => yargs.command(WorkflowTagListCommand).command(WorkflowTagGetCommand).demandCommand(1, "请指定 tag 子命令:list 或 get <name>").help(),
19654
+ handler() {}
19655
+ };
19143
19656
  var WorkflowCommand = {
19144
19657
  command: "workflow",
19145
19658
  describe: "Workflow DAG management and execution - create, run, and monitor workflows",
19146
- builder: (yargs) => yargs.command(WorkflowListCommand).command(WorkflowAddCommand).command(WorkflowNodesCommand).command(WorkflowValidateCommand).command(WorkflowGetCommand).command(WorkflowUpdateCommand).command(WorkflowRemoveCommand).command(WorkflowRunCommand).command(WorkflowStopCommand).command(WorkflowStatusCommand).demandCommand(1, "请指定子命令:list、add、validate、nodes、get、update、remove、run、stop、status\n\nResume: 使用 `workflow run -s <sessionId>` 恢复暂停的工作流").help(),
19659
+ builder: (yargs) => yargs.command(WorkflowListCommand).command(WorkflowAddCommand).command(WorkflowSearchCommand).command(WorkflowTagCommand).command(WorkflowNodesCommand).command(WorkflowValidateCommand).command(WorkflowGetCommand).command(WorkflowUpdateCommand).command(WorkflowRemoveCommand).command(WorkflowRunCommand).command(WorkflowStopCommand).command(WorkflowStatusCommand).demandCommand(1, "请指定子命令:list、add、search、tag、validate、nodes、get、update、remove、run、stop、status\n\nResume: 使用 `workflow run -s <sessionId>` 恢复暂停的工作流").help(),
19147
19660
  handler() {}
19148
19661
  };
19149
19662
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-agent-cli",
3
- "version": "1.5.84",
3
+ "version": "1.5.86",
4
4
  "type": "module",
5
5
  "description": "CLI for roy-agent - Non-interactive command execution",
6
6
  "main": "./dist/index.js",