@orchagent/cli 0.3.61 → 0.3.63
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 +5 -0
- package/dist/commands/logs.js +182 -0
- package/dist/commands/publish.js +84 -0
- package/dist/commands/run.js +63 -4
- package/dist/commands/schedule.js +17 -1
- package/dist/commands/service.js +251 -0
- package/dist/commands/test.js +685 -153
- package/dist/index.js +2 -0
- package/dist/lib/api.js +52 -2
- 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/github.js
CHANGED
|
@@ -226,13 +226,10 @@ async function importFromGitHub(config, repo, options) {
|
|
|
226
226
|
selectedPath = selection.path;
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
-
// Determine visibility (default to public)
|
|
230
|
-
const isPublic = options.private ? false : true;
|
|
231
229
|
const importResult = await (0, api_1.request)(config, 'POST', '/github/import', {
|
|
232
230
|
body: JSON.stringify({
|
|
233
231
|
repo,
|
|
234
232
|
path: selectedPath,
|
|
235
|
-
is_public: isPublic,
|
|
236
233
|
name: options.name,
|
|
237
234
|
}),
|
|
238
235
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -240,7 +237,6 @@ async function importFromGitHub(config, repo, options) {
|
|
|
240
237
|
await (0, analytics_1.track)('cli_github_import', {
|
|
241
238
|
repo,
|
|
242
239
|
path: selectedPath,
|
|
243
|
-
is_public: isPublic,
|
|
244
240
|
type: importResult.agent.type,
|
|
245
241
|
});
|
|
246
242
|
if (options.json) {
|
|
@@ -253,7 +249,6 @@ async function importFromGitHub(config, repo, options) {
|
|
|
253
249
|
process.stdout.write(` Agent: ${importResult.agent.name}\n`);
|
|
254
250
|
process.stdout.write(` Version: ${importResult.agent.version}\n`);
|
|
255
251
|
process.stdout.write(` Type: ${importResult.agent.type}\n`);
|
|
256
|
-
process.stdout.write(` Public: ${isPublic ? chalk_1.default.green('Yes') : chalk_1.default.yellow('No')}\n`);
|
|
257
252
|
process.stdout.write('\n');
|
|
258
253
|
}
|
|
259
254
|
async function getSyncConfig(config, agentId, options) {
|
|
@@ -335,8 +330,6 @@ function registerGitHubCommand(program) {
|
|
|
335
330
|
.command('import <repo>')
|
|
336
331
|
.description('Import an agent or skill from GitHub')
|
|
337
332
|
.option('--path <path>', 'Path to manifest within repo (scans if not specified)')
|
|
338
|
-
.option('--public', 'Make the agent public (default)')
|
|
339
|
-
.option('--private', 'Make the agent private')
|
|
340
333
|
.option('--name <name>', 'Override agent name')
|
|
341
334
|
.option('--json', 'Output raw JSON')
|
|
342
335
|
.action(async (repo, options) => {
|
package/dist/commands/index.js
CHANGED
|
@@ -33,6 +33,7 @@ const schedule_1 = require("./schedule");
|
|
|
33
33
|
const service_1 = require("./service");
|
|
34
34
|
const transfer_1 = require("./transfer");
|
|
35
35
|
const pull_1 = require("./pull");
|
|
36
|
+
const logs_1 = require("./logs");
|
|
36
37
|
function registerCommands(program) {
|
|
37
38
|
(0, login_1.registerLoginCommand)(program);
|
|
38
39
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -66,4 +67,5 @@ function registerCommands(program) {
|
|
|
66
67
|
(0, service_1.registerServiceCommand)(program);
|
|
67
68
|
(0, transfer_1.registerTransferCommand)(program);
|
|
68
69
|
(0, pull_1.registerPullCommand)(program);
|
|
70
|
+
(0, logs_1.registerLogsCommand)(program);
|
|
69
71
|
}
|
package/dist/commands/info.js
CHANGED
|
@@ -60,6 +60,7 @@ async function getAgentInfo(config, org, agent, version) {
|
|
|
60
60
|
version: publicMeta.version,
|
|
61
61
|
description: (publicMeta.description ?? undefined),
|
|
62
62
|
supported_providers: publicMeta.supported_providers || ['any'],
|
|
63
|
+
callable: publicMeta.callable ?? false,
|
|
63
64
|
input_schema: publicMeta.input_schema,
|
|
64
65
|
output_schema: publicMeta.output_schema,
|
|
65
66
|
source_url: meta.source_url,
|
|
@@ -103,6 +104,7 @@ async function getAgentInfo(config, org, agent, version) {
|
|
|
103
104
|
version: targetAgent.version,
|
|
104
105
|
description: targetAgent.description,
|
|
105
106
|
prompt: targetAgent.prompt,
|
|
107
|
+
callable: targetAgent.callable ?? false,
|
|
106
108
|
input_schema: targetAgent.input_schema,
|
|
107
109
|
output_schema: targetAgent.output_schema,
|
|
108
110
|
supported_providers: targetAgent.supported_providers || ['any'],
|
|
@@ -140,6 +142,9 @@ function registerInfoCommand(program) {
|
|
|
140
142
|
process.stdout.write(`${agentData.description}\n\n`);
|
|
141
143
|
}
|
|
142
144
|
process.stdout.write(`Type: ${agentData.type}\n`);
|
|
145
|
+
if (agentData.callable) {
|
|
146
|
+
process.stdout.write(`Callable: ${chalk_1.default.green('yes')} — other agents can invoke this via the orchagent SDK\n`);
|
|
147
|
+
}
|
|
143
148
|
process.stdout.write(`Providers: ${agentData.supported_providers.join(', ')}\n`);
|
|
144
149
|
// Display pricing information
|
|
145
150
|
const priceStr = (0, pricing_1.formatPrice)(agentData);
|
package/dist/commands/init.js
CHANGED
|
@@ -72,6 +72,9 @@ def main():
|
|
|
72
72
|
user_input = data.get("input", "")
|
|
73
73
|
|
|
74
74
|
# --- Your logic here ---
|
|
75
|
+
# To use workspace secrets, add them to "required_secrets" in orchagent.json:
|
|
76
|
+
# "required_secrets": ["MY_API_KEY"]
|
|
77
|
+
# Then access via: os.environ["MY_API_KEY"]
|
|
75
78
|
result = f"Received: {user_input}"
|
|
76
79
|
# --- End your logic ---
|
|
77
80
|
|
|
@@ -256,10 +259,12 @@ function registerInitCommand(program) {
|
|
|
256
259
|
manifest.description = 'An AI agent with tool use';
|
|
257
260
|
manifest.supported_providers = ['anthropic'];
|
|
258
261
|
manifest.loop = { max_turns: 25 };
|
|
262
|
+
manifest.required_secrets = [];
|
|
259
263
|
}
|
|
260
264
|
else if (initMode.flavor === 'code_runtime') {
|
|
261
265
|
manifest.description = 'A code-runtime agent';
|
|
262
266
|
manifest.runtime = { command: 'python main.py' };
|
|
267
|
+
manifest.required_secrets = [];
|
|
263
268
|
}
|
|
264
269
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
265
270
|
if (initMode.flavor === 'code_runtime') {
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerLogsCommand = registerLogsCommand;
|
|
7
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const config_1 = require("../lib/config");
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
const errors_1 = require("../lib/errors");
|
|
12
|
+
const output_1 = require("../lib/output");
|
|
13
|
+
// ============================================
|
|
14
|
+
// HELPERS
|
|
15
|
+
// ============================================
|
|
16
|
+
async function resolveWorkspaceId(config, slug) {
|
|
17
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
18
|
+
const targetSlug = slug ?? configFile.workspace;
|
|
19
|
+
if (!targetSlug) {
|
|
20
|
+
throw new errors_1.CliError('No workspace specified. Use --workspace <slug> or run `orch workspace use <slug>` first.');
|
|
21
|
+
}
|
|
22
|
+
const response = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
23
|
+
const workspace = response.workspaces.find((w) => w.slug === targetSlug);
|
|
24
|
+
if (!workspace) {
|
|
25
|
+
throw new errors_1.CliError(`Workspace '${targetSlug}' not found.`);
|
|
26
|
+
}
|
|
27
|
+
return workspace.id;
|
|
28
|
+
}
|
|
29
|
+
function formatDate(iso) {
|
|
30
|
+
if (!iso)
|
|
31
|
+
return '-';
|
|
32
|
+
return new Date(iso).toLocaleString();
|
|
33
|
+
}
|
|
34
|
+
function statusColor(status) {
|
|
35
|
+
if (!status)
|
|
36
|
+
return '-';
|
|
37
|
+
switch (status) {
|
|
38
|
+
case 'completed':
|
|
39
|
+
return chalk_1.default.green(status);
|
|
40
|
+
case 'failed':
|
|
41
|
+
return chalk_1.default.red(status);
|
|
42
|
+
case 'running':
|
|
43
|
+
return chalk_1.default.yellow(status);
|
|
44
|
+
case 'timeout':
|
|
45
|
+
return chalk_1.default.red(status);
|
|
46
|
+
default:
|
|
47
|
+
return status;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function formatDuration(ms) {
|
|
51
|
+
if (ms == null)
|
|
52
|
+
return '-';
|
|
53
|
+
if (ms < 1000)
|
|
54
|
+
return `${ms}ms`;
|
|
55
|
+
if (ms < 60000)
|
|
56
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
57
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
|
58
|
+
}
|
|
59
|
+
/** Detect if a string looks like a UUID (run ID) */
|
|
60
|
+
function isUuid(value) {
|
|
61
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
62
|
+
}
|
|
63
|
+
// ============================================
|
|
64
|
+
// COMMAND REGISTRATION
|
|
65
|
+
// ============================================
|
|
66
|
+
function registerLogsCommand(program) {
|
|
67
|
+
program
|
|
68
|
+
.command('logs [target]')
|
|
69
|
+
.description('View execution logs. Use with no args to list recent runs, an agent name to filter, or a run ID for full detail.')
|
|
70
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
71
|
+
.option('--status <status>', 'Filter by status: running, completed, failed, timeout')
|
|
72
|
+
.option('--limit <n>', 'Number of runs to show (default: 20)', '20')
|
|
73
|
+
.option('--json', 'Output as JSON')
|
|
74
|
+
.action(async (target, options) => {
|
|
75
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
76
|
+
if (!config.apiKey) {
|
|
77
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
78
|
+
}
|
|
79
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
80
|
+
// If target looks like a UUID, show detailed logs for that run
|
|
81
|
+
if (target && isUuid(target)) {
|
|
82
|
+
await showRunLogs(config, workspaceId, target, options.json);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Otherwise list runs, optionally filtered by agent name
|
|
86
|
+
await listRuns(config, workspaceId, target, options);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// ============================================
|
|
90
|
+
// LIST RUNS
|
|
91
|
+
// ============================================
|
|
92
|
+
async function listRuns(config, workspaceId, agentName, options) {
|
|
93
|
+
const params = new URLSearchParams();
|
|
94
|
+
if (agentName)
|
|
95
|
+
params.set('agent_name', agentName);
|
|
96
|
+
if (options.status)
|
|
97
|
+
params.set('status', options.status);
|
|
98
|
+
const limit = parseInt(options.limit ?? '20', 10);
|
|
99
|
+
params.set('limit', String(Math.min(Math.max(1, limit), 200)));
|
|
100
|
+
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
101
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/runs${qs}`);
|
|
102
|
+
if (options.json) {
|
|
103
|
+
(0, output_1.printJson)(result);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (result.runs.length === 0) {
|
|
107
|
+
if (agentName) {
|
|
108
|
+
process.stdout.write(`No runs found for agent '${agentName}'.\n`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
process.stdout.write('No runs found in this workspace.\n');
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const table = new cli_table3_1.default({
|
|
116
|
+
head: [
|
|
117
|
+
chalk_1.default.bold('Run ID'),
|
|
118
|
+
chalk_1.default.bold('Agent'),
|
|
119
|
+
chalk_1.default.bold('Status'),
|
|
120
|
+
chalk_1.default.bold('Duration'),
|
|
121
|
+
chalk_1.default.bold('Source'),
|
|
122
|
+
chalk_1.default.bold('Started'),
|
|
123
|
+
chalk_1.default.bold('Error'),
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
result.runs.forEach((r) => {
|
|
127
|
+
const errorPreview = r.error_message
|
|
128
|
+
? chalk_1.default.red(r.error_message.length > 50 ? r.error_message.slice(0, 50) + '...' : r.error_message)
|
|
129
|
+
: chalk_1.default.gray('-');
|
|
130
|
+
table.push([
|
|
131
|
+
r.id.slice(0, 8),
|
|
132
|
+
`${r.agent_name}@${r.agent_version}`,
|
|
133
|
+
statusColor(r.status),
|
|
134
|
+
formatDuration(r.duration_ms),
|
|
135
|
+
r.trigger_source ?? '-',
|
|
136
|
+
formatDate(r.started_at || r.created_at),
|
|
137
|
+
errorPreview,
|
|
138
|
+
]);
|
|
139
|
+
});
|
|
140
|
+
process.stdout.write(table.toString() + '\n');
|
|
141
|
+
if (result.total > result.runs.length) {
|
|
142
|
+
process.stdout.write(chalk_1.default.gray(`\nShowing ${result.runs.length} of ${result.total} runs. Use --limit to see more.\n`));
|
|
143
|
+
}
|
|
144
|
+
process.stdout.write(chalk_1.default.gray('\nView detailed logs for a run: orch logs <run-id>\n'));
|
|
145
|
+
}
|
|
146
|
+
// ============================================
|
|
147
|
+
// SHOW RUN LOGS
|
|
148
|
+
// ============================================
|
|
149
|
+
async function showRunLogs(config, workspaceId, runId, json) {
|
|
150
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/runs/${runId}/logs`);
|
|
151
|
+
if (json) {
|
|
152
|
+
(0, output_1.printJson)(result);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Header
|
|
156
|
+
process.stdout.write(chalk_1.default.bold(`\nRun ${runId}\n`) +
|
|
157
|
+
`Agent: ${result.agent_name ?? '-'}@${result.agent_version ?? '-'}\n` +
|
|
158
|
+
`Status: ${statusColor(result.run_status)}\n` +
|
|
159
|
+
`Duration: ${formatDuration(result.execution_time_ms)}\n`);
|
|
160
|
+
if (result.exit_code != null) {
|
|
161
|
+
const exitLabel = result.exit_code === 0 ? chalk_1.default.green(String(result.exit_code)) : chalk_1.default.red(String(result.exit_code));
|
|
162
|
+
process.stdout.write(`Exit code: ${exitLabel}\n`);
|
|
163
|
+
}
|
|
164
|
+
// Error message
|
|
165
|
+
if (result.error_message) {
|
|
166
|
+
process.stdout.write('\n' + chalk_1.default.red.bold('Error:\n') + chalk_1.default.red(result.error_message) + '\n');
|
|
167
|
+
}
|
|
168
|
+
// Stdout
|
|
169
|
+
if (result.stdout) {
|
|
170
|
+
process.stdout.write('\n' + chalk_1.default.bold.cyan('--- stdout ---') + '\n' + result.stdout + '\n');
|
|
171
|
+
}
|
|
172
|
+
// Stderr
|
|
173
|
+
if (result.stderr) {
|
|
174
|
+
process.stdout.write('\n' + chalk_1.default.bold.yellow('--- stderr ---') + '\n' + result.stderr + '\n');
|
|
175
|
+
}
|
|
176
|
+
// No execution log available
|
|
177
|
+
if (!result.has_execution_log && !result.error_message) {
|
|
178
|
+
process.stdout.write(chalk_1.default.gray('\nNo sandbox output available for this run. ' +
|
|
179
|
+
'Execution logs are captured for agents with a code runtime (tool/agent types with runtime.command).\n'));
|
|
180
|
+
}
|
|
181
|
+
process.stdout.write('\n');
|
|
182
|
+
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -5,6 +5,7 @@ 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;
|
|
8
9
|
exports.registerPublishCommand = registerPublishCommand;
|
|
9
10
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
@@ -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
|
*/
|
|
@@ -605,6 +668,26 @@ function registerPublishCommand(program) {
|
|
|
605
668
|
process.stderr.write('No changes made (dry run)\n');
|
|
606
669
|
return;
|
|
607
670
|
}
|
|
671
|
+
// Warn if ORCHAGENT_SERVICE_KEY is in required_secrets — the gateway
|
|
672
|
+
// auto-injects it for agents with manifest dependencies (F-12).
|
|
673
|
+
if (manifest.required_secrets?.includes('ORCHAGENT_SERVICE_KEY')) {
|
|
674
|
+
process.stderr.write('\n⚠ Warning: ORCHAGENT_SERVICE_KEY found in required_secrets.\n' +
|
|
675
|
+
' The gateway auto-injects this for agents with manifest dependencies.\n' +
|
|
676
|
+
' Having it in required_secrets can override the auto-injected key and\n' +
|
|
677
|
+
' break orchestration. Remove it from required_secrets in orchagent.json.\n\n');
|
|
678
|
+
}
|
|
679
|
+
// Scan code for env var references not covered by required_secrets (F-1a).
|
|
680
|
+
// Only relevant for agents with code (code_runtime engine).
|
|
681
|
+
if (executionEngine === 'code_runtime') {
|
|
682
|
+
const undeclared = await scanUndeclaredEnvVars(cwd, manifest.required_secrets || []);
|
|
683
|
+
if (undeclared.length > 0) {
|
|
684
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ Your code references environment variables not in required_secrets:\n`) +
|
|
685
|
+
chalk_1.default.yellow(` ${undeclared.join(', ')}\n\n`) +
|
|
686
|
+
` If these should be workspace secrets, add them to required_secrets\n` +
|
|
687
|
+
` in orchagent.json so they're available in the sandbox at runtime.\n` +
|
|
688
|
+
` (Platform-injected vars like LLM API keys are already excluded.)\n\n`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
608
691
|
// Create the agent (server auto-assigns version)
|
|
609
692
|
let result;
|
|
610
693
|
try {
|
|
@@ -632,6 +715,7 @@ function registerPublishCommand(program) {
|
|
|
632
715
|
sdk_compatible: sdkCompatible || undefined,
|
|
633
716
|
// Orchestration manifest (includes dependencies)
|
|
634
717
|
manifest: manifest.manifest,
|
|
718
|
+
required_secrets: manifest.required_secrets,
|
|
635
719
|
default_skills: skillsFromFlag || manifest.default_skills,
|
|
636
720
|
skills_locked: manifest.skills_locked || options.skillsLocked || undefined,
|
|
637
721
|
allow_local_download: options.localDownload || false,
|
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
|
}
|