@orchagent/cli 0.3.99 → 0.3.101
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 +11 -15
- package/dist/commands/diff.js +4 -2
- package/dist/commands/docs.js +6 -1
- package/dist/commands/estimate.js +2 -1
- package/dist/commands/fork.js +2 -1
- package/dist/commands/init.js +15 -8
- package/dist/commands/install.js +18 -30
- package/dist/commands/logs.js +3 -1
- package/dist/commands/publish.js +28 -2
- package/dist/commands/pull.js +11 -29
- package/dist/commands/replay.js +40 -7
- package/dist/commands/run.js +45 -72
- package/dist/commands/schedule.js +39 -5
- package/dist/commands/security.js +19 -24
- package/dist/commands/test.js +12 -3
- package/dist/commands/transfer.js +15 -5
- package/dist/commands/tree.js +15 -1
- package/dist/lib/api.js +30 -14
- package/dist/lib/bundle.js +7 -1
- package/dist/lib/resolve-agent.js +62 -0
- package/package.json +1 -1
package/dist/commands/run.js
CHANGED
|
@@ -54,6 +54,7 @@ const child_process_1 = require("child_process");
|
|
|
54
54
|
const chalk_1 = __importDefault(require("chalk"));
|
|
55
55
|
const dotenv_1 = require("../lib/dotenv");
|
|
56
56
|
const config_1 = require("../lib/config");
|
|
57
|
+
const resolve_agent_1 = require("../lib/resolve-agent");
|
|
57
58
|
const api_1 = require("../lib/api");
|
|
58
59
|
const errors_1 = require("../lib/errors");
|
|
59
60
|
const output_1 = require("../lib/output");
|
|
@@ -84,18 +85,6 @@ function localCommandForEntrypoint(entrypoint) {
|
|
|
84
85
|
}
|
|
85
86
|
return 'python3';
|
|
86
87
|
}
|
|
87
|
-
function parseAgentRef(value) {
|
|
88
|
-
const [ref, versionPart] = value.split('@');
|
|
89
|
-
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
90
|
-
const segments = ref.split('/');
|
|
91
|
-
if (segments.length === 1) {
|
|
92
|
-
return { agent: segments[0], version };
|
|
93
|
-
}
|
|
94
|
-
if (segments.length === 2) {
|
|
95
|
-
return { org: segments[0], agent: segments[1], version };
|
|
96
|
-
}
|
|
97
|
-
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
98
|
-
}
|
|
99
88
|
function canonicalAgentType(typeValue) {
|
|
100
89
|
const normalized = (typeValue || 'agent').toLowerCase();
|
|
101
90
|
// Handle legacy type names: agentic → agent, code → tool
|
|
@@ -656,7 +645,7 @@ async function downloadAgent(config, org, agent, version, workspaceId) {
|
|
|
656
645
|
entrypoint: targetAgent.entrypoint,
|
|
657
646
|
};
|
|
658
647
|
}
|
|
659
|
-
async function downloadBundleWithFallback(config, org, agentName, version, agentId) {
|
|
648
|
+
async function downloadBundleWithFallback(config, org, agentName, version, agentId, workspaceId) {
|
|
660
649
|
try {
|
|
661
650
|
return await (0, api_1.downloadCodeBundle)(config, org, agentName, version);
|
|
662
651
|
}
|
|
@@ -667,7 +656,7 @@ async function downloadBundleWithFallback(config, org, agentName, version, agent
|
|
|
667
656
|
if (!config.apiKey || !agentId) {
|
|
668
657
|
throw new api_1.ApiError(`Bundle for '${org}/${agentName}@${version}' not found`, 404);
|
|
669
658
|
}
|
|
670
|
-
return await (0, api_1.downloadCodeBundleAuthenticated)(config, agentId);
|
|
659
|
+
return await (0, api_1.downloadCodeBundleAuthenticated)(config, agentId, workspaceId);
|
|
671
660
|
}
|
|
672
661
|
async function checkDependencies(config, dependencies) {
|
|
673
662
|
const results = [];
|
|
@@ -738,7 +727,7 @@ async function downloadSkillDependency(config, ref, defaultOrg) {
|
|
|
738
727
|
const skillData = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${parsed.skill}/${parsed.version}/download`);
|
|
739
728
|
await saveAgentLocally(org, parsed.skill, skillData);
|
|
740
729
|
}
|
|
741
|
-
async function downloadDependenciesRecursively(config, depStatuses, visited = new Set()) {
|
|
730
|
+
async function downloadDependenciesRecursively(config, depStatuses, visited = new Set(), workspaceId) {
|
|
742
731
|
for (const status of depStatuses) {
|
|
743
732
|
if (!status.downloadable || !status.agentData)
|
|
744
733
|
continue;
|
|
@@ -750,7 +739,7 @@ async function downloadDependenciesRecursively(config, depStatuses, visited = ne
|
|
|
750
739
|
await (0, spinner_1.withSpinner)(`Downloading dependency: ${depRef}...`, async () => {
|
|
751
740
|
await saveAgentLocally(org, agent, status.agentData);
|
|
752
741
|
if (status.agentData.has_bundle) {
|
|
753
|
-
await saveBundleLocally(config, org, agent, status.dep.version, status.agentData.id);
|
|
742
|
+
await saveBundleLocally(config, org, agent, status.dep.version, status.agentData.id, workspaceId);
|
|
754
743
|
}
|
|
755
744
|
if (resolveExecutionEngine(status.agentData) === 'code_runtime' && (status.agentData.source_url || status.agentData.pip_package)) {
|
|
756
745
|
await installTool(status.agentData);
|
|
@@ -767,7 +756,7 @@ async function downloadDependenciesRecursively(config, depStatuses, visited = ne
|
|
|
767
756
|
}
|
|
768
757
|
if (status.agentData.dependencies && status.agentData.dependencies.length > 0) {
|
|
769
758
|
const nestedStatuses = await checkDependencies(config, status.agentData.dependencies);
|
|
770
|
-
await downloadDependenciesRecursively(config, nestedStatuses, visited);
|
|
759
|
+
await downloadDependenciesRecursively(config, nestedStatuses, visited, workspaceId);
|
|
771
760
|
}
|
|
772
761
|
}
|
|
773
762
|
}
|
|
@@ -1168,7 +1157,7 @@ async function unzipBundle(zipPath, destDir) {
|
|
|
1168
1157
|
});
|
|
1169
1158
|
});
|
|
1170
1159
|
}
|
|
1171
|
-
async function executeBundleAgent(config, org, agentName, version, agentData, args, inputOption) {
|
|
1160
|
+
async function executeBundleAgent(config, org, agentName, version, agentData, args, inputOption, workspaceId) {
|
|
1172
1161
|
const userCwd = process.cwd();
|
|
1173
1162
|
const tempDir = path_1.default.join(os_1.default.tmpdir(), `orchagent-${agentName}-${Date.now()}`);
|
|
1174
1163
|
await promises_1.default.mkdir(tempDir, { recursive: true });
|
|
@@ -1176,7 +1165,7 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
1176
1165
|
const extractDir = path_1.default.join(tempDir, 'agent');
|
|
1177
1166
|
try {
|
|
1178
1167
|
const bundleBuffer = await (0, spinner_1.withSpinner)(`Downloading ${org}/${agentName}@${version} bundle...`, async () => {
|
|
1179
|
-
const buffer = await downloadBundleWithFallback(config, org, agentName, version, agentData.id);
|
|
1168
|
+
const buffer = await downloadBundleWithFallback(config, org, agentName, version, agentData.id, workspaceId);
|
|
1180
1169
|
await promises_1.default.writeFile(bundleZip, buffer);
|
|
1181
1170
|
return buffer;
|
|
1182
1171
|
}, { successText: (buf) => `Downloaded bundle (${buf.length} bytes)` });
|
|
@@ -1391,7 +1380,7 @@ async function saveAgentLocally(org, agent, agentData) {
|
|
|
1391
1380
|
}
|
|
1392
1381
|
return agentDir;
|
|
1393
1382
|
}
|
|
1394
|
-
async function saveBundleLocally(config, org, agent, version, agentId) {
|
|
1383
|
+
async function saveBundleLocally(config, org, agent, version, agentId, workspaceId) {
|
|
1395
1384
|
const agentDir = path_1.default.join(AGENTS_DIR, org, agent);
|
|
1396
1385
|
const bundleDir = path_1.default.join(agentDir, 'bundle');
|
|
1397
1386
|
const metaPath = path_1.default.join(agentDir, 'agent.json');
|
|
@@ -1411,7 +1400,7 @@ async function saveBundleLocally(config, org, agent, version, agentId) {
|
|
|
1411
1400
|
catch {
|
|
1412
1401
|
// Metadata doesn't exist, need to download
|
|
1413
1402
|
}
|
|
1414
|
-
const bundleBuffer = await (0, spinner_1.withSpinner)(`Downloading bundle for ${org}/${agent}@${version}...`, async () => downloadBundleWithFallback(config, org, agent, version, agentId), { successText: `Downloaded bundle for ${org}/${agent}@${version}` });
|
|
1403
|
+
const bundleBuffer = await (0, spinner_1.withSpinner)(`Downloading bundle for ${org}/${agent}@${version}...`, async () => downloadBundleWithFallback(config, org, agent, version, agentId, workspaceId), { successText: `Downloaded bundle for ${org}/${agent}@${version}` });
|
|
1415
1404
|
const tempZip = path_1.default.join(os_1.default.tmpdir(), `bundle-${Date.now()}.zip`);
|
|
1416
1405
|
await promises_1.default.writeFile(tempZip, bundleBuffer);
|
|
1417
1406
|
try {
|
|
@@ -1869,15 +1858,8 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1869
1858
|
if (!resolved.apiKey) {
|
|
1870
1859
|
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
1871
1860
|
}
|
|
1872
|
-
const
|
|
1873
|
-
const
|
|
1874
|
-
const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
|
|
1875
|
-
if (!org) {
|
|
1876
|
-
throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
|
|
1877
|
-
}
|
|
1878
|
-
// Resolve workspace context for the target org
|
|
1879
|
-
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
|
|
1880
|
-
const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, parsed.agent, parsed.version, workspaceId);
|
|
1861
|
+
const { org, agent: agentName, version, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(agentRef, resolved);
|
|
1862
|
+
const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, agentName, version, workspaceId);
|
|
1881
1863
|
const cloudType = canonicalAgentType(agentMeta.type);
|
|
1882
1864
|
const cloudEngine = resolveExecutionEngine({
|
|
1883
1865
|
type: agentMeta.type,
|
|
@@ -1891,10 +1873,8 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1891
1873
|
const agentRequiredSecrets = agentMeta.required_secrets;
|
|
1892
1874
|
if (agentRequiredSecrets?.length) {
|
|
1893
1875
|
try {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
if (wsId) {
|
|
1897
|
-
const secretsResult = await (0, api_1.request)(resolved, 'GET', `/workspaces/${wsId}/secrets`);
|
|
1876
|
+
if (workspaceId) {
|
|
1877
|
+
const secretsResult = await (0, api_1.request)(resolved, 'GET', `/workspaces/${workspaceId}/secrets`);
|
|
1898
1878
|
const existingNames = new Set(secretsResult.secrets.map((s) => s.name));
|
|
1899
1879
|
const missing = agentRequiredSecrets.filter((s) => !existingNames.has(s));
|
|
1900
1880
|
if (missing.length > 0) {
|
|
@@ -1918,7 +1898,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1918
1898
|
// --estimate-only: show cost estimate and exit without running
|
|
1919
1899
|
if (options.estimate || options.estimateOnly) {
|
|
1920
1900
|
try {
|
|
1921
|
-
const est = await (0, api_1.getAgentCostEstimate)(resolved, org,
|
|
1901
|
+
const est = await (0, api_1.getAgentCostEstimate)(resolved, org, agentName, version);
|
|
1922
1902
|
const e = est.estimate;
|
|
1923
1903
|
if (e.sample_size === 0) {
|
|
1924
1904
|
process.stderr.write(chalk_1.default.yellow('\nNo run history available for cost estimation.\n'));
|
|
@@ -2245,7 +2225,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2245
2225
|
}
|
|
2246
2226
|
} // end of non-injection path
|
|
2247
2227
|
const verboseQs = options.verbose ? '?verbose=true' : '';
|
|
2248
|
-
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${
|
|
2228
|
+
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${agentName}/${version}/${endpoint}${verboseQs}`;
|
|
2249
2229
|
// Enable SSE streaming for sandbox-backed engines (unless --json or --no-stream or --output)
|
|
2250
2230
|
const isManagedLoopAgent = cloudType === 'agent' && cloudEngine === 'managed_loop';
|
|
2251
2231
|
const isCodeRuntimeAgent = cloudEngine === 'code_runtime';
|
|
@@ -2257,13 +2237,13 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2257
2237
|
}
|
|
2258
2238
|
// Print verbose debug info before request
|
|
2259
2239
|
if (options.verbose && !options.json) {
|
|
2260
|
-
process.stderr.write(chalk_1.default.gray(`\n[verbose] ${org}/${
|
|
2240
|
+
process.stderr.write(chalk_1.default.gray(`\n[verbose] ${org}/${agentName}@${version}\n` +
|
|
2261
2241
|
`[verbose] type=${cloudType} engine=${cloudEngine} endpoint=${endpoint}\n` +
|
|
2262
2242
|
`[verbose] stream=${wantStream ? 'yes' : 'no'} url=${url}\n`));
|
|
2263
2243
|
}
|
|
2264
2244
|
const { spinner, dispose: disposeSpinner } = options.json
|
|
2265
2245
|
? { spinner: null, dispose: () => { } }
|
|
2266
|
-
: (0, spinner_1.createElapsedSpinner)(`Running ${org}/${
|
|
2246
|
+
: (0, spinner_1.createElapsedSpinner)(`Running ${org}/${agentName}@${version}...`);
|
|
2267
2247
|
spinner?.start();
|
|
2268
2248
|
// Streamed sandbox runs can take longer; use 10 min timeout (or --wait-timeout).
|
|
2269
2249
|
const waitTimeoutSec = options.waitTimeout ? parseInt(options.waitTimeout, 10) : undefined;
|
|
@@ -2432,11 +2412,11 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2432
2412
|
let finalPayload = null;
|
|
2433
2413
|
let hadError = false;
|
|
2434
2414
|
if (options.verbose) {
|
|
2435
|
-
process.stderr.write(chalk_1.default.gray(`\nStreaming ${org}/${
|
|
2415
|
+
process.stderr.write(chalk_1.default.gray(`\nStreaming ${org}/${agentName}@${version} (verbose):\n`));
|
|
2436
2416
|
process.stderr.write(chalk_1.default.gray(` type=${cloudType} engine=${cloudEngine} endpoint=${endpoint}\n`));
|
|
2437
2417
|
}
|
|
2438
2418
|
else {
|
|
2439
|
-
process.stderr.write(chalk_1.default.gray(`\nStreaming ${org}/${
|
|
2419
|
+
process.stderr.write(chalk_1.default.gray(`\nStreaming ${org}/${agentName}@${version}:\n`));
|
|
2440
2420
|
}
|
|
2441
2421
|
let progressErrorShown = false;
|
|
2442
2422
|
let streamTimedOut = false;
|
|
@@ -2497,7 +2477,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2497
2477
|
? ''
|
|
2498
2478
|
: chalk_1.default.gray('Tip: Use --wait-timeout <seconds> to wait longer.\n')));
|
|
2499
2479
|
await (0, analytics_1.track)('cli_run', {
|
|
2500
|
-
agent: `${org}/${
|
|
2480
|
+
agent: `${org}/${agentName}@${version}`,
|
|
2501
2481
|
input_type: hasInjection ? 'file_injection' : unkeyedFileArgs.length > 0 ? 'file' : options.data ? 'json' : 'empty',
|
|
2502
2482
|
mode: 'cloud',
|
|
2503
2483
|
streamed: true,
|
|
@@ -2508,7 +2488,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2508
2488
|
throw err;
|
|
2509
2489
|
}
|
|
2510
2490
|
await (0, analytics_1.track)('cli_run', {
|
|
2511
|
-
agent: `${org}/${
|
|
2491
|
+
agent: `${org}/${agentName}@${version}`,
|
|
2512
2492
|
input_type: hasInjection ? 'file_injection' : unkeyedFileArgs.length > 0 ? 'file' : options.data ? 'json' : 'empty',
|
|
2513
2493
|
mode: 'cloud',
|
|
2514
2494
|
streamed: true,
|
|
@@ -2549,14 +2529,14 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2549
2529
|
}
|
|
2550
2530
|
const runId = response.headers?.get?.('x-run-id');
|
|
2551
2531
|
if (runId) {
|
|
2552
|
-
process.stderr.write(chalk_1.default.gray(`
|
|
2532
|
+
process.stderr.write(chalk_1.default.gray(`Snapshot saved · Logs: orch logs ${runId} · Replay: orch replay ${runId}\n`));
|
|
2553
2533
|
}
|
|
2554
2534
|
}
|
|
2555
2535
|
}
|
|
2556
2536
|
}
|
|
2557
2537
|
return;
|
|
2558
2538
|
}
|
|
2559
|
-
spinner?.succeed(`Ran ${org}/${
|
|
2539
|
+
spinner?.succeed(`Ran ${org}/${agentName}@${version}`);
|
|
2560
2540
|
const inputType = hasInjection
|
|
2561
2541
|
? 'file_injection'
|
|
2562
2542
|
: unkeyedFileArgs.length > 0
|
|
@@ -2569,7 +2549,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2569
2549
|
? 'metadata'
|
|
2570
2550
|
: 'empty';
|
|
2571
2551
|
await (0, analytics_1.track)('cli_run', {
|
|
2572
|
-
agent: `${org}/${
|
|
2552
|
+
agent: `${org}/${agentName}@${version}`,
|
|
2573
2553
|
input_type: inputType,
|
|
2574
2554
|
mode: 'cloud',
|
|
2575
2555
|
});
|
|
@@ -2638,7 +2618,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2638
2618
|
}
|
|
2639
2619
|
const runId = response.headers?.get?.('x-run-id');
|
|
2640
2620
|
if (runId) {
|
|
2641
|
-
process.stderr.write(chalk_1.default.gray(`
|
|
2621
|
+
process.stderr.write(chalk_1.default.gray(`Snapshot saved · Logs: orch logs ${runId} · Replay: orch replay ${runId}\n`));
|
|
2642
2622
|
}
|
|
2643
2623
|
}
|
|
2644
2624
|
}
|
|
@@ -2670,21 +2650,14 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2670
2650
|
}
|
|
2671
2651
|
}
|
|
2672
2652
|
const resolved = await (0, config_1.getResolvedConfig)();
|
|
2673
|
-
const
|
|
2674
|
-
const configFile = await (0, config_1.loadConfig)();
|
|
2675
|
-
const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
|
|
2676
|
-
if (!org) {
|
|
2677
|
-
throw new errors_1.CliError('Missing org. Use org/agent format.');
|
|
2678
|
-
}
|
|
2679
|
-
// Resolve workspace context for the target org
|
|
2680
|
-
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
|
|
2653
|
+
const { org, agent: localAgentName, version: localVersion, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(agentRef, resolved);
|
|
2681
2654
|
// Download agent definition with spinner
|
|
2682
|
-
const agentData = await (0, spinner_1.withSpinner)(`Downloading ${org}/${
|
|
2655
|
+
const agentData = await (0, spinner_1.withSpinner)(`Downloading ${org}/${localAgentName}@${localVersion}...`, async () => {
|
|
2683
2656
|
try {
|
|
2684
|
-
return await downloadAgent(resolved, org,
|
|
2657
|
+
return await downloadAgent(resolved, org, localAgentName, localVersion, workspaceId);
|
|
2685
2658
|
}
|
|
2686
2659
|
catch (err) {
|
|
2687
|
-
const agentMeta = await (0, api_1.getPublicAgent)(resolved, org,
|
|
2660
|
+
const agentMeta = await (0, api_1.getPublicAgent)(resolved, org, localAgentName, localVersion);
|
|
2688
2661
|
return {
|
|
2689
2662
|
type: agentMeta.type || 'agent',
|
|
2690
2663
|
run_mode: agentMeta.run_mode ?? null,
|
|
@@ -2696,7 +2669,7 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2696
2669
|
supported_providers: agentMeta.supported_providers || ['any'],
|
|
2697
2670
|
};
|
|
2698
2671
|
}
|
|
2699
|
-
}, { successText: `Downloaded ${org}/${
|
|
2672
|
+
}, { successText: `Downloaded ${org}/${localAgentName}@${localVersion}` });
|
|
2700
2673
|
const localType = canonicalAgentType(agentData.type);
|
|
2701
2674
|
const localEngine = resolveExecutionEngine(agentData);
|
|
2702
2675
|
// Skills cannot be run directly
|
|
@@ -2704,8 +2677,8 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2704
2677
|
throw new errors_1.CliError('Skills cannot be run directly.\n\n' +
|
|
2705
2678
|
'Skills are instructions meant to be injected into AI agent contexts.\n\n' +
|
|
2706
2679
|
'Options:\n' +
|
|
2707
|
-
` Install for AI tools: orchagent skill install ${org}/${
|
|
2708
|
-
` Use with an agent: orchagent run <agent> --skills ${org}/${
|
|
2680
|
+
` Install for AI tools: orchagent skill install ${org}/${localAgentName}\n` +
|
|
2681
|
+
` Use with an agent: orchagent run <agent> --skills ${org}/${localAgentName}`);
|
|
2709
2682
|
}
|
|
2710
2683
|
// Managed-loop agents execute locally with the agent runner.
|
|
2711
2684
|
if (localEngine === 'managed_loop') {
|
|
@@ -2713,11 +2686,11 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2713
2686
|
throw new errors_1.CliError('Agent prompt not available for local execution.\n\n' +
|
|
2714
2687
|
'This agent may have local download disabled.\n' +
|
|
2715
2688
|
'Remove the --local flag to run in the cloud:\n' +
|
|
2716
|
-
` orch run ${org}/${
|
|
2689
|
+
` orch run ${org}/${localAgentName}@${localVersion} --data '{"task": "..."}'`);
|
|
2717
2690
|
}
|
|
2718
2691
|
if (!options.input) {
|
|
2719
2692
|
process.stderr.write(`\nAgent downloaded. Run with:\n`);
|
|
2720
|
-
process.stderr.write(` orch run ${org}/${
|
|
2693
|
+
process.stderr.write(` orch run ${org}/${localAgentName}@${localVersion} --local --data '{\"task\": \"...\"}'\n`);
|
|
2721
2694
|
return;
|
|
2722
2695
|
}
|
|
2723
2696
|
// Resolve @file.json / @- stdin syntax before parsing
|
|
@@ -2731,7 +2704,7 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2731
2704
|
}
|
|
2732
2705
|
warnInputSchemaErrors(agentInputData, agentData.input_schema);
|
|
2733
2706
|
// Write prompt to temp dir and run
|
|
2734
|
-
const tempAgentDir = path_1.default.join(os_1.default.tmpdir(), `orchagent-agent-${
|
|
2707
|
+
const tempAgentDir = path_1.default.join(os_1.default.tmpdir(), `orchagent-agent-${localAgentName}-${Date.now()}`);
|
|
2735
2708
|
await promises_1.default.mkdir(tempAgentDir, { recursive: true });
|
|
2736
2709
|
try {
|
|
2737
2710
|
await promises_1.default.writeFile(path_1.default.join(tempAgentDir, 'prompt.md'), agentData.prompt);
|
|
@@ -2761,10 +2734,10 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2761
2734
|
}
|
|
2762
2735
|
if (choice === 'server') {
|
|
2763
2736
|
process.stderr.write(`\nRun without --local for server execution:\n`);
|
|
2764
|
-
process.stderr.write(` orch run ${org}/${
|
|
2737
|
+
process.stderr.write(` orch run ${org}/${localAgentName}@${localVersion} --data '{...}'\n\n`);
|
|
2765
2738
|
process.exit(0);
|
|
2766
2739
|
}
|
|
2767
|
-
await downloadDependenciesRecursively(resolved, depStatuses);
|
|
2740
|
+
await downloadDependenciesRecursively(resolved, depStatuses, new Set(), workspaceId);
|
|
2768
2741
|
}
|
|
2769
2742
|
// Check if user is overriding locked skills
|
|
2770
2743
|
const agentSkillsLocked = agentData.skills_locked;
|
|
@@ -2784,13 +2757,13 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2784
2757
|
}
|
|
2785
2758
|
}
|
|
2786
2759
|
// Save locally
|
|
2787
|
-
const agentDir = await saveAgentLocally(org,
|
|
2760
|
+
const agentDir = await saveAgentLocally(org, localAgentName, agentData);
|
|
2788
2761
|
process.stderr.write(`\nAgent saved to: ${agentDir}\n`);
|
|
2789
2762
|
if (localEngine === 'code_runtime') {
|
|
2790
2763
|
if (agentData.has_bundle) {
|
|
2791
2764
|
if (options.downloadOnly) {
|
|
2792
2765
|
process.stdout.write(`\nCode runtime bundle is available for local execution.\n`);
|
|
2793
|
-
process.stdout.write(`Run with: orch run ${org}/${
|
|
2766
|
+
process.stdout.write(`Run with: orch run ${org}/${localAgentName} --local [args...]\n`);
|
|
2794
2767
|
return;
|
|
2795
2768
|
}
|
|
2796
2769
|
// Pre-build injected payload for bundle agent if keyed files/mounts present
|
|
@@ -2806,13 +2779,13 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2806
2779
|
});
|
|
2807
2780
|
bundleInput = injected.body;
|
|
2808
2781
|
}
|
|
2809
|
-
await executeBundleAgent(resolved, org,
|
|
2782
|
+
await executeBundleAgent(resolved, org, localAgentName, localVersion, agentData, args, bundleInput, workspaceId);
|
|
2810
2783
|
return;
|
|
2811
2784
|
}
|
|
2812
2785
|
if (agentData.run_command && (agentData.source_url || agentData.pip_package)) {
|
|
2813
2786
|
if (options.downloadOnly) {
|
|
2814
2787
|
process.stdout.write(`\nTool ready for local execution.\n`);
|
|
2815
|
-
process.stdout.write(`Run with: orch run ${org}/${
|
|
2788
|
+
process.stdout.write(`Run with: orch run ${org}/${localAgentName} --local [args...]\n`);
|
|
2816
2789
|
return;
|
|
2817
2790
|
}
|
|
2818
2791
|
await executeTool(agentData, args);
|
|
@@ -2820,12 +2793,12 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2820
2793
|
}
|
|
2821
2794
|
// Fallback: code runtime agent doesn't support local execution.
|
|
2822
2795
|
process.stdout.write(`\nThis code runtime agent is configured for server execution.\n`);
|
|
2823
|
-
process.stdout.write(`\nRun without --local: orch run ${org}/${
|
|
2796
|
+
process.stdout.write(`\nRun without --local: orch run ${org}/${localAgentName}@${localVersion} --data '{...}'\n`);
|
|
2824
2797
|
return;
|
|
2825
2798
|
}
|
|
2826
2799
|
if (options.downloadOnly) {
|
|
2827
2800
|
process.stdout.write(`\nAgent downloaded. Run with:\n`);
|
|
2828
|
-
process.stdout.write(` orch run ${org}/${
|
|
2801
|
+
process.stdout.write(` orch run ${org}/${localAgentName}@${localVersion} --local --input '{...}'\n`);
|
|
2829
2802
|
return;
|
|
2830
2803
|
}
|
|
2831
2804
|
// Check for keyed file/mount injection
|
|
@@ -2835,7 +2808,7 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2835
2808
|
// Direct LLM agents execute locally via prompt composition.
|
|
2836
2809
|
if (!options.input && !execLocalHasInjection) {
|
|
2837
2810
|
process.stdout.write(`\nAgent ready.\n`);
|
|
2838
|
-
process.stdout.write(`Run with: orch run ${org}/${
|
|
2811
|
+
process.stdout.write(`Run with: orch run ${org}/${localAgentName}@${localVersion} --local --input '{...}'\n`);
|
|
2839
2812
|
return;
|
|
2840
2813
|
}
|
|
2841
2814
|
let inputData;
|
|
@@ -162,6 +162,7 @@ function registerScheduleCommand(program) {
|
|
|
162
162
|
.option('--alert-webhook <url>', 'Webhook URL to POST on failure (HTTPS required)')
|
|
163
163
|
.option('--alert-on-failure-count <n>', 'Number of consecutive failures before alerting (default: 3)', parseInt)
|
|
164
164
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
165
|
+
.option('--json', 'Output as JSON')
|
|
165
166
|
.action(async (agentArg, options) => {
|
|
166
167
|
const config = await (0, config_1.getResolvedConfig)();
|
|
167
168
|
if (!config.apiKey) {
|
|
@@ -217,6 +218,10 @@ function registerScheduleCommand(program) {
|
|
|
217
218
|
body: JSON.stringify(body),
|
|
218
219
|
headers: { 'Content-Type': 'application/json' },
|
|
219
220
|
});
|
|
221
|
+
if (options.json) {
|
|
222
|
+
(0, output_1.printJson)(result);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
220
225
|
const s = result.schedule;
|
|
221
226
|
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule created\n\n`);
|
|
222
227
|
process.stdout.write(` ID: ${s.id}\n`);
|
|
@@ -264,6 +269,7 @@ function registerScheduleCommand(program) {
|
|
|
264
269
|
.option('--alert-on-failure-count <n>', 'Number of consecutive failures before alerting', parseInt)
|
|
265
270
|
.option('--clear-alert-webhook', 'Remove the alert webhook URL')
|
|
266
271
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
272
|
+
.option('--json', 'Output as JSON')
|
|
267
273
|
.action(async (partialScheduleId, options) => {
|
|
268
274
|
const config = await (0, config_1.getResolvedConfig)();
|
|
269
275
|
if (!config.apiKey) {
|
|
@@ -322,6 +328,10 @@ function registerScheduleCommand(program) {
|
|
|
322
328
|
body: JSON.stringify(updates),
|
|
323
329
|
headers: { 'Content-Type': 'application/json' },
|
|
324
330
|
});
|
|
331
|
+
if (options.json) {
|
|
332
|
+
(0, output_1.printJson)(result);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
325
335
|
const s = result.schedule;
|
|
326
336
|
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule updated\n\n`);
|
|
327
337
|
process.stdout.write(` ID: ${s.id}\n`);
|
|
@@ -349,6 +359,7 @@ function registerScheduleCommand(program) {
|
|
|
349
359
|
.description('Delete a schedule')
|
|
350
360
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
351
361
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
362
|
+
.option('--json', 'Output as JSON (implies --yes)')
|
|
352
363
|
.action(async (partialScheduleId, options) => {
|
|
353
364
|
const config = await (0, config_1.getResolvedConfig)();
|
|
354
365
|
if (!config.apiKey) {
|
|
@@ -356,7 +367,7 @@ function registerScheduleCommand(program) {
|
|
|
356
367
|
}
|
|
357
368
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
358
369
|
const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
|
|
359
|
-
if (!options.yes) {
|
|
370
|
+
if (!options.yes && !options.json) {
|
|
360
371
|
const rl = promises_1.default.createInterface({
|
|
361
372
|
input: process.stdin,
|
|
362
373
|
output: process.stdout,
|
|
@@ -368,7 +379,11 @@ function registerScheduleCommand(program) {
|
|
|
368
379
|
return;
|
|
369
380
|
}
|
|
370
381
|
}
|
|
371
|
-
await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/schedules/${scheduleId}`);
|
|
382
|
+
const result = await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/schedules/${scheduleId}`);
|
|
383
|
+
if (options.json) {
|
|
384
|
+
(0, output_1.printJson)({ ...result, id: scheduleId });
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
372
387
|
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule ${scheduleId} deleted\n`);
|
|
373
388
|
});
|
|
374
389
|
// orch schedule trigger <schedule-id>
|
|
@@ -378,6 +393,7 @@ function registerScheduleCommand(program) {
|
|
|
378
393
|
.option('--data <json>', 'Override input data as JSON')
|
|
379
394
|
.addOption(new commander_1.Option('--input <json>').hideHelp())
|
|
380
395
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
396
|
+
.option('--json', 'Output as JSON')
|
|
381
397
|
.action(async (partialScheduleId, options) => {
|
|
382
398
|
const config = await (0, config_1.getResolvedConfig)();
|
|
383
399
|
if (!config.apiKey) {
|
|
@@ -396,11 +412,17 @@ function registerScheduleCommand(program) {
|
|
|
396
412
|
throw new errors_1.CliError('Invalid JSON in --data');
|
|
397
413
|
}
|
|
398
414
|
}
|
|
399
|
-
|
|
415
|
+
if (!options.json) {
|
|
416
|
+
process.stdout.write('Triggering schedule...\n');
|
|
417
|
+
}
|
|
400
418
|
const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/trigger`, body ? {
|
|
401
419
|
body,
|
|
402
420
|
headers: { 'Content-Type': 'application/json' },
|
|
403
421
|
} : {});
|
|
422
|
+
if (options.json) {
|
|
423
|
+
(0, output_1.printJson)(result);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
404
426
|
// Status-aware header message
|
|
405
427
|
const isAsync = result.status === 'queued' || result.status === 'deduplicated';
|
|
406
428
|
if (isAsync) {
|
|
@@ -565,6 +587,7 @@ function registerScheduleCommand(program) {
|
|
|
565
587
|
.command('test-alert <schedule-id>')
|
|
566
588
|
.description('Send a test alert to the schedule\'s configured webhook URL')
|
|
567
589
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
590
|
+
.option('--json', 'Output as JSON')
|
|
568
591
|
.action(async (partialScheduleId, options) => {
|
|
569
592
|
const config = await (0, config_1.getResolvedConfig)();
|
|
570
593
|
if (!config.apiKey) {
|
|
@@ -572,8 +595,14 @@ function registerScheduleCommand(program) {
|
|
|
572
595
|
}
|
|
573
596
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
574
597
|
const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
|
|
575
|
-
|
|
598
|
+
if (!options.json) {
|
|
599
|
+
process.stdout.write('Sending test alert...\n');
|
|
600
|
+
}
|
|
576
601
|
const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/test-alert`);
|
|
602
|
+
if (options.json) {
|
|
603
|
+
(0, output_1.printJson)({ ...result, schedule_id: scheduleId });
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
577
606
|
if (result.success) {
|
|
578
607
|
process.stdout.write(chalk_1.default.green('\u2713') + ' Test alert delivered successfully\n');
|
|
579
608
|
}
|
|
@@ -587,6 +616,7 @@ function registerScheduleCommand(program) {
|
|
|
587
616
|
.description('Regenerate the webhook secret (invalidates old URL)')
|
|
588
617
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
589
618
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
619
|
+
.option('--json', 'Output as JSON (implies --yes)')
|
|
590
620
|
.action(async (partialScheduleId, options) => {
|
|
591
621
|
const config = await (0, config_1.getResolvedConfig)();
|
|
592
622
|
if (!config.apiKey) {
|
|
@@ -594,7 +624,7 @@ function registerScheduleCommand(program) {
|
|
|
594
624
|
}
|
|
595
625
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
596
626
|
const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
|
|
597
|
-
if (!options.yes) {
|
|
627
|
+
if (!options.yes && !options.json) {
|
|
598
628
|
const rl = promises_1.default.createInterface({
|
|
599
629
|
input: process.stdin,
|
|
600
630
|
output: process.stdout,
|
|
@@ -609,6 +639,10 @@ function registerScheduleCommand(program) {
|
|
|
609
639
|
}
|
|
610
640
|
}
|
|
611
641
|
const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/regenerate-webhook`);
|
|
642
|
+
if (options.json) {
|
|
643
|
+
(0, output_1.printJson)({ ...result, schedule_id: scheduleId });
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
612
646
|
process.stdout.write(chalk_1.default.green('\u2713') + ' Webhook secret regenerated\n\n');
|
|
613
647
|
process.stdout.write(` ${chalk_1.default.bold('New Webhook URL')} (save this — retrieve later with ${chalk_1.default.cyan('orch schedule info --reveal')}):\n`);
|
|
614
648
|
process.stdout.write(` ${result.webhook_url}\n\n`);
|
|
@@ -11,26 +11,11 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
11
11
|
const config_1 = require("../lib/config");
|
|
12
12
|
const api_1 = require("../lib/api");
|
|
13
13
|
const errors_1 = require("../lib/errors");
|
|
14
|
+
const resolve_agent_1 = require("../lib/resolve-agent");
|
|
14
15
|
const output_1 = require("../lib/output");
|
|
15
16
|
const spinner_1 = require("../lib/spinner");
|
|
16
17
|
const llm_1 = require("../lib/llm");
|
|
17
18
|
const analytics_1 = require("../lib/analytics");
|
|
18
|
-
const DEFAULT_VERSION = 'latest';
|
|
19
|
-
function parseAgentRef(value) {
|
|
20
|
-
const [ref, versionPart] = value.split('@');
|
|
21
|
-
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
22
|
-
const segments = ref.split('/');
|
|
23
|
-
if (segments.length === 1) {
|
|
24
|
-
return { agent: segments[0], version };
|
|
25
|
-
}
|
|
26
|
-
if (segments.length === 2) {
|
|
27
|
-
return { org: segments[0], agent: segments[1], version };
|
|
28
|
-
}
|
|
29
|
-
if (segments.length === 3) {
|
|
30
|
-
return { org: segments[0], agent: segments[1], version: segments[2] };
|
|
31
|
-
}
|
|
32
|
-
throw new errors_1.CliError('Invalid agent reference. Use org/agent/version or org/agent@version format.');
|
|
33
|
-
}
|
|
34
19
|
// Severity color mapping
|
|
35
20
|
function severityColor(severity) {
|
|
36
21
|
switch (severity.toLowerCase()) {
|
|
@@ -189,13 +174,8 @@ Examples:
|
|
|
189
174
|
if (!resolved.apiKey) {
|
|
190
175
|
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
191
176
|
}
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
|
|
195
|
-
if (!org) {
|
|
196
|
-
throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
|
|
197
|
-
}
|
|
198
|
-
const agentId = `${org}/${parsed.agent}/${parsed.version}`;
|
|
177
|
+
const { org, agent: agentName, version, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(agentRef, resolved);
|
|
178
|
+
const agentId = `${org}/${agentName}/${version}`;
|
|
199
179
|
// Detect LLM key for the scan
|
|
200
180
|
let llmKey;
|
|
201
181
|
let llmProvider;
|
|
@@ -208,7 +188,13 @@ Examples:
|
|
|
208
188
|
llmProvider = options.provider;
|
|
209
189
|
}
|
|
210
190
|
else {
|
|
211
|
-
|
|
191
|
+
// Respect --provider preference when detecting local keys
|
|
192
|
+
let providersToCheck = ['any'];
|
|
193
|
+
if (options.provider) {
|
|
194
|
+
(0, llm_1.validateProvider)(options.provider);
|
|
195
|
+
providersToCheck = [options.provider];
|
|
196
|
+
}
|
|
197
|
+
const detected = await (0, llm_1.detectLlmKey)(providersToCheck, resolved);
|
|
212
198
|
if (detected) {
|
|
213
199
|
llmKey = detected.key;
|
|
214
200
|
llmProvider = detected.provider;
|
|
@@ -227,6 +213,12 @@ Examples:
|
|
|
227
213
|
if (options.maxAttacks) {
|
|
228
214
|
requestBody.max_attacks = options.maxAttacks;
|
|
229
215
|
}
|
|
216
|
+
// Send provider preference so gateway can narrow vault key search
|
|
217
|
+
// (even when no local key is found, the gateway resolves from vault)
|
|
218
|
+
const effectiveProvider = llmProvider || options.provider;
|
|
219
|
+
if (effectiveProvider) {
|
|
220
|
+
requestBody.llm_provider = effectiveProvider;
|
|
221
|
+
}
|
|
230
222
|
const url = `${resolved.apiUrl.replace(/\/$/, '')}/security/test`;
|
|
231
223
|
// Make the API call with a spinner
|
|
232
224
|
const spinner = (0, spinner_1.createSpinner)(`Scanning ${agentId} for vulnerabilities...`);
|
|
@@ -236,6 +228,9 @@ Examples:
|
|
|
236
228
|
'Content-Type': 'application/json',
|
|
237
229
|
Authorization: `Bearer ${resolved.apiKey}`,
|
|
238
230
|
};
|
|
231
|
+
if (workspaceId) {
|
|
232
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
233
|
+
}
|
|
239
234
|
if (llmKey) {
|
|
240
235
|
headers['X-LLM-API-Key'] = llmKey;
|
|
241
236
|
}
|
package/dist/commands/test.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NO_LLM_KEY_FIXTURE_MESSAGE = void 0;
|
|
6
7
|
exports.registerTestCommand = registerTestCommand;
|
|
7
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
@@ -18,6 +19,15 @@ const config_1 = require("../lib/config");
|
|
|
18
19
|
const llm_1 = require("../lib/llm");
|
|
19
20
|
const bundle_1 = require("../lib/bundle");
|
|
20
21
|
const test_mock_runner_1 = require("../lib/test-mock-runner");
|
|
22
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
23
|
+
exports.NO_LLM_KEY_FIXTURE_MESSAGE = 'No LLM API key found for fixture tests.\n\n' +
|
|
24
|
+
'Fixture tests run locally on your machine and cannot access workspace vault keys.\n' +
|
|
25
|
+
'Set a local environment variable:\n\n' +
|
|
26
|
+
' export OPENAI_API_KEY=sk-...\n' +
|
|
27
|
+
' export ANTHROPIC_API_KEY=sk-ant-...\n' +
|
|
28
|
+
' export GEMINI_API_KEY=AI...\n\n' +
|
|
29
|
+
'Or add it to a .env file in your agent directory.\n\n' +
|
|
30
|
+
'To run with vault keys instead, use: orch run --cloud';
|
|
21
31
|
// ─── Utility functions ───────────────────────────────────────────────────────
|
|
22
32
|
function validateFixture(data, fixturePath) {
|
|
23
33
|
const fileName = path_1.default.basename(fixturePath);
|
|
@@ -511,11 +521,10 @@ async function runPromptFixtureTests(agentDir, fixtures, verbose, config) {
|
|
|
511
521
|
catch {
|
|
512
522
|
// Schema is optional
|
|
513
523
|
}
|
|
514
|
-
// Detect LLM key
|
|
524
|
+
// Detect LLM key — fixture tests run locally, so vault keys can't be used
|
|
515
525
|
const detected = await (0, llm_1.detectLlmKey)(['any'], config);
|
|
516
526
|
if (!detected) {
|
|
517
|
-
throw new errors_1.CliError(
|
|
518
|
-
'Set an environment variable (e.g., OPENAI_API_KEY) or run `orch secrets set <PROVIDER>_API_KEY <key>`');
|
|
527
|
+
throw new errors_1.CliError(exports.NO_LLM_KEY_FIXTURE_MESSAGE);
|
|
519
528
|
}
|
|
520
529
|
const { provider, key, model: serverModel } = detected;
|
|
521
530
|
const model = serverModel ?? (0, llm_1.getDefaultModel)(provider);
|