@orchagent/cli 0.3.62 → 0.3.64
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/github.js +0 -7
- package/dist/commands/index.js +2 -0
- package/dist/commands/info.js +5 -0
- package/dist/commands/init.js +132 -15
- package/dist/commands/logs.js +182 -0
- package/dist/commands/publish.js +206 -9
- package/dist/commands/run.js +63 -4
- package/dist/commands/schedule.js +17 -1
- package/dist/commands/test.js +685 -153
- package/dist/index.js +2 -0
- package/dist/lib/api.js +67 -8
- package/dist/lib/dotenv.js +64 -0
- package/dist/lib/errors.js +7 -1
- package/dist/lib/suggest.js +146 -0
- package/package.json +1 -1
package/dist/commands/publish.js
CHANGED
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.extractTemplateVariables = extractTemplateVariables;
|
|
7
7
|
exports.deriveInputSchema = deriveInputSchema;
|
|
8
|
+
exports.scanUndeclaredEnvVars = scanUndeclaredEnvVars;
|
|
9
|
+
exports.checkDependencies = checkDependencies;
|
|
8
10
|
exports.registerPublishCommand = registerPublishCommand;
|
|
9
11
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
12
|
const path_1 = __importDefault(require("path"));
|
|
@@ -14,7 +16,6 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
14
16
|
const config_1 = require("../lib/config");
|
|
15
17
|
const api_1 = require("../lib/api");
|
|
16
18
|
const errors_1 = require("../lib/errors");
|
|
17
|
-
const api_2 = require("../lib/api");
|
|
18
19
|
const analytics_1 = require("../lib/analytics");
|
|
19
20
|
const bundle_1 = require("../lib/bundle");
|
|
20
21
|
/**
|
|
@@ -55,6 +56,68 @@ function deriveInputSchema(variables) {
|
|
|
55
56
|
required: [...variables],
|
|
56
57
|
};
|
|
57
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Scan Python files for environment variable references and return var names
|
|
61
|
+
* that aren't covered by required_secrets or auto-injected by the platform.
|
|
62
|
+
*/
|
|
63
|
+
async function scanUndeclaredEnvVars(agentDir, requiredSecrets) {
|
|
64
|
+
// Auto-injected by the gateway — never need to be in required_secrets
|
|
65
|
+
const autoInjected = new Set([
|
|
66
|
+
'ORCHAGENT_SERVICE_KEY', 'ORCHAGENT_GATEWAY_URL', 'ORCHAGENT_CALL_CHAIN',
|
|
67
|
+
'ORCHAGENT_DEADLINE_MS', 'ORCHAGENT_MAX_HOPS', 'ORCHAGENT_DOWNSTREAM_REMAINING',
|
|
68
|
+
'ORCHAGENT_SDK_REQUIRED', 'ORCHAGENT_BILLING_ORG_ID', 'ORCHAGENT_ROOT_RUN_ID',
|
|
69
|
+
'ORCHAGENT_REQUEST_ID',
|
|
70
|
+
// LLM keys injected via the platform's credential mechanism
|
|
71
|
+
'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GEMINI_API_KEY', 'LLM_MODEL',
|
|
72
|
+
// Standard system env vars
|
|
73
|
+
'PATH', 'HOME', 'USER', 'LANG', 'SHELL', 'TERM', 'PWD', 'TMPDIR',
|
|
74
|
+
]);
|
|
75
|
+
const declared = new Set(requiredSecrets);
|
|
76
|
+
// Python env var access patterns
|
|
77
|
+
const patterns = [
|
|
78
|
+
/os\.environ\s*\[\s*['"]([A-Z][A-Z0-9_]*)['"]\s*\]/g,
|
|
79
|
+
/os\.environ\.get\s*\(\s*['"]([A-Z][A-Z0-9_]*)['"]/g,
|
|
80
|
+
/os\.getenv\s*\(\s*['"]([A-Z][A-Z0-9_]*)['"]/g,
|
|
81
|
+
];
|
|
82
|
+
const found = new Set();
|
|
83
|
+
// Scan .py files in the agent directory (up to 2 levels deep)
|
|
84
|
+
async function scanDir(dir, depth) {
|
|
85
|
+
let entries;
|
|
86
|
+
try {
|
|
87
|
+
entries = await promises_1.default.readdir(dir, { withFileTypes: true });
|
|
88
|
+
if (!entries || !Array.isArray(entries))
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
const name = entry.name;
|
|
96
|
+
const fullPath = path_1.default.join(dir, name);
|
|
97
|
+
if (entry.isDirectory() && depth < 2 && !name.startsWith('.') && name !== 'node_modules' && name !== '__pycache__' && name !== 'venv' && name !== '.venv') {
|
|
98
|
+
await scanDir(fullPath, depth + 1);
|
|
99
|
+
}
|
|
100
|
+
else if (entry.isFile() && name.endsWith('.py')) {
|
|
101
|
+
try {
|
|
102
|
+
const content = await promises_1.default.readFile(fullPath, 'utf-8');
|
|
103
|
+
for (const re of patterns) {
|
|
104
|
+
re.lastIndex = 0;
|
|
105
|
+
let m;
|
|
106
|
+
while ((m = re.exec(content)) !== null) { // eslint-disable-line no-cond-assign
|
|
107
|
+
found.add(m[1]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Skip unreadable files
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
await scanDir(agentDir, 0);
|
|
118
|
+
// Return env vars that are referenced but not declared or auto-injected
|
|
119
|
+
return [...found].filter(v => !declared.has(v) && !autoInjected.has(v)).sort();
|
|
120
|
+
}
|
|
58
121
|
/**
|
|
59
122
|
* Check if orchagent-sdk is listed in requirements.txt or pyproject.toml
|
|
60
123
|
*/
|
|
@@ -207,6 +270,56 @@ function commandForEntrypoint(entrypoint) {
|
|
|
207
270
|
}
|
|
208
271
|
return `python ${entrypoint}`;
|
|
209
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Check if manifest dependencies are published and callable.
|
|
275
|
+
* Best-effort: network errors cause the check to be silently skipped
|
|
276
|
+
* (returns empty array) to avoid false alarms.
|
|
277
|
+
*/
|
|
278
|
+
async function checkDependencies(config, dependencies, publishingOrgSlug, workspaceId) {
|
|
279
|
+
// Pre-fetch user's agents if any deps are in the same org (one API call)
|
|
280
|
+
let myAgents = null;
|
|
281
|
+
const hasSameOrgDeps = dependencies.some(d => {
|
|
282
|
+
const [org] = d.id.split('/');
|
|
283
|
+
return org === publishingOrgSlug;
|
|
284
|
+
});
|
|
285
|
+
if (hasSameOrgDeps) {
|
|
286
|
+
try {
|
|
287
|
+
const headers = {};
|
|
288
|
+
if (workspaceId)
|
|
289
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
290
|
+
myAgents = await (0, api_1.request)(config, 'GET', '/agents', { headers });
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return []; // Can't reach API — skip check entirely
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return Promise.all(dependencies.map(async (dep) => {
|
|
297
|
+
const parts = dep.id.split('/');
|
|
298
|
+
const ref = `${dep.id}@${dep.version}`;
|
|
299
|
+
if (parts.length !== 2)
|
|
300
|
+
return { ref, status: 'not_found' };
|
|
301
|
+
const [depOrg, depName] = parts;
|
|
302
|
+
// Same org: check against pre-fetched agent list
|
|
303
|
+
if (depOrg === publishingOrgSlug && myAgents) {
|
|
304
|
+
const match = myAgents.find(a => a.name === depName && a.version === dep.version);
|
|
305
|
+
if (!match)
|
|
306
|
+
return { ref, status: 'not_found' };
|
|
307
|
+
return { ref, status: match.callable ? 'found_callable' : 'found_not_callable' };
|
|
308
|
+
}
|
|
309
|
+
// Different org: try public endpoint
|
|
310
|
+
try {
|
|
311
|
+
const agent = await (0, api_1.getPublicAgent)(config, depOrg, depName, dep.version);
|
|
312
|
+
return { ref, status: agent.callable ? 'found_callable' : 'found_not_callable' };
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
if (err?.status === 404) {
|
|
316
|
+
return { ref, status: 'not_found' };
|
|
317
|
+
}
|
|
318
|
+
// Network/unexpected error — don't false alarm
|
|
319
|
+
return { ref, status: 'found_callable' };
|
|
320
|
+
}
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
210
323
|
function registerPublishCommand(program) {
|
|
211
324
|
program
|
|
212
325
|
.command('publish')
|
|
@@ -224,18 +337,33 @@ function registerPublishCommand(program) {
|
|
|
224
337
|
: undefined;
|
|
225
338
|
const config = await (0, config_1.getResolvedConfig)({}, options.profile);
|
|
226
339
|
const cwd = process.cwd();
|
|
340
|
+
// Resolve workspace context — if `orch workspace use` was called, publish
|
|
341
|
+
// to that workspace instead of the personal org (F-5)
|
|
342
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
343
|
+
let workspaceId;
|
|
344
|
+
if (configFile.workspace) {
|
|
345
|
+
const { workspaces } = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
346
|
+
const ws = workspaces.find(w => w.slug === configFile.workspace);
|
|
347
|
+
if (!ws) {
|
|
348
|
+
throw new errors_1.CliError(`Workspace '${configFile.workspace}' not found. Run \`orch workspace list\` to see available workspaces.`);
|
|
349
|
+
}
|
|
350
|
+
workspaceId = ws.id;
|
|
351
|
+
}
|
|
227
352
|
// Check for SKILL.md first (skills take precedence)
|
|
228
353
|
const skillMdPath = path_1.default.join(cwd, 'SKILL.md');
|
|
229
354
|
const skillData = await parseSkillMd(skillMdPath);
|
|
230
355
|
if (skillData) {
|
|
231
356
|
// Publish as a skill (server auto-assigns version)
|
|
232
|
-
const org = await (0, api_1.getOrg)(config);
|
|
357
|
+
const org = await (0, api_1.getOrg)(config, workspaceId);
|
|
358
|
+
if (workspaceId && !options.dryRun) {
|
|
359
|
+
process.stdout.write(`Workspace: ${org.slug}\n`);
|
|
360
|
+
}
|
|
233
361
|
// SC-05: Collect all files in the skill directory for multi-file support
|
|
234
362
|
const skillFiles = await collectSkillFiles(cwd);
|
|
235
363
|
const hasMultipleFiles = skillFiles.length > 1;
|
|
236
364
|
// Handle dry-run for skills
|
|
237
365
|
if (options.dryRun) {
|
|
238
|
-
const preview = await (0, api_1.previewAgentVersion)(config, skillData.frontmatter.name);
|
|
366
|
+
const preview = await (0, api_1.previewAgentVersion)(config, skillData.frontmatter.name, workspaceId);
|
|
239
367
|
const skillBodyBytes = Buffer.byteLength(skillData.body, 'utf-8');
|
|
240
368
|
const totalFilesSize = skillFiles.reduce((sum, f) => sum + f.size, 0);
|
|
241
369
|
const versionInfo = preview.existing_versions.length > 0
|
|
@@ -252,6 +380,9 @@ function registerPublishCommand(program) {
|
|
|
252
380
|
process.stderr.write('\nSkill Preview:\n');
|
|
253
381
|
process.stderr.write(` Name: ${skillData.frontmatter.name}\n`);
|
|
254
382
|
process.stderr.write(` Type: skill\n`);
|
|
383
|
+
if (workspaceId) {
|
|
384
|
+
process.stderr.write(` Workspace: ${org.slug}\n`);
|
|
385
|
+
}
|
|
255
386
|
process.stderr.write(` Version: ${versionInfo}\n`);
|
|
256
387
|
process.stderr.write(` Visibility: private\n`);
|
|
257
388
|
process.stderr.write(` Providers: any\n`);
|
|
@@ -282,7 +413,7 @@ function registerPublishCommand(program) {
|
|
|
282
413
|
// SC-05: Include all skill files for UI preview
|
|
283
414
|
skill_files: hasMultipleFiles ? skillFiles : undefined,
|
|
284
415
|
allow_local_download: options.localDownload || false,
|
|
285
|
-
});
|
|
416
|
+
}, workspaceId);
|
|
286
417
|
const skillVersion = skillResult.agent?.version || 'v1';
|
|
287
418
|
const skillAgentId = skillResult.agent?.id;
|
|
288
419
|
await (0, analytics_1.track)('cli_publish', { agent_type: 'skill', multi_file: hasMultipleFiles });
|
|
@@ -511,8 +642,11 @@ function registerPublishCommand(program) {
|
|
|
511
642
|
if (options.docker && executionEngine !== 'code_runtime') {
|
|
512
643
|
throw new errors_1.CliError('--docker is only supported for code runtime agents');
|
|
513
644
|
}
|
|
514
|
-
// Get org info
|
|
515
|
-
const org = await (0, api_1.getOrg)(config);
|
|
645
|
+
// Get org info (workspace-aware — returns workspace org if workspace is active)
|
|
646
|
+
const org = await (0, api_1.getOrg)(config, workspaceId);
|
|
647
|
+
if (workspaceId && !options.dryRun) {
|
|
648
|
+
process.stdout.write(`Workspace: ${org.slug}\n`);
|
|
649
|
+
}
|
|
516
650
|
// Default to 'any' provider if not specified
|
|
517
651
|
const supportedProviders = manifest.supported_providers || ['any'];
|
|
518
652
|
// Detect SDK compatibility for code runtime agents
|
|
@@ -523,9 +657,33 @@ function registerPublishCommand(program) {
|
|
|
523
657
|
process.stdout.write(`SDK detected - agent will be marked as Local Ready\n`);
|
|
524
658
|
}
|
|
525
659
|
}
|
|
660
|
+
// Check if manifest dependencies are published and callable (F-9b).
|
|
661
|
+
// Runs for both dry-run and normal publish so users catch issues early.
|
|
662
|
+
const manifestDeps = manifest.manifest?.dependencies;
|
|
663
|
+
if (manifestDeps?.length) {
|
|
664
|
+
const depResults = await checkDependencies(config, manifestDeps, org.slug, workspaceId);
|
|
665
|
+
const notFound = depResults.filter(r => r.status === 'not_found');
|
|
666
|
+
const notCallable = depResults.filter(r => r.status === 'found_not_callable');
|
|
667
|
+
if (notFound.length > 0) {
|
|
668
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ Unpublished dependencies:\n`));
|
|
669
|
+
for (const dep of notFound) {
|
|
670
|
+
process.stderr.write(chalk_1.default.yellow(` - ${dep.ref}\n`));
|
|
671
|
+
}
|
|
672
|
+
process.stderr.write(`\n These agents must be published before this orchestrator can call them.\n` +
|
|
673
|
+
` Publish each dependency first, then re-run this publish.\n\n`);
|
|
674
|
+
}
|
|
675
|
+
if (notCallable.length > 0) {
|
|
676
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ Dependencies not marked as callable:\n`));
|
|
677
|
+
for (const dep of notCallable) {
|
|
678
|
+
process.stderr.write(chalk_1.default.yellow(` - ${dep.ref}\n`));
|
|
679
|
+
}
|
|
680
|
+
process.stderr.write(`\n Agents must have callable: true in orchagent.json to be invoked\n` +
|
|
681
|
+
` by orchestrators. Update and republish each dependency.\n\n`);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
526
684
|
// Handle dry-run for agents
|
|
527
685
|
if (options.dryRun) {
|
|
528
|
-
const preview = await (0, api_1.previewAgentVersion)(config, manifest.name);
|
|
686
|
+
const preview = await (0, api_1.previewAgentVersion)(config, manifest.name, workspaceId);
|
|
529
687
|
const versionInfo = preview.existing_versions.length > 0
|
|
530
688
|
? `${preview.next_version} (new version, ${preview.existing_versions[preview.existing_versions.length - 1]} exists)`
|
|
531
689
|
: `${preview.next_version} (first version)`;
|
|
@@ -578,6 +736,9 @@ function registerPublishCommand(program) {
|
|
|
578
736
|
process.stderr.write('\nAgent Preview:\n');
|
|
579
737
|
process.stderr.write(` Name: ${manifest.name}\n`);
|
|
580
738
|
process.stderr.write(` Type: ${canonicalType}\n`);
|
|
739
|
+
if (workspaceId) {
|
|
740
|
+
process.stderr.write(` Workspace: ${org.slug}\n`);
|
|
741
|
+
}
|
|
581
742
|
process.stderr.write(` Run mode: ${runMode}\n`);
|
|
582
743
|
process.stderr.write(` Engine: ${executionEngine}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
|
|
583
744
|
process.stderr.write(` Callable: ${callable ? 'enabled' : 'disabled'}\n`);
|
|
@@ -605,6 +766,26 @@ function registerPublishCommand(program) {
|
|
|
605
766
|
process.stderr.write('No changes made (dry run)\n');
|
|
606
767
|
return;
|
|
607
768
|
}
|
|
769
|
+
// Warn if ORCHAGENT_SERVICE_KEY is in required_secrets — the gateway
|
|
770
|
+
// auto-injects it for agents with manifest dependencies (F-12).
|
|
771
|
+
if (manifest.required_secrets?.includes('ORCHAGENT_SERVICE_KEY')) {
|
|
772
|
+
process.stderr.write('\n⚠ Warning: ORCHAGENT_SERVICE_KEY found in required_secrets.\n' +
|
|
773
|
+
' The gateway auto-injects this for agents with manifest dependencies.\n' +
|
|
774
|
+
' Having it in required_secrets can override the auto-injected key and\n' +
|
|
775
|
+
' break orchestration. Remove it from required_secrets in orchagent.json.\n\n');
|
|
776
|
+
}
|
|
777
|
+
// Scan code for env var references not covered by required_secrets (F-1a).
|
|
778
|
+
// Only relevant for agents with code (code_runtime engine).
|
|
779
|
+
if (executionEngine === 'code_runtime') {
|
|
780
|
+
const undeclared = await scanUndeclaredEnvVars(cwd, manifest.required_secrets || []);
|
|
781
|
+
if (undeclared.length > 0) {
|
|
782
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ Your code references environment variables not in required_secrets:\n`) +
|
|
783
|
+
chalk_1.default.yellow(` ${undeclared.join(', ')}\n\n`) +
|
|
784
|
+
` If these should be workspace secrets, add them to required_secrets\n` +
|
|
785
|
+
` in orchagent.json so they're available in the sandbox at runtime.\n` +
|
|
786
|
+
` (Platform-injected vars like LLM API keys are already excluded.)\n\n`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
608
789
|
// Create the agent (server auto-assigns version)
|
|
609
790
|
let result;
|
|
610
791
|
try {
|
|
@@ -636,11 +817,11 @@ function registerPublishCommand(program) {
|
|
|
636
817
|
default_skills: skillsFromFlag || manifest.default_skills,
|
|
637
818
|
skills_locked: manifest.skills_locked || options.skillsLocked || undefined,
|
|
638
819
|
allow_local_download: options.localDownload || false,
|
|
639
|
-
});
|
|
820
|
+
}, workspaceId);
|
|
640
821
|
}
|
|
641
822
|
catch (err) {
|
|
642
823
|
// Improve SECURITY_BLOCKED error display
|
|
643
|
-
if (err instanceof
|
|
824
|
+
if (err instanceof api_1.ApiError && err.status === 422) {
|
|
644
825
|
const payload = err.payload;
|
|
645
826
|
const errorCode = payload?.error?.code;
|
|
646
827
|
if (errorCode === 'SECURITY_BLOCKED') {
|
|
@@ -772,6 +953,22 @@ function registerPublishCommand(program) {
|
|
|
772
953
|
process.stdout.write(`\nService key (save this - shown only once):\n`);
|
|
773
954
|
process.stdout.write(` ${result.service_key}\n`);
|
|
774
955
|
}
|
|
956
|
+
// Show next-step CLI command based on run mode
|
|
957
|
+
const runRef = `${org.slug}/${manifest.name}`;
|
|
958
|
+
if (runMode === 'always_on') {
|
|
959
|
+
process.stdout.write(`\nDeploy as service:\n`);
|
|
960
|
+
process.stdout.write(` orch service deploy ${runRef}\n`);
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
const schemaProps = inputSchema && typeof inputSchema === 'object' && 'properties' in inputSchema
|
|
964
|
+
? Object.keys(inputSchema.properties).slice(0, 3)
|
|
965
|
+
: null;
|
|
966
|
+
const exampleFields = schemaProps?.length
|
|
967
|
+
? schemaProps.map(k => `"${k}": "..."`).join(', ')
|
|
968
|
+
: '"input": "..."';
|
|
969
|
+
process.stdout.write(`\nRun with CLI:\n`);
|
|
970
|
+
process.stdout.write(` orch run ${runRef} --data '{${exampleFields}}'\n`);
|
|
971
|
+
}
|
|
775
972
|
process.stdout.write(`\nAPI endpoint:\n`);
|
|
776
973
|
process.stdout.write(` POST ${config.apiUrl}/${org.slug}/${manifest.name}/${assignedVersion}/run\n`);
|
|
777
974
|
if (shouldUploadBundle) {
|
package/dist/commands/run.js
CHANGED
|
@@ -46,6 +46,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
46
46
|
const os_1 = __importDefault(require("os"));
|
|
47
47
|
const child_process_1 = require("child_process");
|
|
48
48
|
const chalk_1 = __importDefault(require("chalk"));
|
|
49
|
+
const dotenv_1 = require("../lib/dotenv");
|
|
49
50
|
const config_1 = require("../lib/config");
|
|
50
51
|
const api_1 = require("../lib/api");
|
|
51
52
|
const errors_1 = require("../lib/errors");
|
|
@@ -1303,6 +1304,17 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1303
1304
|
runtime: manifest.runtime || null,
|
|
1304
1305
|
loop: manifest.loop || null,
|
|
1305
1306
|
});
|
|
1307
|
+
// Load .env from agent directory (existing env vars take precedence)
|
|
1308
|
+
const dotEnvVars = await (0, dotenv_1.loadDotEnv)(resolved);
|
|
1309
|
+
const dotEnvCount = Object.keys(dotEnvVars).length;
|
|
1310
|
+
if (dotEnvCount > 0) {
|
|
1311
|
+
for (const [key, value] of Object.entries(dotEnvVars)) {
|
|
1312
|
+
if (!(key in process.env) || process.env[key] === undefined) {
|
|
1313
|
+
process.env[key] = value;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
process.stderr.write(chalk_1.default.gray(`Loaded ${dotEnvCount} variable${dotEnvCount === 1 ? '' : 's'} from .env\n`));
|
|
1317
|
+
}
|
|
1306
1318
|
if (localType === 'skill') {
|
|
1307
1319
|
throw new errors_1.CliError('Skills cannot be run directly.\n\n' +
|
|
1308
1320
|
'Skills are instructions meant to be injected into AI agent contexts.\n' +
|
|
@@ -1908,7 +1920,8 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1908
1920
|
sourceLabel = multipart.sourceLabel;
|
|
1909
1921
|
}
|
|
1910
1922
|
} // end of non-injection path
|
|
1911
|
-
const
|
|
1923
|
+
const verboseQs = options.verbose ? '?verbose=true' : '';
|
|
1924
|
+
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${parsed.agent}/${parsed.version}/${endpoint}${verboseQs}`;
|
|
1912
1925
|
// Enable SSE streaming for managed-loop agents (unless --json or --no-stream or --output)
|
|
1913
1926
|
const isManagedLoopAgent = cloudType === 'agent' && cloudEngine === 'managed_loop';
|
|
1914
1927
|
const wantStream = isManagedLoopAgent && !options.json && !options.noStream && !options.output;
|
|
@@ -1985,14 +1998,39 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1985
1998
|
payload.message ||
|
|
1986
1999
|
response.statusText
|
|
1987
2000
|
: response.statusText;
|
|
2001
|
+
const requestId = typeof payload === 'object' && payload
|
|
2002
|
+
? payload.metadata?.request_id
|
|
2003
|
+
: undefined;
|
|
2004
|
+
const refSuffix = requestId ? `\n\nref: ${requestId}` : '';
|
|
2005
|
+
if (errorCode === 'SANDBOX_ERROR') {
|
|
2006
|
+
spinner?.fail('Agent execution failed');
|
|
2007
|
+
const hint = typeof payload === 'object' && payload
|
|
2008
|
+
? payload.error?.hint
|
|
2009
|
+
: undefined;
|
|
2010
|
+
throw new errors_1.CliError(`${message}\n\n` +
|
|
2011
|
+
`This is an error in the agent's code, not the platform.\n` +
|
|
2012
|
+
`Check the agent code and requirements, then republish.` +
|
|
2013
|
+
(hint ? `\n\nHint: ${hint}` : '') +
|
|
2014
|
+
refSuffix);
|
|
2015
|
+
}
|
|
2016
|
+
if (errorCode === 'SANDBOX_TIMEOUT') {
|
|
2017
|
+
spinner?.fail('Agent timed out');
|
|
2018
|
+
throw new errors_1.CliError(`${message}\n\n` +
|
|
2019
|
+
`The agent did not complete in time. Try:\n` +
|
|
2020
|
+
` - Simplifying the input\n` +
|
|
2021
|
+
` - Using a smaller dataset\n` +
|
|
2022
|
+
` - Contacting the agent author to increase the timeout` +
|
|
2023
|
+
refSuffix);
|
|
2024
|
+
}
|
|
1988
2025
|
if (response.status >= 500) {
|
|
1989
2026
|
spinner?.fail(`Server error (${response.status})`);
|
|
1990
2027
|
throw new errors_1.CliError(`${message}\n\n` +
|
|
1991
|
-
`This is a
|
|
1992
|
-
`If it persists,
|
|
2028
|
+
`This is a platform error — try again in a moment.\n` +
|
|
2029
|
+
`If it persists, contact support.` +
|
|
2030
|
+
refSuffix);
|
|
1993
2031
|
}
|
|
1994
2032
|
spinner?.fail(`Run failed: ${message}`);
|
|
1995
|
-
throw new errors_1.CliError(message);
|
|
2033
|
+
throw new errors_1.CliError(message + refSuffix);
|
|
1996
2034
|
}
|
|
1997
2035
|
// Handle SSE streaming response
|
|
1998
2036
|
const contentType = response.headers?.get?.('content-type') || '';
|
|
@@ -2059,6 +2097,9 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2059
2097
|
const total = (usage.input_tokens || 0) + (usage.output_tokens || 0);
|
|
2060
2098
|
parts.push(`${total.toLocaleString()} tokens (${(usage.input_tokens || 0).toLocaleString()} in, ${(usage.output_tokens || 0).toLocaleString()} out)`);
|
|
2061
2099
|
}
|
|
2100
|
+
if (typeof meta.request_id === 'string') {
|
|
2101
|
+
parts.push(`ref: ${meta.request_id}`);
|
|
2102
|
+
}
|
|
2062
2103
|
if (parts.length > 0) {
|
|
2063
2104
|
process.stderr.write(chalk_1.default.gray(`${parts.join(' · ')}\n`));
|
|
2064
2105
|
}
|
|
@@ -2118,6 +2159,20 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2118
2159
|
if (typeof payload === 'object' && payload !== null && 'metadata' in payload) {
|
|
2119
2160
|
const meta = payload.metadata;
|
|
2120
2161
|
if (meta) {
|
|
2162
|
+
// Show sandbox output when --verbose
|
|
2163
|
+
if (options.verbose) {
|
|
2164
|
+
const stderr = meta.stderr;
|
|
2165
|
+
const stdout = meta.stdout;
|
|
2166
|
+
if (stderr) {
|
|
2167
|
+
process.stderr.write(chalk_1.default.bold.yellow('\n--- stderr ---') + '\n' + stderr + '\n');
|
|
2168
|
+
}
|
|
2169
|
+
if (stdout) {
|
|
2170
|
+
process.stderr.write(chalk_1.default.bold.cyan('\n--- stdout ---') + '\n' + stdout + '\n');
|
|
2171
|
+
}
|
|
2172
|
+
if (!stderr && !stdout) {
|
|
2173
|
+
process.stderr.write(chalk_1.default.gray('\nNo sandbox output captured.\n'));
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2121
2176
|
const parts = [];
|
|
2122
2177
|
if (typeof meta.processing_time_ms === 'number') {
|
|
2123
2178
|
parts.push(`${(meta.processing_time_ms / 1000).toFixed(1)}s total`);
|
|
@@ -2130,6 +2185,9 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2130
2185
|
const total = (usage.input_tokens || 0) + (usage.output_tokens || 0);
|
|
2131
2186
|
parts.push(`${total.toLocaleString()} tokens (${(usage.input_tokens || 0).toLocaleString()} in, ${(usage.output_tokens || 0).toLocaleString()} out)`);
|
|
2132
2187
|
}
|
|
2188
|
+
if (typeof meta.request_id === 'string') {
|
|
2189
|
+
parts.push(`ref: ${meta.request_id}`);
|
|
2190
|
+
}
|
|
2133
2191
|
if (parts.length > 0) {
|
|
2134
2192
|
process.stderr.write(chalk_1.default.gray(`\n${parts.join(' · ')}\n`));
|
|
2135
2193
|
}
|
|
@@ -2372,6 +2430,7 @@ function registerRunCommand(program) {
|
|
|
2372
2430
|
.option('--data <json>', 'JSON payload (string or @file, @- for stdin)')
|
|
2373
2431
|
.option('--input <json>', 'Alias for --data')
|
|
2374
2432
|
.option('--json', 'Output raw JSON')
|
|
2433
|
+
.option('--verbose', 'Show sandbox stdout/stderr output (cloud only)')
|
|
2375
2434
|
.option('--provider <provider>', 'LLM provider (openai, anthropic, gemini, ollama)')
|
|
2376
2435
|
.option('--model <model>', 'LLM model to use (overrides agent default)')
|
|
2377
2436
|
.option('--key <key>', 'LLM API key (overrides env vars)')
|
|
@@ -101,9 +101,12 @@ function registerScheduleCommand(program) {
|
|
|
101
101
|
const failsLabel = s.consecutive_failures > 0
|
|
102
102
|
? chalk_1.default.red(String(s.consecutive_failures))
|
|
103
103
|
: chalk_1.default.gray('0');
|
|
104
|
+
const agentLabel = s.auto_update === false
|
|
105
|
+
? `${s.agent_name}@${s.agent_version} ${chalk_1.default.yellow('[pinned]')}`
|
|
106
|
+
: `${s.agent_name}@${s.agent_version}`;
|
|
104
107
|
table.push([
|
|
105
108
|
s.id.slice(0, 8),
|
|
106
|
-
|
|
109
|
+
agentLabel,
|
|
107
110
|
s.schedule_type,
|
|
108
111
|
s.schedule_type === 'cron' ? (s.cron_expression ?? '-') : 'webhook',
|
|
109
112
|
enabledLabel,
|
|
@@ -125,6 +128,7 @@ function registerScheduleCommand(program) {
|
|
|
125
128
|
.option('--timezone <tz>', 'Timezone for cron schedule (default: UTC)', 'UTC')
|
|
126
129
|
.option('--input <json>', 'Input data as JSON string')
|
|
127
130
|
.option('--provider <provider>', 'LLM provider (anthropic, openai, gemini)')
|
|
131
|
+
.option('--pin-version', 'Pin to this version (disable auto-update on publish)')
|
|
128
132
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
129
133
|
.action(async (agentArg, options) => {
|
|
130
134
|
const config = await (0, config_1.getResolvedConfig)();
|
|
@@ -165,6 +169,8 @@ function registerScheduleCommand(program) {
|
|
|
165
169
|
body.input_data = inputData;
|
|
166
170
|
if (options.provider)
|
|
167
171
|
body.llm_provider = options.provider;
|
|
172
|
+
if (options.pinVersion)
|
|
173
|
+
body.auto_update = false;
|
|
168
174
|
const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules`, {
|
|
169
175
|
body: JSON.stringify(body),
|
|
170
176
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -199,6 +205,8 @@ function registerScheduleCommand(program) {
|
|
|
199
205
|
.option('--provider <provider>', 'New LLM provider')
|
|
200
206
|
.option('--enable', 'Enable the schedule')
|
|
201
207
|
.option('--disable', 'Disable the schedule')
|
|
208
|
+
.option('--auto-update', 'Enable auto-update on publish')
|
|
209
|
+
.option('--pin-version', 'Pin to current version (disable auto-update)')
|
|
202
210
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
203
211
|
.action(async (scheduleId, options) => {
|
|
204
212
|
const config = await (0, config_1.getResolvedConfig)();
|
|
@@ -208,6 +216,9 @@ function registerScheduleCommand(program) {
|
|
|
208
216
|
if (options.enable && options.disable) {
|
|
209
217
|
throw new errors_1.CliError('Cannot use both --enable and --disable');
|
|
210
218
|
}
|
|
219
|
+
if (options.autoUpdate && options.pinVersion) {
|
|
220
|
+
throw new errors_1.CliError('Cannot use both --auto-update and --pin-version');
|
|
221
|
+
}
|
|
211
222
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
212
223
|
const updates = {};
|
|
213
224
|
if (options.cron)
|
|
@@ -220,6 +231,10 @@ function registerScheduleCommand(program) {
|
|
|
220
231
|
updates.enabled = true;
|
|
221
232
|
if (options.disable)
|
|
222
233
|
updates.enabled = false;
|
|
234
|
+
if (options.autoUpdate)
|
|
235
|
+
updates.auto_update = true;
|
|
236
|
+
if (options.pinVersion)
|
|
237
|
+
updates.auto_update = false;
|
|
223
238
|
if (options.input) {
|
|
224
239
|
try {
|
|
225
240
|
updates.input_data = JSON.parse(options.input);
|
|
@@ -333,6 +348,7 @@ function registerScheduleCommand(program) {
|
|
|
333
348
|
process.stdout.write(` Timezone: ${s.timezone}\n`);
|
|
334
349
|
}
|
|
335
350
|
process.stdout.write(` Enabled: ${s.enabled ? chalk_1.default.green('yes') : chalk_1.default.red('no')}\n`);
|
|
351
|
+
process.stdout.write(` Auto-update: ${s.auto_update === false ? chalk_1.default.yellow('pinned') : chalk_1.default.green('yes')}\n`);
|
|
336
352
|
if (s.auto_disabled_at) {
|
|
337
353
|
process.stdout.write(` ${chalk_1.default.bgRed.white(' AUTO-DISABLED ')} at ${formatDate(s.auto_disabled_at)}\n`);
|
|
338
354
|
}
|