@orchagent/cli 0.3.86 → 0.3.87
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/commands/agent-keys.js +21 -7
- package/dist/commands/agents.js +60 -5
- package/dist/commands/config.js +4 -0
- package/dist/commands/delete.js +2 -2
- package/dist/commands/dev.js +226 -0
- package/dist/commands/diff.js +418 -0
- package/dist/commands/estimate.js +105 -0
- package/dist/commands/fork.js +11 -1
- package/dist/commands/health.js +226 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/info.js +75 -0
- package/dist/commands/init.js +729 -38
- package/dist/commands/publish.js +237 -21
- package/dist/commands/run.js +272 -28
- package/dist/commands/schedule.js +11 -6
- package/dist/commands/test.js +68 -1
- package/dist/lib/api.js +29 -4
- package/dist/lib/batch-publish.js +223 -0
- package/dist/lib/dev-server.js +425 -0
- package/dist/lib/doctor/checks/environment.js +1 -1
- package/dist/lib/key-store.js +121 -0
- package/dist/lib/spinner.js +50 -0
- package/dist/lib/test-mock-runner.js +334 -0
- package/dist/lib/update-notifier.js +1 -1
- package/package.json +1 -1
- package/src/resources/__pycache__/agent_runner.cpython-311.pyc +0 -0
- package/src/resources/__pycache__/agent_runner.cpython-312.pyc +0 -0
- package/src/resources/__pycache__/test_agent_runner_mocks.cpython-311-pytest-9.0.2.pyc +0 -0
- package/src/resources/__pycache__/test_agent_runner_mocks.cpython-312-pytest-8.4.2.pyc +0 -0
- package/src/resources/agent_runner.py +29 -2
- package/src/resources/test_agent_runner_mocks.py +290 -0
package/dist/commands/publish.js
CHANGED
|
@@ -42,17 +42,21 @@ exports.scanUndeclaredEnvVars = scanUndeclaredEnvVars;
|
|
|
42
42
|
exports.scanReservedPort = scanReservedPort;
|
|
43
43
|
exports.detectSdkCompatible = detectSdkCompatible;
|
|
44
44
|
exports.checkDependencies = checkDependencies;
|
|
45
|
+
exports.batchPublish = batchPublish;
|
|
45
46
|
exports.registerPublishCommand = registerPublishCommand;
|
|
46
47
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
47
48
|
const path_1 = __importDefault(require("path"));
|
|
48
49
|
const os_1 = __importDefault(require("os"));
|
|
49
50
|
const yaml_1 = __importDefault(require("yaml"));
|
|
50
51
|
const chalk_1 = __importDefault(require("chalk"));
|
|
52
|
+
const child_process_1 = require("child_process");
|
|
51
53
|
const config_1 = require("../lib/config");
|
|
52
54
|
const api_1 = require("../lib/api");
|
|
53
55
|
const errors_1 = require("../lib/errors");
|
|
54
56
|
const analytics_1 = require("../lib/analytics");
|
|
55
57
|
const bundle_1 = require("../lib/bundle");
|
|
58
|
+
const key_store_1 = require("../lib/key-store");
|
|
59
|
+
const batch_publish_1 = require("../lib/batch-publish");
|
|
56
60
|
/**
|
|
57
61
|
* Extract template placeholders from a prompt template.
|
|
58
62
|
* Matches double-brace patterns like {{variable}}.
|
|
@@ -442,6 +446,133 @@ async function checkDependencies(config, dependencies, publishingOrgSlug, worksp
|
|
|
442
446
|
}
|
|
443
447
|
}));
|
|
444
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Batch publish all agents found in subdirectories, in dependency order.
|
|
451
|
+
* Discovers orchagent.json/SKILL.md in immediate subdirectories,
|
|
452
|
+
* topologically sorts by manifest dependencies, and publishes leaf-first.
|
|
453
|
+
*/
|
|
454
|
+
async function batchPublish(rootDir, options) {
|
|
455
|
+
process.stderr.write(`\nScanning for agents in ${rootDir}...\n`);
|
|
456
|
+
const agents = await (0, batch_publish_1.discoverAgents)(rootDir);
|
|
457
|
+
if (agents.length === 0) {
|
|
458
|
+
throw new errors_1.CliError('No agents found. Expected subdirectories with orchagent.json or SKILL.md files.\n\n' +
|
|
459
|
+
'Example monorepo layout:\n' +
|
|
460
|
+
' my-project/\n' +
|
|
461
|
+
' leaf-tool/\n' +
|
|
462
|
+
' orchagent.json\n' +
|
|
463
|
+
' orchestrator/\n' +
|
|
464
|
+
' orchagent.json\n' +
|
|
465
|
+
' prompt.md\n\n' +
|
|
466
|
+
'Run `orch publish --all` from the parent directory.');
|
|
467
|
+
}
|
|
468
|
+
const result = (0, batch_publish_1.topoSort)(agents);
|
|
469
|
+
if (!result.ok) {
|
|
470
|
+
throw new errors_1.CliError(`Circular dependency detected between: ${result.cycle.join(' → ')}\n\n` +
|
|
471
|
+
'Break the cycle by removing a dependency in one of these agents\' orchagent.json manifest.dependencies.');
|
|
472
|
+
}
|
|
473
|
+
const sorted = result.sorted;
|
|
474
|
+
// Resolve org for display (best-effort)
|
|
475
|
+
let orgSlug;
|
|
476
|
+
try {
|
|
477
|
+
const config = await (0, config_1.getResolvedConfig)({}, options.profile);
|
|
478
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
479
|
+
let workspaceId;
|
|
480
|
+
if (configFile.workspace && !options.profile) {
|
|
481
|
+
const { workspaces } = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
482
|
+
const ws = workspaces.find(w => w.slug === configFile.workspace);
|
|
483
|
+
if (ws)
|
|
484
|
+
workspaceId = ws.id;
|
|
485
|
+
}
|
|
486
|
+
const org = await (0, api_1.getOrg)(config, workspaceId);
|
|
487
|
+
orgSlug = org.slug;
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
// Non-critical — just won't show org prefix
|
|
491
|
+
}
|
|
492
|
+
const plan = (0, batch_publish_1.formatPublishPlan)(sorted, orgSlug);
|
|
493
|
+
process.stderr.write(plan);
|
|
494
|
+
if (options.dryRun) {
|
|
495
|
+
process.stderr.write(chalk_1.default.cyan('DRY RUN — running orch publish --dry-run in each directory:\n\n'));
|
|
496
|
+
}
|
|
497
|
+
// Build the CLI args to forward (exclude --all)
|
|
498
|
+
const forwardArgs = [];
|
|
499
|
+
if (options.profile)
|
|
500
|
+
forwardArgs.push('--profile', options.profile);
|
|
501
|
+
if (options.dryRun)
|
|
502
|
+
forwardArgs.push('--dry-run');
|
|
503
|
+
if (options.skills)
|
|
504
|
+
forwardArgs.push('--skills', options.skills);
|
|
505
|
+
if (options.skillsLocked)
|
|
506
|
+
forwardArgs.push('--skills-locked');
|
|
507
|
+
if (options.docker)
|
|
508
|
+
forwardArgs.push('--docker');
|
|
509
|
+
if (options.localDownload)
|
|
510
|
+
forwardArgs.push('--local-download');
|
|
511
|
+
if (options.requiredSecrets === false)
|
|
512
|
+
forwardArgs.push('--no-required-secrets');
|
|
513
|
+
const results = [];
|
|
514
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
515
|
+
const agent = sorted[i];
|
|
516
|
+
const label = `[${i + 1}/${sorted.length}]`;
|
|
517
|
+
process.stderr.write(`${chalk_1.default.bold(label)} Publishing ${chalk_1.default.cyan(agent.name)} from ${agent.dirName}/...\n`);
|
|
518
|
+
// Spawn orch publish in the agent's directory
|
|
519
|
+
const spawnResult = (0, child_process_1.spawnSync)(process.argv[0], [process.argv[1], 'publish', ...forwardArgs], {
|
|
520
|
+
cwd: agent.dir,
|
|
521
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
522
|
+
env: process.env,
|
|
523
|
+
timeout: 120_000, // 2 minute timeout per agent
|
|
524
|
+
});
|
|
525
|
+
if (spawnResult.status === 0) {
|
|
526
|
+
results.push({ name: agent.name, dir: agent.dirName, success: true });
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
const errMsg = spawnResult.error?.message || `exit code ${spawnResult.status}`;
|
|
530
|
+
results.push({ name: agent.name, dir: agent.dirName, success: false, error: errMsg });
|
|
531
|
+
// Stop on first failure — downstream agents depend on this one
|
|
532
|
+
process.stderr.write(chalk_1.default.red(`\n✗ Failed to publish ${agent.name}. Stopping — downstream agents may depend on it.\n`));
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
if (i < sorted.length - 1) {
|
|
536
|
+
process.stderr.write('\n'); // Visual separator between publishes
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
// Summary
|
|
540
|
+
const succeeded = results.filter(r => r.success);
|
|
541
|
+
const failed = results.filter(r => !r.success);
|
|
542
|
+
const skipped = sorted.length - results.length;
|
|
543
|
+
process.stderr.write('\n' + chalk_1.default.bold('─'.repeat(50)) + '\n');
|
|
544
|
+
process.stderr.write(chalk_1.default.bold(`Batch publish summary:\n\n`));
|
|
545
|
+
for (const r of results) {
|
|
546
|
+
if (r.success) {
|
|
547
|
+
process.stderr.write(` ${chalk_1.default.green('✔')} ${r.name}\n`);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
process.stderr.write(` ${chalk_1.default.red('✗')} ${r.name} — ${r.error}\n`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (skipped > 0) {
|
|
554
|
+
const skippedAgents = sorted.slice(results.length);
|
|
555
|
+
for (const a of skippedAgents) {
|
|
556
|
+
process.stderr.write(` ${chalk_1.default.yellow('○')} ${a.name} (skipped)\n`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
process.stderr.write(`\n ${succeeded.length} succeeded`);
|
|
560
|
+
if (failed.length > 0)
|
|
561
|
+
process.stderr.write(`, ${chalk_1.default.red(`${failed.length} failed`)}`);
|
|
562
|
+
if (skipped > 0)
|
|
563
|
+
process.stderr.write(`, ${chalk_1.default.yellow(`${skipped} skipped`)}`);
|
|
564
|
+
process.stderr.write('\n');
|
|
565
|
+
await (0, analytics_1.track)('cli_publish_all', {
|
|
566
|
+
total: sorted.length,
|
|
567
|
+
succeeded: succeeded.length,
|
|
568
|
+
failed: failed.length,
|
|
569
|
+
skipped,
|
|
570
|
+
dry_run: options.dryRun || false,
|
|
571
|
+
});
|
|
572
|
+
if (failed.length > 0) {
|
|
573
|
+
throw new errors_1.CliError(`Batch publish failed: ${failed[0].name}`, errors_1.ExitCodes.GENERAL_ERROR);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
445
576
|
function registerPublishCommand(program) {
|
|
446
577
|
program
|
|
447
578
|
.command('publish')
|
|
@@ -454,12 +585,18 @@ function registerPublishCommand(program) {
|
|
|
454
585
|
.option('--docker', 'Include Dockerfile for custom environment (builds E2B template)')
|
|
455
586
|
.option('--local-download', 'Allow users to download and run locally (default: server-only)')
|
|
456
587
|
.option('--no-required-secrets', 'Skip required_secrets check for tool/agent types')
|
|
588
|
+
.option('--all', 'Publish all agents in subdirectories (dependency order)')
|
|
457
589
|
.action(async (options) => {
|
|
590
|
+
const cwd = process.cwd();
|
|
591
|
+
// --all: batch publish all agents in subdirectories
|
|
592
|
+
if (options.all) {
|
|
593
|
+
await batchPublish(cwd, options);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
458
596
|
const skillsFromFlag = options.skills
|
|
459
597
|
? options.skills.split(',').map(s => s.trim()).filter(Boolean)
|
|
460
598
|
: undefined;
|
|
461
599
|
const config = await (0, config_1.getResolvedConfig)({}, options.profile);
|
|
462
|
-
const cwd = process.cwd();
|
|
463
600
|
// Resolve workspace context — if `orch workspace use` was called, publish
|
|
464
601
|
// to that workspace instead of the personal org (F-5)
|
|
465
602
|
// Skip workspace resolution when using a named profile — the global
|
|
@@ -686,24 +823,6 @@ function registerPublishCommand(program) {
|
|
|
686
823
|
// Validate managed-loop specific fields + normalize loop payload
|
|
687
824
|
let loopConfig;
|
|
688
825
|
if (executionEngine === 'managed_loop') {
|
|
689
|
-
if (manifest.custom_tools) {
|
|
690
|
-
const reservedNames = new Set(['bash', 'read_file', 'write_file', 'list_files', 'submit_result']);
|
|
691
|
-
const seenNames = new Set();
|
|
692
|
-
for (const tool of manifest.custom_tools) {
|
|
693
|
-
if (!tool.name || !tool.command) {
|
|
694
|
-
throw new errors_1.CliError(`Invalid custom_tool: each tool must have 'name' and 'command' fields.\n` +
|
|
695
|
-
`Found: ${JSON.stringify(tool)}`);
|
|
696
|
-
}
|
|
697
|
-
if (reservedNames.has(tool.name)) {
|
|
698
|
-
throw new errors_1.CliError(`Custom tool '${tool.name}' conflicts with a built-in tool name.\n` +
|
|
699
|
-
`Reserved names: ${[...reservedNames].join(', ')}`);
|
|
700
|
-
}
|
|
701
|
-
if (seenNames.has(tool.name)) {
|
|
702
|
-
throw new errors_1.CliError(`Duplicate custom tool name: '${tool.name}'`);
|
|
703
|
-
}
|
|
704
|
-
seenNames.add(tool.name);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
826
|
if (manifest.max_turns !== undefined) {
|
|
708
827
|
if (typeof manifest.max_turns !== 'number' || manifest.max_turns < 1 || manifest.max_turns > 50) {
|
|
709
828
|
throw new errors_1.CliError('max_turns must be a number between 1 and 50');
|
|
@@ -722,6 +841,27 @@ function registerPublishCommand(program) {
|
|
|
722
841
|
providedLoop.max_turns = 25;
|
|
723
842
|
}
|
|
724
843
|
loopConfig = providedLoop;
|
|
844
|
+
// Validate custom_tools from the merged loopConfig (covers both top-level
|
|
845
|
+
// manifest.custom_tools and loop.custom_tools placements — BUG-15)
|
|
846
|
+
const mergedTools = Array.isArray(loopConfig.custom_tools) ? loopConfig.custom_tools : [];
|
|
847
|
+
if (mergedTools.length > 0) {
|
|
848
|
+
const reservedNames = new Set(['bash', 'read_file', 'write_file', 'list_files', 'submit_result']);
|
|
849
|
+
const seenNames = new Set();
|
|
850
|
+
for (const tool of mergedTools) {
|
|
851
|
+
if (!tool.name || !tool.command) {
|
|
852
|
+
throw new errors_1.CliError(`Invalid custom_tool: each tool must have 'name' and 'command' fields.\n` +
|
|
853
|
+
`Found: ${JSON.stringify(tool)}`);
|
|
854
|
+
}
|
|
855
|
+
if (reservedNames.has(tool.name)) {
|
|
856
|
+
throw new errors_1.CliError(`Custom tool '${tool.name}' conflicts with a built-in tool name.\n` +
|
|
857
|
+
`Reserved names: ${[...reservedNames].join(', ')}`);
|
|
858
|
+
}
|
|
859
|
+
if (seenNames.has(tool.name)) {
|
|
860
|
+
throw new errors_1.CliError(`Duplicate custom tool name: '${tool.name}'`);
|
|
861
|
+
}
|
|
862
|
+
seenNames.add(tool.name);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
725
865
|
if (!manifest.supported_providers) {
|
|
726
866
|
manifest.supported_providers = ['anthropic'];
|
|
727
867
|
}
|
|
@@ -870,7 +1010,7 @@ function registerPublishCommand(program) {
|
|
|
870
1010
|
const schemaTypes = [inputSchema ? 'input' : null, outputSchema ? 'output' : null].filter(Boolean).join(' + ');
|
|
871
1011
|
process.stderr.write(` ✓ schema.json found (${schemaTypes} schemas)\n`);
|
|
872
1012
|
}
|
|
873
|
-
const customToolCount =
|
|
1013
|
+
const customToolCount = Array.isArray(loopConfig?.custom_tools) ? loopConfig.custom_tools.length : 0;
|
|
874
1014
|
process.stderr.write(` ✓ Custom tools: ${customToolCount}\n`);
|
|
875
1015
|
process.stderr.write(` ✓ Max turns: ${loopConfig?.max_turns || manifest.max_turns || 25}\n`);
|
|
876
1016
|
}
|
|
@@ -917,6 +1057,71 @@ function registerPublishCommand(program) {
|
|
|
917
1057
|
if (manifest.required_secrets?.length) {
|
|
918
1058
|
process.stderr.write(` Secrets: ${manifest.required_secrets.join(', ')}\n`);
|
|
919
1059
|
}
|
|
1060
|
+
if (manifest.environment) {
|
|
1061
|
+
const envParts = [];
|
|
1062
|
+
if (manifest.environment.python_version)
|
|
1063
|
+
envParts.push(`Python ${manifest.environment.python_version}`);
|
|
1064
|
+
if (manifest.environment.node_version)
|
|
1065
|
+
envParts.push(`Node ${manifest.environment.node_version}`);
|
|
1066
|
+
if (manifest.environment.pip_flags)
|
|
1067
|
+
envParts.push(`pip: ${manifest.environment.pip_flags}`);
|
|
1068
|
+
if (manifest.environment.npm_flags)
|
|
1069
|
+
envParts.push(`npm: ${manifest.environment.npm_flags}`);
|
|
1070
|
+
if (envParts.length) {
|
|
1071
|
+
process.stderr.write(` Environment: ${envParts.join(', ')}\n`);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
// Server-side validation (BUG-11: dry-run missed server-side checks)
|
|
1075
|
+
process.stderr.write(`\nServer validation...\n`);
|
|
1076
|
+
try {
|
|
1077
|
+
const validation = await (0, api_1.validateAgentPublish)(config, {
|
|
1078
|
+
name: manifest.name,
|
|
1079
|
+
type: canonicalType,
|
|
1080
|
+
run_mode: runMode,
|
|
1081
|
+
runtime: runtimeConfig,
|
|
1082
|
+
loop: loopConfig,
|
|
1083
|
+
callable,
|
|
1084
|
+
description: manifest.description,
|
|
1085
|
+
prompt,
|
|
1086
|
+
url: agentUrl,
|
|
1087
|
+
input_schema: inputSchema,
|
|
1088
|
+
output_schema: outputSchema,
|
|
1089
|
+
is_public: false,
|
|
1090
|
+
supported_providers: supportedProviders,
|
|
1091
|
+
default_models: manifest.default_models,
|
|
1092
|
+
timeout_seconds: manifest.timeout_seconds,
|
|
1093
|
+
run_command: manifest.run_command,
|
|
1094
|
+
sdk_compatible: sdkCompatible || undefined,
|
|
1095
|
+
manifest: manifest.manifest,
|
|
1096
|
+
required_secrets: manifest.required_secrets,
|
|
1097
|
+
default_skills: skillsFromFlag || manifest.default_skills,
|
|
1098
|
+
skills_locked: manifest.skills_locked || options.skillsLocked || undefined,
|
|
1099
|
+
allow_local_download: options.localDownload || false,
|
|
1100
|
+
environment: manifest.environment,
|
|
1101
|
+
}, workspaceId);
|
|
1102
|
+
if (validation.warnings?.length) {
|
|
1103
|
+
for (const warning of validation.warnings) {
|
|
1104
|
+
process.stderr.write(chalk_1.default.yellow(` ⚠ ${warning}\n`));
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if (!validation.valid) {
|
|
1108
|
+
for (const error of validation.errors) {
|
|
1109
|
+
process.stderr.write(chalk_1.default.red(` ✗ ${error}\n`));
|
|
1110
|
+
}
|
|
1111
|
+
process.stderr.write(chalk_1.default.red(`\nDry run failed: server-side validation found ${validation.errors.length} error(s)\n`));
|
|
1112
|
+
process.stderr.write('No changes made (dry run)\n');
|
|
1113
|
+
const err = new errors_1.CliError('Server-side validation failed', errors_1.ExitCodes.INVALID_INPUT);
|
|
1114
|
+
err.displayed = true;
|
|
1115
|
+
throw err;
|
|
1116
|
+
}
|
|
1117
|
+
process.stderr.write(` ✓ Server-side validation passed\n`);
|
|
1118
|
+
}
|
|
1119
|
+
catch (err) {
|
|
1120
|
+
if (err instanceof errors_1.CliError)
|
|
1121
|
+
throw err;
|
|
1122
|
+
// Network or auth errors — show warning but don't block dry-run
|
|
1123
|
+
process.stderr.write(chalk_1.default.yellow(` ⚠ Could not reach server for validation (offline?)\n`));
|
|
1124
|
+
}
|
|
920
1125
|
process.stderr.write(`\nWould publish: ${preview.org_slug}/${manifest.name}@${preview.next_version}\n`);
|
|
921
1126
|
if (shouldUploadBundle) {
|
|
922
1127
|
const bundlePreview = await (0, bundle_1.previewBundle)(cwd, {
|
|
@@ -1012,6 +1217,8 @@ function registerPublishCommand(program) {
|
|
|
1012
1217
|
default_skills: skillsFromFlag || manifest.default_skills,
|
|
1013
1218
|
skills_locked: manifest.skills_locked || options.skillsLocked || undefined,
|
|
1014
1219
|
allow_local_download: options.localDownload || false,
|
|
1220
|
+
// Environment pinning
|
|
1221
|
+
environment: manifest.environment,
|
|
1015
1222
|
}, workspaceId);
|
|
1016
1223
|
}
|
|
1017
1224
|
catch (err) {
|
|
@@ -1185,8 +1392,17 @@ function registerPublishCommand(program) {
|
|
|
1185
1392
|
}
|
|
1186
1393
|
}
|
|
1187
1394
|
if (result.service_key) {
|
|
1188
|
-
process.stdout.write(`\nService key
|
|
1395
|
+
process.stdout.write(`\nService key:\n`);
|
|
1189
1396
|
process.stdout.write(` ${result.service_key}\n`);
|
|
1397
|
+
try {
|
|
1398
|
+
const keyPrefix = result.service_key.substring(0, 12);
|
|
1399
|
+
const savedPath = await (0, key_store_1.saveServiceKey)(org.slug, manifest.name, assignedVersion, result.service_key, keyPrefix);
|
|
1400
|
+
process.stdout.write(` ${chalk_1.default.gray(`Saved to ${savedPath}`)}\n`);
|
|
1401
|
+
}
|
|
1402
|
+
catch {
|
|
1403
|
+
process.stdout.write(` ${chalk_1.default.yellow('Could not save key locally. Copy it now — it cannot be retrieved from the server.')}\n`);
|
|
1404
|
+
}
|
|
1405
|
+
process.stdout.write(` Retrieve later: ${chalk_1.default.cyan(`orch agent-keys list ${org.slug}/${manifest.name}`)}\n`);
|
|
1190
1406
|
}
|
|
1191
1407
|
// Show next-step CLI command based on run mode
|
|
1192
1408
|
const runRef = `${org.slug}/${manifest.name}`;
|