@amaster.ai/runtime-cli 1.1.14-beta.0 → 1.1.14-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ CLI for Amaster SDK - Multi-app support for OpenClaw. Manage multiple Amaster ap
9
9
  - **Structured Output**: `json`, `pretty`, `table`, `ndjson`, and `csv`
10
10
  - **Entity Management**: CRUD, options lookup, and bulk operations on entities
11
11
  - **BPM**: Manage Camunda processes, tasks, and instances
12
- - **Workflow**: Run workflows
12
+ - **Workflow**: Discover, inspect, and run workflows
13
13
  - **S3 Storage**: Upload files, download objects, and inspect metadata
14
14
  - **OpenClaw Integration**: Initialize app-specific skills and MCP servers
15
15
 
@@ -86,6 +86,10 @@ amaster logout --app myapp
86
86
  ### 4. Use SDK Features
87
87
 
88
88
  ```bash
89
+ # Discover runnable workflows and inspect required inputs
90
+ amaster workflow list --app myapp --format pretty
91
+ amaster workflow get <workflow-app-id> --app myapp --format pretty
92
+
89
93
  # Run an anonymous workflow when the app exposes it publicly
90
94
  amaster workflow run text-sentiment-analysis --app myapp \
91
95
  --input '{"input_text":"I love this product. It is fast and easy to use."}'
@@ -639,6 +643,15 @@ amaster bpm modify-instance proc-123 --app myapp \
639
643
  ### Workflow
640
644
 
641
645
  ```bash
646
+ amaster workflow list [options] # List workflow apps and runnable names
647
+ --app <app-code> # App code (uses default if not specified)
648
+ --page <n> # Page number
649
+ --limit <n> # Page size
650
+ --mode <mode> # workflow | chat | completion | agent-chat
651
+
652
+ amaster workflow get <id> [options] # Get workflow details and input variables by workflow app ID
653
+ --app <app-code> # App code (uses default if not specified)
654
+
642
655
  amaster workflow run <name> [options] # Run a workflow
643
656
  --app <app-code> # App code (uses default if not specified)
644
657
  -i, --input <json> # Workflow inputs object as JSON or @file
@@ -649,6 +662,8 @@ amaster workflow run <name> [options] # Run a workflow
649
662
  --trace-id <traceId> # Workflow trace ID
650
663
  ```
651
664
 
665
+ Use `workflow list` to discover workflow app IDs and `runnable_name`. Use the `id` with `workflow get`, and use the `runnable_name` or the `name` returned by `workflow get` with `workflow run`.
666
+
652
667
  ### S3 Storage
653
668
 
654
669
  ```bash
package/dist/cli.cjs CHANGED
@@ -2267,6 +2267,8 @@ async function getUserRoles(client, options) {
2267
2267
  exitWithCommandError3(spinner, "Failed to fetch user roles", error);
2268
2268
  }
2269
2269
  }
