@orchagent/cli 0.3.63 → 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/init.js +127 -15
- package/dist/commands/publish.js +123 -9
- package/dist/lib/api.js +15 -6
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -86,15 +86,15 @@ if __name__ == "__main__":
|
|
|
86
86
|
main()
|
|
87
87
|
`;
|
|
88
88
|
function readmeTemplate(agentName, flavor) {
|
|
89
|
-
const inputField = flavor === 'managed_loop' ? 'task' : 'input';
|
|
90
|
-
const inputDescription = flavor === 'managed_loop' ? 'The task to perform' : 'The input to process';
|
|
89
|
+
const inputField = flavor === 'managed_loop' || flavor === 'orchestrator' ? 'task' : 'input';
|
|
90
|
+
const inputDescription = flavor === 'managed_loop' || flavor === 'orchestrator' ? 'The task to perform' : 'The input to process';
|
|
91
91
|
const cloudExample = flavor === 'code_runtime'
|
|
92
92
|
? `orchagent run ${agentName} --data '{"input": "Hello world"}'`
|
|
93
93
|
: `orchagent run ${agentName} --data '{"${inputField}": "Hello world"}'`;
|
|
94
94
|
const localExample = flavor === 'code_runtime'
|
|
95
95
|
? `orchagent run ${agentName} --local --data '{"input": "Hello world"}'`
|
|
96
96
|
: `orchagent run ${agentName} --local --data '{"${inputField}": "Hello world"}'`;
|
|
97
|
-
|
|
97
|
+
let readme = `# ${agentName}
|
|
98
98
|
|
|
99
99
|
A brief description of what this agent does.
|
|
100
100
|
|
|
@@ -124,6 +124,20 @@ ${localExample}
|
|
|
124
124
|
|-------|------|-------------|
|
|
125
125
|
| \`result\` | string | The agent's response |
|
|
126
126
|
`;
|
|
127
|
+
if (flavor === 'orchestrator') {
|
|
128
|
+
readme += `
|
|
129
|
+
## Dependencies
|
|
130
|
+
|
|
131
|
+
This orchestrator calls other agents. Update \`manifest.dependencies\` in \`orchagent.json\` with your actual dependencies.
|
|
132
|
+
|
|
133
|
+
**Publish order:** Publish dependency agents first, then this orchestrator.
|
|
134
|
+
|
|
135
|
+
| Dependency | Version | Description |
|
|
136
|
+
|------------|---------|-------------|
|
|
137
|
+
| \`org/agent-name\` | v1 | TODO: describe what this agent does |
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
return readme;
|
|
127
141
|
}
|
|
128
142
|
const AGENT_PROMPT_TEMPLATE = `You are a helpful AI agent.
|
|
129
143
|
|
|
@@ -157,6 +171,65 @@ const AGENT_SCHEMA_TEMPLATE = `{
|
|
|
157
171
|
}
|
|
158
172
|
}
|
|
159
173
|
`;
|
|
174
|
+
const ORCHESTRATOR_MAIN_PY = `"""
|
|
175
|
+
orchagent orchestrator entrypoint.
|
|
176
|
+
|
|
177
|
+
Reads JSON input from stdin, calls dependency agents via the orchagent SDK,
|
|
178
|
+
and writes JSON output to stdout.
|
|
179
|
+
|
|
180
|
+
Usage:
|
|
181
|
+
echo '{"task": "do something"}' | python main.py
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
import asyncio
|
|
185
|
+
import json
|
|
186
|
+
import sys
|
|
187
|
+
|
|
188
|
+
from orchagent import AgentClient
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def main():
|
|
192
|
+
# Read JSON input from stdin
|
|
193
|
+
raw = sys.stdin.read()
|
|
194
|
+
try:
|
|
195
|
+
data = json.loads(raw) if raw.strip() else {}
|
|
196
|
+
except json.JSONDecodeError:
|
|
197
|
+
print(json.dumps({"error": "Invalid JSON input"}))
|
|
198
|
+
sys.exit(1)
|
|
199
|
+
|
|
200
|
+
task = data.get("task", "")
|
|
201
|
+
|
|
202
|
+
# --- Your orchestration logic here ---
|
|
203
|
+
# The AgentClient reads ORCHAGENT_SERVICE_KEY from the environment automatically.
|
|
204
|
+
# Do NOT add ORCHAGENT_SERVICE_KEY to required_secrets — the gateway injects it.
|
|
205
|
+
client = AgentClient()
|
|
206
|
+
|
|
207
|
+
# Call a dependency agent (must be listed in manifest.dependencies)
|
|
208
|
+
result = asyncio.run(
|
|
209
|
+
client.call("org/agent-name@v1", {"input": task})
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# You can chain multiple calls, run them in parallel, or add conditional logic:
|
|
213
|
+
#
|
|
214
|
+
# Sequential:
|
|
215
|
+
# result2 = asyncio.run(client.call("org/another-agent@v1", {"input": result}))
|
|
216
|
+
#
|
|
217
|
+
# Parallel:
|
|
218
|
+
# r1, r2 = asyncio.run(asyncio.gather(
|
|
219
|
+
# client.call("org/agent-a@v1", {"input": task}),
|
|
220
|
+
# client.call("org/agent-b@v1", {"input": task}),
|
|
221
|
+
# ))
|
|
222
|
+
# --- End orchestration logic ---
|
|
223
|
+
|
|
224
|
+
# Write JSON output to stdout
|
|
225
|
+
print(json.dumps({"result": result, "success": True}))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
main()
|
|
230
|
+
`;
|
|
231
|
+
const ORCHESTRATOR_REQUIREMENTS = `orchagent-sdk>=0.1.0
|
|
232
|
+
`;
|
|
160
233
|
const SKILL_TEMPLATE = `---
|
|
161
234
|
name: my-skill
|
|
162
235
|
description: When to use this skill
|
|
@@ -189,6 +262,7 @@ function registerInitCommand(program) {
|
|
|
189
262
|
.description('Initialize a new agent project')
|
|
190
263
|
.argument('[name]', 'Agent name (default: current directory name)')
|
|
191
264
|
.option('--type <type>', 'Type: prompt, tool, agent, or skill (legacy aliases: agentic, code)', 'prompt')
|
|
265
|
+
.option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
|
|
192
266
|
.option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
|
|
193
267
|
.action(async (name, options) => {
|
|
194
268
|
const cwd = process.cwd();
|
|
@@ -196,7 +270,13 @@ function registerInitCommand(program) {
|
|
|
196
270
|
if (!['on_demand', 'always_on'].includes(runMode)) {
|
|
197
271
|
throw new errors_1.CliError("Invalid --run-mode. Use 'on_demand' or 'always_on'.");
|
|
198
272
|
}
|
|
199
|
-
|
|
273
|
+
let initMode = resolveInitFlavor(options.type);
|
|
274
|
+
if (options.orchestrator) {
|
|
275
|
+
if (initMode.type === 'skill') {
|
|
276
|
+
throw new errors_1.CliError('Cannot use --orchestrator with --type skill. Orchestrators are agent-type agents that call other agents.');
|
|
277
|
+
}
|
|
278
|
+
initMode = { type: 'agent', flavor: 'orchestrator' };
|
|
279
|
+
}
|
|
200
280
|
// When a name is provided, create a subdirectory for the project
|
|
201
281
|
const targetDir = name ? path_1.default.join(cwd, name) : cwd;
|
|
202
282
|
const agentName = name || path_1.default.basename(cwd);
|
|
@@ -247,7 +327,7 @@ function registerInitCommand(program) {
|
|
|
247
327
|
throw err;
|
|
248
328
|
}
|
|
249
329
|
}
|
|
250
|
-
if (initMode.flavor !== 'code_runtime' && runMode === 'always_on') {
|
|
330
|
+
if (initMode.flavor !== 'code_runtime' && initMode.flavor !== 'orchestrator' && runMode === 'always_on') {
|
|
251
331
|
throw new errors_1.CliError("run_mode=always_on requires runtime.command in orchagent.json (e.g. \"runtime\": { \"command\": \"python main.py\" }). Use --type tool for code-runtime agents.");
|
|
252
332
|
}
|
|
253
333
|
// Create manifest and type-specific files
|
|
@@ -255,7 +335,19 @@ function registerInitCommand(program) {
|
|
|
255
335
|
manifest.name = agentName;
|
|
256
336
|
manifest.type = initMode.type;
|
|
257
337
|
manifest.run_mode = runMode;
|
|
258
|
-
if (initMode.flavor === '
|
|
338
|
+
if (initMode.flavor === 'orchestrator') {
|
|
339
|
+
manifest.description = 'An orchestrator agent that coordinates other agents';
|
|
340
|
+
manifest.runtime = { command: 'python main.py' };
|
|
341
|
+
manifest.manifest = {
|
|
342
|
+
manifest_version: 1,
|
|
343
|
+
dependencies: [{ id: 'org/agent-name', version: 'v1' }],
|
|
344
|
+
max_hops: 3,
|
|
345
|
+
timeout_ms: 120000,
|
|
346
|
+
per_call_downstream_cap: 50,
|
|
347
|
+
};
|
|
348
|
+
manifest.required_secrets = [];
|
|
349
|
+
}
|
|
350
|
+
else if (initMode.flavor === 'managed_loop') {
|
|
259
351
|
manifest.description = 'An AI agent with tool use';
|
|
260
352
|
manifest.supported_providers = ['anthropic'];
|
|
261
353
|
manifest.loop = { max_turns: 25 };
|
|
@@ -267,7 +359,14 @@ function registerInitCommand(program) {
|
|
|
267
359
|
manifest.required_secrets = [];
|
|
268
360
|
}
|
|
269
361
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
270
|
-
if (initMode.flavor === '
|
|
362
|
+
if (initMode.flavor === 'orchestrator') {
|
|
363
|
+
const entrypointPath = path_1.default.join(targetDir, 'main.py');
|
|
364
|
+
const requirementsPath = path_1.default.join(targetDir, 'requirements.txt');
|
|
365
|
+
await promises_1.default.writeFile(entrypointPath, ORCHESTRATOR_MAIN_PY);
|
|
366
|
+
await promises_1.default.writeFile(requirementsPath, ORCHESTRATOR_REQUIREMENTS);
|
|
367
|
+
await promises_1.default.writeFile(schemaPath, AGENT_SCHEMA_TEMPLATE);
|
|
368
|
+
}
|
|
369
|
+
else if (initMode.flavor === 'code_runtime') {
|
|
271
370
|
const entrypointPath = path_1.default.join(targetDir, 'main.py');
|
|
272
371
|
await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
|
|
273
372
|
await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
|
|
@@ -286,19 +385,32 @@ function registerInitCommand(program) {
|
|
|
286
385
|
process.stdout.write(`Initialized agent "${agentName}" in ${targetDir}\n`);
|
|
287
386
|
process.stdout.write(`\nFiles created:\n`);
|
|
288
387
|
const prefix = name ? name + '/' : '';
|
|
289
|
-
process.stdout.write(` ${prefix}orchagent.json
|
|
290
|
-
if (initMode.flavor === '
|
|
291
|
-
process.stdout.write(` ${prefix}main.py
|
|
388
|
+
process.stdout.write(` ${prefix}orchagent.json - Agent configuration\n`);
|
|
389
|
+
if (initMode.flavor === 'orchestrator') {
|
|
390
|
+
process.stdout.write(` ${prefix}main.py - Orchestrator entrypoint (SDK calls)\n`);
|
|
391
|
+
process.stdout.write(` ${prefix}requirements.txt - Python dependencies (orchagent-sdk)\n`);
|
|
392
|
+
}
|
|
393
|
+
else if (initMode.flavor === 'code_runtime') {
|
|
394
|
+
process.stdout.write(` ${prefix}main.py - Agent entrypoint (stdin/stdout JSON)\n`);
|
|
292
395
|
}
|
|
293
396
|
else {
|
|
294
|
-
process.stdout.write(` ${prefix}prompt.md
|
|
397
|
+
process.stdout.write(` ${prefix}prompt.md - Prompt template\n`);
|
|
295
398
|
}
|
|
296
|
-
process.stdout.write(` ${prefix}schema.json
|
|
297
|
-
process.stdout.write(` ${prefix}README.md
|
|
399
|
+
process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
|
|
400
|
+
process.stdout.write(` ${prefix}README.md - Agent documentation\n`);
|
|
298
401
|
process.stdout.write(` Run mode: ${runMode}\n`);
|
|
299
|
-
process.stdout.write(` Execution: ${initMode.flavor}\n`);
|
|
402
|
+
process.stdout.write(` Execution: ${initMode.flavor === 'orchestrator' ? 'code_runtime (orchestrator)' : initMode.flavor}\n`);
|
|
300
403
|
process.stdout.write(`\nNext steps:\n`);
|
|
301
|
-
if (initMode.flavor === '
|
|
404
|
+
if (initMode.flavor === 'orchestrator') {
|
|
405
|
+
const stepNum = name ? 2 : 1;
|
|
406
|
+
if (name) {
|
|
407
|
+
process.stdout.write(` 1. cd ${name}\n`);
|
|
408
|
+
}
|
|
409
|
+
process.stdout.write(` ${stepNum}. Update manifest.dependencies in orchagent.json with your actual agents\n`);
|
|
410
|
+
process.stdout.write(` ${stepNum + 1}. Edit main.py with your orchestration logic\n`);
|
|
411
|
+
process.stdout.write(` ${stepNum + 2}. Publish dependency agents first, then: orchagent publish\n`);
|
|
412
|
+
}
|
|
413
|
+
else if (initMode.flavor === 'code_runtime') {
|
|
302
414
|
const stepNum = name ? 2 : 1;
|
|
303
415
|
if (name) {
|
|
304
416
|
process.stdout.write(` 1. cd ${name}\n`);
|
package/dist/commands/publish.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.extractTemplateVariables = extractTemplateVariables;
|
|
7
7
|
exports.deriveInputSchema = deriveInputSchema;
|
|
8
8
|
exports.scanUndeclaredEnvVars = scanUndeclaredEnvVars;
|
|
9
|
+
exports.checkDependencies = checkDependencies;
|
|
9
10
|
exports.registerPublishCommand = registerPublishCommand;
|
|
10
11
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
@@ -15,7 +16,6 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
15
16
|
const config_1 = require("../lib/config");
|
|
16
17
|
const api_1 = require("../lib/api");
|
|
17
18
|
const errors_1 = require("../lib/errors");
|
|
18
|
-
const api_2 = require("../lib/api");
|
|
19
19
|
const analytics_1 = require("../lib/analytics");
|
|
20
20
|
const bundle_1 = require("../lib/bundle");
|
|
21
21
|
/**
|
|
@@ -270,6 +270,56 @@ function commandForEntrypoint(entrypoint) {
|
|
|
270
270
|
}
|
|
271
271
|
return `python ${entrypoint}`;
|
|
272
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
|
+
}
|
|
273
323
|
function registerPublishCommand(program) {
|
|
274
324
|
program
|
|
275
325
|
.command('publish')
|
|
@@ -287,18 +337,33 @@ function registerPublishCommand(program) {
|
|
|
287
337
|
: undefined;
|
|
288
338
|
const config = await (0, config_1.getResolvedConfig)({}, options.profile);
|
|
289
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
|
+
}
|
|
290
352
|
// Check for SKILL.md first (skills take precedence)
|
|
291
353
|
const skillMdPath = path_1.default.join(cwd, 'SKILL.md');
|
|
292
354
|
const skillData = await parseSkillMd(skillMdPath);
|
|
293
355
|
if (skillData) {
|
|
294
356
|
// Publish as a skill (server auto-assigns version)
|
|
295
|
-
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
|
+
}
|
|
296
361
|
// SC-05: Collect all files in the skill directory for multi-file support
|
|
297
362
|
const skillFiles = await collectSkillFiles(cwd);
|
|
298
363
|
const hasMultipleFiles = skillFiles.length > 1;
|
|
299
364
|
// Handle dry-run for skills
|
|
300
365
|
if (options.dryRun) {
|
|
301
|
-
const preview = await (0, api_1.previewAgentVersion)(config, skillData.frontmatter.name);
|
|
366
|
+
const preview = await (0, api_1.previewAgentVersion)(config, skillData.frontmatter.name, workspaceId);
|
|
302
367
|
const skillBodyBytes = Buffer.byteLength(skillData.body, 'utf-8');
|
|
303
368
|
const totalFilesSize = skillFiles.reduce((sum, f) => sum + f.size, 0);
|
|
304
369
|
const versionInfo = preview.existing_versions.length > 0
|
|
@@ -315,6 +380,9 @@ function registerPublishCommand(program) {
|
|
|
315
380
|
process.stderr.write('\nSkill Preview:\n');
|
|
316
381
|
process.stderr.write(` Name: ${skillData.frontmatter.name}\n`);
|
|
317
382
|
process.stderr.write(` Type: skill\n`);
|
|
383
|
+
if (workspaceId) {
|
|
384
|
+
process.stderr.write(` Workspace: ${org.slug}\n`);
|
|
385
|
+
}
|
|
318
386
|
process.stderr.write(` Version: ${versionInfo}\n`);
|
|
319
387
|
process.stderr.write(` Visibility: private\n`);
|
|
320
388
|
process.stderr.write(` Providers: any\n`);
|
|
@@ -345,7 +413,7 @@ function registerPublishCommand(program) {
|
|
|
345
413
|
// SC-05: Include all skill files for UI preview
|
|
346
414
|
skill_files: hasMultipleFiles ? skillFiles : undefined,
|
|
347
415
|
allow_local_download: options.localDownload || false,
|
|
348
|
-
});
|
|
416
|
+
}, workspaceId);
|
|
349
417
|
const skillVersion = skillResult.agent?.version || 'v1';
|
|
350
418
|
const skillAgentId = skillResult.agent?.id;
|
|
351
419
|
await (0, analytics_1.track)('cli_publish', { agent_type: 'skill', multi_file: hasMultipleFiles });
|
|
@@ -574,8 +642,11 @@ function registerPublishCommand(program) {
|
|
|
574
642
|
if (options.docker && executionEngine !== 'code_runtime') {
|
|
575
643
|
throw new errors_1.CliError('--docker is only supported for code runtime agents');
|
|
576
644
|
}
|
|
577
|
-
// Get org info
|
|
578
|
-
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
|
+
}
|
|
579
650
|
// Default to 'any' provider if not specified
|
|
580
651
|
const supportedProviders = manifest.supported_providers || ['any'];
|
|
581
652
|
// Detect SDK compatibility for code runtime agents
|
|
@@ -586,9 +657,33 @@ function registerPublishCommand(program) {
|
|
|
586
657
|
process.stdout.write(`SDK detected - agent will be marked as Local Ready\n`);
|
|
587
658
|
}
|
|
588
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
|
+
}
|
|
589
684
|
// Handle dry-run for agents
|
|
590
685
|
if (options.dryRun) {
|
|
591
|
-
const preview = await (0, api_1.previewAgentVersion)(config, manifest.name);
|
|
686
|
+
const preview = await (0, api_1.previewAgentVersion)(config, manifest.name, workspaceId);
|
|
592
687
|
const versionInfo = preview.existing_versions.length > 0
|
|
593
688
|
? `${preview.next_version} (new version, ${preview.existing_versions[preview.existing_versions.length - 1]} exists)`
|
|
594
689
|
: `${preview.next_version} (first version)`;
|
|
@@ -641,6 +736,9 @@ function registerPublishCommand(program) {
|
|
|
641
736
|
process.stderr.write('\nAgent Preview:\n');
|
|
642
737
|
process.stderr.write(` Name: ${manifest.name}\n`);
|
|
643
738
|
process.stderr.write(` Type: ${canonicalType}\n`);
|
|
739
|
+
if (workspaceId) {
|
|
740
|
+
process.stderr.write(` Workspace: ${org.slug}\n`);
|
|
741
|
+
}
|
|
644
742
|
process.stderr.write(` Run mode: ${runMode}\n`);
|
|
645
743
|
process.stderr.write(` Engine: ${executionEngine}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
|
|
646
744
|
process.stderr.write(` Callable: ${callable ? 'enabled' : 'disabled'}\n`);
|
|
@@ -719,11 +817,11 @@ function registerPublishCommand(program) {
|
|
|
719
817
|
default_skills: skillsFromFlag || manifest.default_skills,
|
|
720
818
|
skills_locked: manifest.skills_locked || options.skillsLocked || undefined,
|
|
721
819
|
allow_local_download: options.localDownload || false,
|
|
722
|
-
});
|
|
820
|
+
}, workspaceId);
|
|
723
821
|
}
|
|
724
822
|
catch (err) {
|
|
725
823
|
// Improve SECURITY_BLOCKED error display
|
|
726
|
-
if (err instanceof
|
|
824
|
+
if (err instanceof api_1.ApiError && err.status === 422) {
|
|
727
825
|
const payload = err.payload;
|
|
728
826
|
const errorCode = payload?.error?.code;
|
|
729
827
|
if (errorCode === 'SECURITY_BLOCKED') {
|
|
@@ -855,6 +953,22 @@ function registerPublishCommand(program) {
|
|
|
855
953
|
process.stdout.write(`\nService key (save this - shown only once):\n`);
|
|
856
954
|
process.stdout.write(` ${result.service_key}\n`);
|
|
857
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
|
+
}
|
|
858
972
|
process.stdout.write(`\nAPI endpoint:\n`);
|
|
859
973
|
process.stdout.write(` POST ${config.apiUrl}/${org.slug}/${manifest.name}/${assignedVersion}/run\n`);
|
|
860
974
|
if (shouldUploadBundle) {
|
package/dist/lib/api.js
CHANGED
|
@@ -275,8 +275,11 @@ async function publicRequest(config, path) {
|
|
|
275
275
|
}
|
|
276
276
|
return (await response.json());
|
|
277
277
|
}
|
|
278
|
-
async function getOrg(config) {
|
|
279
|
-
|
|
278
|
+
async function getOrg(config, workspaceId) {
|
|
279
|
+
const headers = {};
|
|
280
|
+
if (workspaceId)
|
|
281
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
282
|
+
return request(config, 'GET', '/org', { headers });
|
|
280
283
|
}
|
|
281
284
|
async function updateOrg(config, payload) {
|
|
282
285
|
return request(config, 'PATCH', '/org', {
|
|
@@ -290,10 +293,13 @@ async function getPublicAgent(config, org, agent, version) {
|
|
|
290
293
|
async function listMyAgents(config) {
|
|
291
294
|
return request(config, 'GET', '/agents');
|
|
292
295
|
}
|
|
293
|
-
async function createAgent(config, data) {
|
|
296
|
+
async function createAgent(config, data, workspaceId) {
|
|
297
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
298
|
+
if (workspaceId)
|
|
299
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
294
300
|
return request(config, 'POST', '/agents', {
|
|
295
301
|
body: JSON.stringify(data),
|
|
296
|
-
headers
|
|
302
|
+
headers,
|
|
297
303
|
});
|
|
298
304
|
}
|
|
299
305
|
async function fetchLlmKeys(config) {
|
|
@@ -446,8 +452,11 @@ async function transferAgent(config, agentId, data) {
|
|
|
446
452
|
/**
|
|
447
453
|
* Preview the next version number for an agent.
|
|
448
454
|
*/
|
|
449
|
-
async function previewAgentVersion(config, agentName) {
|
|
450
|
-
|
|
455
|
+
async function previewAgentVersion(config, agentName, workspaceId) {
|
|
456
|
+
const headers = {};
|
|
457
|
+
if (workspaceId)
|
|
458
|
+
headers['X-Workspace-Id'] = workspaceId;
|
|
459
|
+
return request(config, 'GET', `/agents/preview?name=${encodeURIComponent(agentName)}`, { headers });
|
|
451
460
|
}
|
|
452
461
|
/**
|
|
453
462
|
* Report a skill installation to the backend.
|
package/package.json
CHANGED