2270
+ var WORKFLOW_LIST_PATH = "/api/proxy/builtin/platform/workflow/openapi/apps";
2271
+ var WORKFLOW_GET_PATH = (workflowId) => `/api/proxy/builtin/platform/workflow/openapi/apps/${encodeURIComponent(workflowId)}/dsl`;
2270
2272
  function resolveInputSource4(input) {
2271
2273
  if (!input.startsWith("@")) {
2272
2274
  return input;
@@ -2292,6 +2294,151 @@ function parseJsonInput3(input, label) {
2292
2294
  function isPlainObject4(value) {
2293
2295
  return typeof value === "object" && value !== null && !Array.isArray(value);
2294
2296
  }
2297
+ function parseWorkflowTracing(value) {
2298
+ if (!value) {
2299
+ return null;
2300
+ }
2301
+ if (isPlainObject4(value)) {
2302
+ return value;
2303
+ }
2304
+ if (typeof value !== "string") {
2305
+ return null;
2306
+ }
2307
+ try {
2308
+ const parsed = JSON.parse(value);
2309
+ return isPlainObject4(parsed) ? parsed : null;
2310
+ } catch {
2311
+ return null;
2312
+ }
2313
+ }
2314
+ function getRunnableName(summary) {
2315
+ const tracing = parseWorkflowTracing(summary.tracing);
2316
+ const baseName = tracing?.base_name;
2317
+ if (typeof baseName === "string" && baseName.trim()) {
2318
+ return baseName.trim();
2319
+ }
2320
+ return null;
2321
+ }
2322
+ function normalizeWorkflowSummary(summary) {
2323
+ return {
2324
+ ...summary,
2325
+ id: typeof summary.id === "string" ? summary.id : "",
2326
+ name: typeof summary.name === "string" ? summary.name : "",
2327
+ runnable_name: getRunnableName(summary),
2328
+ description: typeof summary.description === "string" ? summary.description : null,
2329
+ mode: typeof summary.mode === "string" ? summary.mode : null
2330
+ };
2331
+ }
2332
+ function extractWorkflowInputVariables(dsl) {
2333
+ if (!isPlainObject4(dsl)) {
2334
+ return [];
2335
+ }
2336
+ const candidateNodeSets = [];
2337
+ if (Array.isArray(dsl.nodes)) {
2338
+ candidateNodeSets.push(dsl.nodes);
2339
+ }
2340
+ if (isPlainObject4(dsl.graph) && Array.isArray(dsl.graph.nodes)) {
2341
+ candidateNodeSets.push(dsl.graph.nodes);
2342
+ }
2343
+ const variables = [];
2344
+ for (const nodeSet of candidateNodeSets) {
2345
+ for (const node of nodeSet) {
2346
+ if (!isPlainObject4(node) || !isPlainObject4(node.data)) {
2347
+ continue;
2348
+ }
2349
+ const nodeData = node.data;
2350
+ if (nodeData.type !== "start" || !Array.isArray(nodeData.variables)) {
2351
+ continue;
2352
+ }
2353
+ for (const variable of nodeData.variables) {
2354
+ if (!isPlainObject4(variable) || typeof variable.variable !== "string") {
2355
+ continue;
2356
+ }
2357
+ variables.push({
2358
+ ...variable,
2359
+ variable: variable.variable,
2360
+ label: typeof variable.label === "string" ? variable.label : void 0,
2361
+ required: typeof variable.required === "boolean" ? variable.required : void 0,
2362
+ type: typeof variable.type === "string" ? variable.type : void 0
2363
+ });
2364
+ }
2365
+ }
2366
+ }
2367
+ return variables;
2368
+ }
2369
+ function normalizeWorkflowDefinition(definition) {
2370
+ return {
2371
+ ...definition,
2372
+ workflow_id: typeof definition.workflow_id === "string" ? definition.workflow_id : "",
2373
+ app_id: typeof definition.app_id === "string" ? definition.app_id : "",
2374
+ version: typeof definition.version === "string" ? definition.version : void 0,
2375
+ name: typeof definition.name === "string" ? definition.name : "",
2376
+ dsl: isPlainObject4(definition.dsl) ? definition.dsl : void 0,
2377
+ context: typeof definition.context === "string" ? definition.context : void 0,
2378
+ input_variables: extractWorkflowInputVariables(definition.dsl)
2379
+ };
2380
+ }
2381
+ function invalidResponse(status, message) {
2382
+ return {
2383
+ data: null,
2384
+ error: {
2385
+ status,
2386
+ message
2387
+ },
2388
+ status
2389
+ };
2390
+ }
2391
+ function createWorkflowService(client) {
2392
+ return {
2393
+ async list(params = {}) {
2394
+ const response = await client.http.request({
2395
+ url: WORKFLOW_LIST_PATH,
2396
+ method: "GET",
2397
+ params: {
2398
+ page: params.page ?? 1,
2399
+ limit: params.limit ?? 20,
2400
+ mode: params.mode ?? "workflow"
2401
+ }
2402
+ });
2403
+ if (!response.data || !Array.isArray(response.data.data)) {
2404
+ return invalidResponse(
2405
+ response.status,
2406
+ response.error?.message || "Invalid workflow list response structure"
2407
+ );
2408
+ }
2409
+ return {
2410
+ ...response,
2411
+ data: {
2412
+ ...response.data,
2413
+ page: typeof response.data.page === "number" ? response.data.page : params.page ?? 1,
2414
+ limit: typeof response.data.limit === "number" ? response.data.limit : params.limit ?? 20,
2415
+ total: typeof response.data.total === "number" ? response.data.total : response.data.data.length,
2416
+ has_more: typeof response.data.has_more === "boolean" ? response.data.has_more : false,
2417
+ data: response.data.data.map(normalizeWorkflowSummary)
2418
+ }
2419
+ };
2420
+ },
2421
+ async get(workflowId) {
2422
+ if (!workflowId) {
2423
+ return invalidResponse(400, "workflowId is required");
2424
+ }
2425
+ const response = await client.http.request({
2426
+ url: WORKFLOW_GET_PATH(workflowId),
2427
+ method: "GET"
2428
+ });
2429
+ if (!response.data || !isPlainObject4(response.data)) {
2430
+ return invalidResponse(
2431
+ response.status,
2432
+ response.error?.message || "Invalid workflow definition response structure"
2433
+ );
2434
+ }
2435
+ return {
2436
+ ...response,
2437
+ data: normalizeWorkflowDefinition(response.data)
2438
+ };
2439
+ }
2440
+ };
2441
+ }
2295
2442
  function loadOptionalJsonObjectInput3(input, label) {
2296
2443
  if (!input) {
2297
2444
  return {};
@@ -2366,6 +2513,128 @@ async function runWorkflow(client, options) {
2366
2513
  exitWithCommandError4(spinner, "Failed to run workflow", error);
2367
2514
  }
2368
2515
  }
2516
+ async function listWorkflows(client, options = {}) {
2517
+ const spinner = createSpinner("Fetching workflows...", options.format);
2518
+ try {
2519
+ const workflowService = createWorkflowService(client);
2520
+ const result = await workflowService.list({
2521
+ page: options.page,
2522
+ limit: options.limit,
2523
+ mode: options.mode
2524
+ });
2525
+ if (result.error) {
2526
+ spinner.fail("Failed to fetch workflows");
2527
+ console.error(chalk4__default.default.red(result.error.message));
2528
+ process.exit(1);
2529
+ }
2530
+ const payload = result.data;
2531
+ const workflows = payload?.data || [];
2532
+ spinner.succeed(`Found ${workflows.length} workflow${workflows.length === 1 ? "" : "s"}`);
2533
+ renderOutput(payload, {
2534
+ format: options.format,
2535
+ tableRows: workflows.map((workflow) => ({
2536
+ id: workflow.id,
2537
+ runnableName: workflow.runnable_name,
2538
+ platformName: workflow.name,
2539
+ mode: workflow.mode,
2540
+ description: workflow.description
2541
+ })),
2542
+ csvRows: workflows.map((workflow) => ({
2543
+ id: workflow.id,
2544
+ runnableName: workflow.runnable_name,
2545
+ platformName: workflow.name,
2546
+ mode: workflow.mode,
2547
+ description: workflow.description
2548
+ })),
2549
+ ndjsonItems: workflows,
2550
+ pretty: () => {
2551
+ console.log(chalk4__default.default.blue("\nWorkflows\n"));
2552
+ if (workflows.length === 0) {
2553
+ console.log(chalk4__default.default.gray("No workflows found."));
2554
+ return;
2555
+ }
2556
+ for (const workflow of workflows) {
2557
+ console.log(`${chalk4__default.default.green("\u2022")} ${chalk4__default.default.bold(workflow.runnable_name || workflow.name)}`);
2558
+ console.log(` ${chalk4__default.default.gray("ID:")} ${workflow.id}`);
2559
+ console.log(` ${chalk4__default.default.gray("Platform Name:")} ${workflow.name}`);
2560
+ console.log(` ${chalk4__default.default.gray("Mode:")} ${workflow.mode || "workflow"}`);
2561
+ if (workflow.description) {
2562
+ console.log(` ${chalk4__default.default.gray("Description:")} ${workflow.description}`);
2563
+ }
2564
+ }
2565
+ }
2566
+ });
2567
+ } catch (error) {
2568
+ exitWithCommandError4(spinner, "Failed to fetch workflows", error);
2569
+ }
2570
+ }
2571
+ async function getWorkflow(client, options) {
2572
+ const spinner = createSpinner(`Fetching workflow: ${options.id}...`, options.format);
2573
+ try {
2574
+ const workflowService = createWorkflowService(client);
2575
+ const result = await workflowService.get(options.id);
2576
+ if (result.error) {
2577
+ spinner.fail("Failed to fetch workflow");
2578
+ console.error(chalk4__default.default.red(result.error.message));
2579
+ process.exit(1);
2580
+ }
2581
+ const workflow = result.data;
2582
+ const inputVariables = workflow?.input_variables || [];
2583
+ spinner.succeed("Workflow loaded");
2584
+ renderOutput(workflow, {
2585
+ format: options.format,
2586
+ tableRows: workflow ? [
2587
+ {
2588
+ appId: workflow.app_id,
2589
+ workflowId: workflow.workflow_id,
2590
+ runnableName: workflow.name,
2591
+ version: workflow.version || null,
2592
+ inputVariables: inputVariables.map((item) => item.variable).join(", ")
2593
+ }
2594
+ ] : [],
2595
+ csvRows: workflow ? [
2596
+ {
2597
+ appId: workflow.app_id,
2598
+ workflowId: workflow.workflow_id,
2599
+ runnableName: workflow.name,
2600
+ version: workflow.version || null,
2601
+ inputVariables: inputVariables.map((item) => item.variable).join(", ")
2602
+ }
2603
+ ] : [],
2604
+ ndjsonItems: workflow ? [workflow] : [],
2605
+ pretty: () => {
2606
+ if (!workflow) {
2607
+ console.log(chalk4__default.default.gray("No workflow found."));
2608
+ return;
2609
+ }
2610
+ console.log(chalk4__default.default.blue(`
2611
+ Workflow: ${workflow.name}
2612
+ `));
2613
+ console.log(`${chalk4__default.default.gray("App ID:")} ${workflow.app_id}`);
2614
+ console.log(`${chalk4__default.default.gray("Workflow ID:")} ${workflow.workflow_id}`);
2615
+ if (workflow.version) {
2616
+ console.log(`${chalk4__default.default.gray("Version:")} ${workflow.version}`);
2617
+ }
2618
+ console.log(`
2619
+ ${chalk4__default.default.gray("Input Variables:")}`);
2620
+ if (inputVariables.length === 0) {
2621
+ console.log(chalk4__default.default.gray(" none"));
2622
+ } else {
2623
+ for (const variable of inputVariables) {
2624
+ const label = variable.label ? ` ${chalk4__default.default.gray(`(${variable.label})`)}` : "";
2625
+ const type = variable.type ? ` ${chalk4__default.default.gray(`[${variable.type}]`)}` : "";
2626
+ const required = variable.required ? chalk4__default.default.yellow(" required") : chalk4__default.default.gray(" optional");
2627
+ console.log(` - ${chalk4__default.default.bold(variable.variable)}${label}${type}${required}`);
2628
+ }
2629
+ }
2630
+ console.log(`
2631
+ ${chalk4__default.default.gray("Run With:")} amaster workflow run ${workflow.name} --app <app-code> --input '{...}'`);
2632
+ }
2633
+ });
2634
+ } catch (error) {
2635
+ exitWithCommandError4(spinner, "Failed to fetch workflow", error);
2636
+ }
2637
+ }
2369
2638
  function exitWithCommandError5(spinner, failureMessage, error) {
2370
2639
  spinner.fail(failureMessage);
2371
2640
  const details = error instanceof Error ? error.message : String(error);
@@ -2774,11 +3043,12 @@ function createAmasterClient(appCode) {
2774
3043
  }
2775
3044
  const client$1 = client.createClient({
2776
3045
  baseURL: appConfig.baseURL,
2777
- ...token ? {
2778
- headers: {
3046
+ headers: {
3047
+ "x-app-code": appCode,
3048
+ ...token ? {
2779
3049
  Authorization: `Bearer ${token}`
2780
- }
2781
- } : {},
3050
+ } : {}
3051
+ },
2782
3052
  onUnauthorized: () => {
2783
3053
  if (token) {
2784
3054
  console.error(chalk4__default.default.red("\n\u274C Session expired. Please login again."));
@@ -3331,7 +3601,25 @@ bpmCmd.command("start-form-deployed <key>").description("Get deployed start form
3331
3601
  format: options.format
3332
3602
  });
3333
3603
  });
3334
- var workflowCmd = program.command("workflow").description("Run workflows");
3604
+ var workflowCmd = program.command("workflow").description("Inspect and run workflows");
3605
+ workflowCmd.command("list").description("List runnable workflows").option("--app <app-code>", "App code (uses default if not specified)").option("--page <n>", "Page number").option("--limit <n>", "Page size").addOption(
3606
+ new commander.Option("--mode <mode>", "Workflow app mode").choices(["workflow", "chat", "completion", "agent-chat"])
3607
+ ).addOption(createFormatOption()).action(async (options) => {
3608
+ const appCode = resolveAppCode(options.app);
3609
+ await listWorkflows(createAmasterClient(appCode), {
3610
+ page: options.page ? parseInt(options.page, 10) : void 0,
3611
+ limit: options.limit ? parseInt(options.limit, 10) : void 0,
3612
+ mode: options.mode,
3613
+ format: options.format
3614
+ });
3615
+ });
3616
+ workflowCmd.command("get <id>").alias("inspect").description("Get workflow details and input variables by workflow app ID").option("--app <app-code>", "App code (uses default if not specified)").addOption(createFormatOption()).action(async (id, options) => {
3617
+ const appCode = resolveAppCode(options.app);
3618
+ await getWorkflow(createAmasterClient(appCode), {
3619
+ id,
3620
+ format: options.format
3621
+ });
3622
+ });
3335
3623
  workflowCmd.command("run <name>").alias("execute").description("Run a workflow").option("--app <app-code>", "App code (uses default if not specified)").option("-i, --input <json>", "Workflow inputs object as JSON or @file").option("-r, --request <json>", "Full WorkflowRunRequest as JSON or @file").addOption(
3336
3624
  new commander.Option("--response-mode <mode>", "Workflow response mode").choices(["blocking", "streaming"])
3337
3625
  ).option("--user <user>", "Workflow user identifier").option("--files <json>", "Workflow files array as JSON or @file").option("--trace-id <traceId>", "Workflow trace ID").addOption(createFormatOption()).action(async (name, options) => {