@orchagent/cli 0.3.100 → 0.3.102
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 +5 -9
- package/dist/commands/diff.js +4 -2
- package/dist/commands/docs.js +6 -1
- package/dist/commands/fork.js +2 -1
- package/dist/commands/init.js +28 -25
- package/dist/commands/install.js +18 -30
- package/dist/commands/logs.js +3 -1
- package/dist/commands/publish.js +117 -4
- package/dist/commands/pull.js +27 -39
- package/dist/commands/replay.js +40 -7
- package/dist/commands/run.js +60 -96
- package/dist/commands/schedule.js +39 -5
- package/dist/commands/security.js +9 -25
- package/dist/commands/templates/cron-job.js +1 -1
- package/dist/commands/templates/github-weekly-summary.js +1 -1
- package/dist/commands/test.js +2 -2
- package/dist/commands/transfer.js +15 -5
- package/dist/commands/tree.js +15 -3
- package/dist/commands/validate.js +9 -0
- package/dist/lib/bundle.js +7 -1
- package/dist/lib/llm.js +37 -1
- package/dist/lib/resolve-agent.js +62 -0
- package/package.json +1 -1
|
@@ -9,18 +9,14 @@ const config_1 = require("../lib/config");
|
|
|
9
9
|
const api_1 = require("../lib/api");
|
|
10
10
|
const errors_1 = require("../lib/errors");
|
|
11
11
|
const key_store_1 = require("../lib/key-store");
|
|
12
|
+
const resolve_agent_1 = require("../lib/resolve-agent");
|
|
12
13
|
/**
|
|
13
14
|
* Resolve an agent reference ("org/agent" or just "agent") to an agent ID.
|
|
14
|
-
* Uses the
|
|
15
|
+
* Uses the shared resolveAgentContext() for parsing and org/workspace resolution,
|
|
16
|
+
* then finds the latest version via the authenticated list-agents endpoint.
|
|
15
17
|
*/
|
|
16
18
|
async function resolveAgentId(config, ref) {
|
|
17
|
-
const
|
|
18
|
-
const agentName = parts.length >= 2 ? parts[1] : parts[0];
|
|
19
|
-
const orgSlug = parts.length >= 2 ? parts[0] : undefined;
|
|
20
|
-
// Resolve workspace context from org slug or config
|
|
21
|
-
const configFile = await (0, config_1.loadConfig)();
|
|
22
|
-
const resolvedOrg = orgSlug ?? configFile.workspace ?? config.defaultOrg;
|
|
23
|
-
const workspaceId = resolvedOrg ? await (0, api_1.resolveWorkspaceIdForOrg)(config, resolvedOrg) : undefined;
|
|
19
|
+
const { org, agent: agentName, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(ref, config);
|
|
24
20
|
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
25
21
|
const matching = agents.filter(a => a.name === agentName);
|
|
26
22
|
if (matching.length === 0) {
|
|
@@ -28,7 +24,7 @@ async function resolveAgentId(config, ref) {
|
|
|
28
24
|
}
|
|
29
25
|
// Use the latest version
|
|
30
26
|
const latest = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
31
|
-
return { agent: latest, agentId: latest.id, orgSlug: latest.org_slug ??
|
|
27
|
+
return { agent: latest, agentId: latest.id, orgSlug: latest.org_slug ?? org, workspaceId };
|
|
32
28
|
}
|
|
33
29
|
function registerAgentKeysCommand(program) {
|
|
34
30
|
const agentKeys = program
|
package/dist/commands/diff.js
CHANGED
|
@@ -246,8 +246,10 @@ function parseSecondRef(value, firstOrg, firstName) {
|
|
|
246
246
|
const parsed = (0, agent_ref_1.parseAgentRef)(value);
|
|
247
247
|
return { org: parsed.org ?? firstOrg, agent: parsed.agent, version: parsed.version };
|
|
248
248
|
}
|
|
249
|
-
// Otherwise treat as a version shorthand for the same agent
|
|
250
|
-
|
|
249
|
+
// Otherwise treat as a version shorthand for the same agent.
|
|
250
|
+
// Strip leading '@' — users naturally type `@v2` since the full form is `org/agent@v2`.
|
|
251
|
+
const version = value.startsWith('@') ? value.slice(1) : value;
|
|
252
|
+
return { org: firstOrg, agent: firstName, version };
|
|
251
253
|
}
|
|
252
254
|
// ── Command ────────────────────────────────────────────────────
|
|
253
255
|
function registerDiffCommand(program) {
|
package/dist/commands/docs.js
CHANGED
|
@@ -10,16 +10,21 @@ const DOCS_ROUTES = {
|
|
|
10
10
|
'': '/',
|
|
11
11
|
'cli': '/using-agents/cli-commands',
|
|
12
12
|
'agents': '/building-agents/agent-types',
|
|
13
|
+
'orchestration': '/building-agents/orchestration',
|
|
13
14
|
'skills': '/building-agents/orchestration',
|
|
14
15
|
'sdk': '/building-agents/sdk',
|
|
15
16
|
'api': '/api-reference/overview',
|
|
16
17
|
'quickstart': '/quickstart',
|
|
18
|
+
'scheduling': '/using-agents/scheduling',
|
|
19
|
+
'services': '/using-agents/services',
|
|
20
|
+
'security': '/concepts/security',
|
|
21
|
+
'billing': '/concepts/billing',
|
|
17
22
|
};
|
|
18
23
|
function registerDocsCommand(program) {
|
|
19
24
|
program
|
|
20
25
|
.command('docs')
|
|
21
26
|
.description('Open documentation in browser')
|
|
22
|
-
.argument('[topic]', 'Topic: cli, agents, skills, sdk, api, quickstart')
|
|
27
|
+
.argument('[topic]', 'Topic: cli, agents, orchestration, skills, sdk, api, quickstart, scheduling, services, security, billing')
|
|
23
28
|
.action(async (topic) => {
|
|
24
29
|
if (topic && !(topic in DOCS_ROUTES)) {
|
|
25
30
|
const validTopics = Object.keys(DOCS_ROUTES).filter(k => k).join(', ');
|
package/dist/commands/fork.js
CHANGED
|
@@ -81,8 +81,9 @@ Examples:
|
|
|
81
81
|
throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
|
|
82
82
|
}
|
|
83
83
|
const { agent, version } = parsed;
|
|
84
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
|
|
84
85
|
write('Resolving source agent...\n');
|
|
85
|
-
const source = await (0, api_1.getAgentWithFallback)(config, org, agent, version);
|
|
86
|
+
const source = await (0, api_1.getAgentWithFallback)(config, org, agent, version, workspaceId);
|
|
86
87
|
if (!source.id) {
|
|
87
88
|
throw new errors_1.CliError(`Could not resolve source agent ID for '${org}/${agent}@${version}'.`);
|
|
88
89
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -58,7 +58,7 @@ Reads JSON input from stdin, processes it, and writes JSON output to stdout.
|
|
|
58
58
|
This is the standard orchagent tool protocol.
|
|
59
59
|
|
|
60
60
|
Usage:
|
|
61
|
-
echo '{"input": "hello"}' |
|
|
61
|
+
echo '{"input": "hello"}' | python3 main.py
|
|
62
62
|
"""
|
|
63
63
|
|
|
64
64
|
import json
|
|
@@ -136,7 +136,7 @@ IMPORTANT: Port 8080 is reserved by the platform health server.
|
|
|
136
136
|
Use a different port (default: 3000).
|
|
137
137
|
|
|
138
138
|
Local development:
|
|
139
|
-
|
|
139
|
+
python3 main.py
|
|
140
140
|
"""
|
|
141
141
|
|
|
142
142
|
import json
|
|
@@ -405,7 +405,7 @@ orchagent fan-out orchestrator.
|
|
|
405
405
|
Calls multiple agents in parallel, combines their results.
|
|
406
406
|
|
|
407
407
|
Usage:
|
|
408
|
-
echo '{"task": "analyze this"}' |
|
|
408
|
+
echo '{"task": "analyze this"}' | python3 main.py
|
|
409
409
|
"""
|
|
410
410
|
|
|
411
411
|
import asyncio
|
|
@@ -497,7 +497,7 @@ orchagent pipeline orchestrator.
|
|
|
497
497
|
Calls agents sequentially — each step's output feeds into the next.
|
|
498
498
|
|
|
499
499
|
Usage:
|
|
500
|
-
echo '{"task": "process this data"}' |
|
|
500
|
+
echo '{"task": "process this data"}' | python3 main.py
|
|
501
501
|
"""
|
|
502
502
|
|
|
503
503
|
import asyncio
|
|
@@ -584,7 +584,7 @@ orchagent map-reduce orchestrator.
|
|
|
584
584
|
Splits input into chunks, processes each in parallel (map), then aggregates (reduce).
|
|
585
585
|
|
|
586
586
|
Usage:
|
|
587
|
-
echo '{"items": ["item1", "item2", "item3"]}' |
|
|
587
|
+
echo '{"items": ["item1", "item2", "item3"]}' | python3 main.py
|
|
588
588
|
"""
|
|
589
589
|
|
|
590
590
|
import asyncio
|
|
@@ -803,7 +803,7 @@ At least one platform token is required.
|
|
|
803
803
|
|
|
804
804
|
\`\`\`sh
|
|
805
805
|
pip install -r requirements.txt
|
|
806
|
-
|
|
806
|
+
python3 main.py
|
|
807
807
|
\`\`\`
|
|
808
808
|
|
|
809
809
|
### 5. Deploy
|
|
@@ -848,7 +848,7 @@ cp .env.example .env
|
|
|
848
848
|
# Fill in DISCORD_BOT_TOKEN, ANTHROPIC_API_KEY, DISCORD_CHANNEL_IDS
|
|
849
849
|
|
|
850
850
|
pip install -r requirements.txt
|
|
851
|
-
|
|
851
|
+
python3 main.py
|
|
852
852
|
\`\`\`
|
|
853
853
|
|
|
854
854
|
### 4. Deploy
|
|
@@ -1068,7 +1068,7 @@ Reads JSON input from stdin, processes the task, and writes JSON output to stdou
|
|
|
1068
1068
|
This is a code-runtime agent — you control the logic and can call any LLM provider.
|
|
1069
1069
|
|
|
1070
1070
|
Usage:
|
|
1071
|
-
echo '{"task": "summarize this text"}' |
|
|
1071
|
+
echo '{"task": "summarize this text"}' | python3 main.py
|
|
1072
1072
|
"""
|
|
1073
1073
|
|
|
1074
1074
|
import json
|
|
@@ -1203,7 +1203,7 @@ Reads JSON input from stdin, calls dependency agents via the orchagent SDK,
|
|
|
1203
1203
|
and writes JSON output to stdout.
|
|
1204
1204
|
|
|
1205
1205
|
Usage:
|
|
1206
|
-
echo '{"task": "do something"}' |
|
|
1206
|
+
echo '{"task": "do something"}' | python3 main.py
|
|
1207
1207
|
"""
|
|
1208
1208
|
|
|
1209
1209
|
import asyncio
|
|
@@ -1328,7 +1328,7 @@ Listens for messages in configured channels and responds using the Anthropic API
|
|
|
1328
1328
|
Local development:
|
|
1329
1329
|
1. Copy .env.example to .env and fill in your tokens
|
|
1330
1330
|
2. pip install -r requirements.txt
|
|
1331
|
-
3.
|
|
1331
|
+
3. python3 main.py
|
|
1332
1332
|
"""
|
|
1333
1333
|
|
|
1334
1334
|
import asyncio
|
|
@@ -1494,7 +1494,10 @@ function resolveInitFlavor(typeOption) {
|
|
|
1494
1494
|
if (normalized === 'prompt') {
|
|
1495
1495
|
return { type: 'prompt', flavor: 'direct_llm' };
|
|
1496
1496
|
}
|
|
1497
|
-
if (normalized === 'agent'
|
|
1497
|
+
if (normalized === 'agent') {
|
|
1498
|
+
return { type: 'agent', flavor: 'managed_loop' };
|
|
1499
|
+
}
|
|
1500
|
+
if (normalized === 'agentic') {
|
|
1498
1501
|
return { type: 'agent', flavor: 'code_runtime' };
|
|
1499
1502
|
}
|
|
1500
1503
|
if (normalized === 'tool' || normalized === 'code') {
|
|
@@ -1511,7 +1514,7 @@ function registerInitCommand(program) {
|
|
|
1511
1514
|
.option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
|
|
1512
1515
|
.option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
|
|
1513
1516
|
.option('--language <lang>', 'Language: python or javascript (default: python)', 'python')
|
|
1514
|
-
.option('--loop', 'Use platform-managed LLM loop
|
|
1517
|
+
.option('--loop', 'Use platform-managed LLM loop execution (explicit for --type agentic; default for --type agent)')
|
|
1515
1518
|
.option('--template <name>', 'Start from a template (use --list-templates to see options)')
|
|
1516
1519
|
.option('--list-templates', 'Show available templates with descriptions')
|
|
1517
1520
|
.action(async (name, options) => {
|
|
@@ -1617,7 +1620,7 @@ function registerInitCommand(program) {
|
|
|
1617
1620
|
const isJavaScript = ['javascript', 'js', 'typescript', 'ts'].includes(language);
|
|
1618
1621
|
// Block unsupported JS flavors
|
|
1619
1622
|
if (isJavaScript && initMode.flavor === 'managed_loop') {
|
|
1620
|
-
throw new errors_1.CliError('JavaScript is not supported for
|
|
1623
|
+
throw new errors_1.CliError('JavaScript is not supported for managed-loop agents. Use --type agentic for a code-runtime agent scaffold.');
|
|
1621
1624
|
}
|
|
1622
1625
|
// JS orchestrators are now supported via the orchagent-sdk npm package
|
|
1623
1626
|
// Block --language for types that don't create runtime files
|
|
@@ -1683,7 +1686,7 @@ function registerInitCommand(program) {
|
|
|
1683
1686
|
type: 'agent',
|
|
1684
1687
|
description: 'Multi-platform support agent powered by Claude. Connects to Discord, Telegram, and/or Slack.',
|
|
1685
1688
|
run_mode: 'always_on',
|
|
1686
|
-
runtime: { command: '
|
|
1689
|
+
runtime: { command: 'python3 main.py' },
|
|
1687
1690
|
entrypoint: 'main.py',
|
|
1688
1691
|
supported_providers: ['anthropic'],
|
|
1689
1692
|
default_models: { anthropic: 'claude-sonnet-4-5-20250929' },
|
|
@@ -1731,7 +1734,7 @@ function registerInitCommand(program) {
|
|
|
1731
1734
|
process.stdout.write(` ${s}. Edit config.py with your product name and description\n`);
|
|
1732
1735
|
process.stdout.write(` ${s + 1}. Replace knowledge/ files with your own docs\n`);
|
|
1733
1736
|
process.stdout.write(` ${s + 2}. Copy .env.example to .env and add platform tokens\n`);
|
|
1734
|
-
process.stdout.write(` ${s + 3}. Test locally: pip install -r requirements.txt &&
|
|
1737
|
+
process.stdout.write(` ${s + 3}. Test locally: pip install -r requirements.txt && python3 main.py\n`);
|
|
1735
1738
|
process.stdout.write(` ${s + 4}. Deploy: orch publish && orch service deploy\n`);
|
|
1736
1739
|
process.stdout.write(AGENT_BUILDER_HINT);
|
|
1737
1740
|
return;
|
|
@@ -1901,7 +1904,7 @@ function registerInitCommand(program) {
|
|
|
1901
1904
|
type: 'agent',
|
|
1902
1905
|
description: `A ${templateLabel} orchestrator agent`,
|
|
1903
1906
|
run_mode: runMode,
|
|
1904
|
-
runtime: { command: isJavaScript ? 'node main.js' : '
|
|
1907
|
+
runtime: { command: isJavaScript ? 'node main.js' : 'python3 main.py' },
|
|
1905
1908
|
manifest: {
|
|
1906
1909
|
manifest_version: 1,
|
|
1907
1910
|
dependencies,
|
|
@@ -1974,7 +1977,7 @@ function registerInitCommand(program) {
|
|
|
1974
1977
|
type: 'tool',
|
|
1975
1978
|
description: 'A scheduled job that runs on a cron schedule',
|
|
1976
1979
|
run_mode: 'on_demand',
|
|
1977
|
-
runtime: { command: isJavaScript ? 'node main.js' : '
|
|
1980
|
+
runtime: { command: isJavaScript ? 'node main.js' : 'python3 main.py' },
|
|
1978
1981
|
required_secrets: [],
|
|
1979
1982
|
tags: ['scheduled', 'cron'],
|
|
1980
1983
|
};
|
|
@@ -2015,7 +2018,7 @@ function registerInitCommand(program) {
|
|
|
2015
2018
|
process.stdout.write(` 1. cd ${name}\n`);
|
|
2016
2019
|
}
|
|
2017
2020
|
const mainFile = isJavaScript ? 'main.js' : 'main.py';
|
|
2018
|
-
const testCmd = isJavaScript ? 'node main.js' : '
|
|
2021
|
+
const testCmd = isJavaScript ? 'node main.js' : 'python3 main.py';
|
|
2019
2022
|
process.stdout.write(` ${stepNum}. Edit ${mainFile} with your job logic\n`);
|
|
2020
2023
|
process.stdout.write(` ${stepNum + 1}. Test: echo '{}' | ${testCmd}\n`);
|
|
2021
2024
|
process.stdout.write(` ${stepNum + 2}. Publish: orch publish\n`);
|
|
@@ -2037,7 +2040,7 @@ function registerInitCommand(program) {
|
|
|
2037
2040
|
}
|
|
2038
2041
|
}
|
|
2039
2042
|
if (initMode.flavor !== 'code_runtime' && initMode.flavor !== 'orchestrator' && initMode.flavor !== 'discord' && runMode === 'always_on') {
|
|
2040
|
-
throw new errors_1.CliError("run_mode=always_on requires runtime.command in orchagent.json (e.g. \"runtime\": { \"command\": \"
|
|
2043
|
+
throw new errors_1.CliError("run_mode=always_on requires runtime.command in orchagent.json (e.g. \"runtime\": { \"command\": \"python3 main.py\" }). Use --type tool or --type agentic for code-runtime agents.");
|
|
2041
2044
|
}
|
|
2042
2045
|
// Create manifest and type-specific files
|
|
2043
2046
|
const manifest = JSON.parse(MANIFEST_TEMPLATE);
|
|
@@ -2051,7 +2054,7 @@ function registerInitCommand(program) {
|
|
|
2051
2054
|
manifest.entrypoint = 'main.js';
|
|
2052
2055
|
}
|
|
2053
2056
|
else {
|
|
2054
|
-
manifest.runtime = { command: '
|
|
2057
|
+
manifest.runtime = { command: 'python3 main.py' };
|
|
2055
2058
|
}
|
|
2056
2059
|
manifest.manifest = {
|
|
2057
2060
|
manifest_version: 1,
|
|
@@ -2070,7 +2073,7 @@ function registerInitCommand(program) {
|
|
|
2070
2073
|
}
|
|
2071
2074
|
else if (initMode.flavor === 'discord') {
|
|
2072
2075
|
manifest.description = 'An always-on Discord bot powered by Claude';
|
|
2073
|
-
manifest.runtime = { command: '
|
|
2076
|
+
manifest.runtime = { command: 'python3 main.py' };
|
|
2074
2077
|
manifest.supported_providers = ['anthropic'];
|
|
2075
2078
|
manifest.required_secrets = ['ANTHROPIC_API_KEY', 'DISCORD_BOT_TOKEN', 'DISCORD_CHANNEL_IDS'];
|
|
2076
2079
|
manifest.tags = ['discord', 'always-on'];
|
|
@@ -2082,7 +2085,7 @@ function registerInitCommand(program) {
|
|
|
2082
2085
|
manifest.entrypoint = 'main.js';
|
|
2083
2086
|
}
|
|
2084
2087
|
else {
|
|
2085
|
-
manifest.runtime = { command: '
|
|
2088
|
+
manifest.runtime = { command: 'python3 main.py' };
|
|
2086
2089
|
}
|
|
2087
2090
|
manifest.required_secrets = [];
|
|
2088
2091
|
}
|
|
@@ -2199,7 +2202,7 @@ function registerInitCommand(program) {
|
|
|
2199
2202
|
process.stdout.write(` ${stepNum}. Create a Discord bot at https://discord.com/developers/applications\n`);
|
|
2200
2203
|
process.stdout.write(` ${stepNum + 1}. Enable Message Content Intent in bot settings\n`);
|
|
2201
2204
|
process.stdout.write(` ${stepNum + 2}. Copy .env.example to .env and fill in your tokens\n`);
|
|
2202
|
-
process.stdout.write(` ${stepNum + 3}. Test locally: pip install -r requirements.txt &&
|
|
2205
|
+
process.stdout.write(` ${stepNum + 3}. Test locally: pip install -r requirements.txt && python3 main.py\n`);
|
|
2203
2206
|
process.stdout.write(` ${stepNum + 4}. Deploy: orch publish\n`);
|
|
2204
2207
|
}
|
|
2205
2208
|
else if (initMode.flavor === 'code_runtime') {
|
|
@@ -2209,7 +2212,7 @@ function registerInitCommand(program) {
|
|
|
2209
2212
|
}
|
|
2210
2213
|
if (runMode === 'always_on') {
|
|
2211
2214
|
const mainFile = isJavaScript ? 'main.js' : 'main.py';
|
|
2212
|
-
const testCmd = isJavaScript ? 'node main.js' : '
|
|
2215
|
+
const testCmd = isJavaScript ? 'node main.js' : 'python3 main.py';
|
|
2213
2216
|
process.stdout.write(` ${stepNum}. Edit ${mainFile} with your service logic\n`);
|
|
2214
2217
|
process.stdout.write(` ${stepNum + 1}. Test locally: ${testCmd}\n`);
|
|
2215
2218
|
process.stdout.write(` ${stepNum + 2}. Publish: orch publish\n`);
|
|
@@ -2226,7 +2229,7 @@ function registerInitCommand(program) {
|
|
|
2226
2229
|
const inputField = initMode.type === 'agent' ? 'task' : 'input';
|
|
2227
2230
|
process.stdout.write(` ${stepNum}. Edit main.py with your agent logic\n`);
|
|
2228
2231
|
process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
|
|
2229
|
-
process.stdout.write(` ${stepNum + 2}. Test: echo '{"${inputField}": "test"}' |
|
|
2232
|
+
process.stdout.write(` ${stepNum + 2}. Test: echo '{"${inputField}": "test"}' | python3 main.py\n`);
|
|
2230
2233
|
process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
|
|
2231
2234
|
}
|
|
2232
2235
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -10,24 +10,12 @@ const os_1 = __importDefault(require("os"));
|
|
|
10
10
|
const config_1 = require("../lib/config");
|
|
11
11
|
const api_1 = require("../lib/api");
|
|
12
12
|
const errors_1 = require("../lib/errors");
|
|
13
|
+
const resolve_agent_1 = require("../lib/resolve-agent");
|
|
13
14
|
const analytics_1 = require("../lib/analytics");
|
|
14
15
|
const adapters_1 = require("../adapters");
|
|
15
16
|
const skill_resolve_1 = require("../lib/skill-resolve");
|
|
16
17
|
const installed_1 = require("../lib/installed");
|
|
17
18
|
const agents_md_utils_1 = require("../lib/agents-md-utils");
|
|
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 { name: segments[0], version };
|
|
25
|
-
}
|
|
26
|
-
if (segments.length === 2) {
|
|
27
|
-
return { org: segments[0], name: segments[1], version };
|
|
28
|
-
}
|
|
29
|
-
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
30
|
-
}
|
|
31
19
|
async function downloadAgentWithFallback(config, org, name, version, workspaceId) {
|
|
32
20
|
// Fetch public metadata first to check if paid
|
|
33
21
|
let publicMeta;
|
|
@@ -135,19 +123,21 @@ function registerInstallCommand(program) {
|
|
|
135
123
|
errors: [],
|
|
136
124
|
};
|
|
137
125
|
const resolved = await (0, config_1.getResolvedConfig)();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
let agentCtx;
|
|
127
|
+
try {
|
|
128
|
+
agentCtx = await (0, resolve_agent_1.resolveAgentContext)(agentArg, resolved);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (jsonMode && err instanceof errors_1.CliError) {
|
|
132
|
+
result.errors.push(err.message);
|
|
144
133
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
145
134
|
process.exit(errors_1.ExitCodes.INVALID_INPUT);
|
|
146
135
|
}
|
|
147
|
-
throw
|
|
136
|
+
throw err;
|
|
148
137
|
}
|
|
149
|
-
|
|
150
|
-
result.
|
|
138
|
+
const { org, agent: agentName, version, workspaceId } = agentCtx;
|
|
139
|
+
result.agent = `${org}/${agentName}`;
|
|
140
|
+
result.version = version;
|
|
151
141
|
// Determine target formats
|
|
152
142
|
let targetFormats = [];
|
|
153
143
|
if (options.format) {
|
|
@@ -186,13 +176,11 @@ function registerInstallCommand(program) {
|
|
|
186
176
|
throw new errors_1.CliError(errMsg);
|
|
187
177
|
}
|
|
188
178
|
result.scope = scope;
|
|
189
|
-
// Resolve workspace context for the target org
|
|
190
|
-
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
|
|
191
179
|
// Download agent
|
|
192
|
-
log(`Fetching ${org}/${
|
|
180
|
+
log(`Fetching ${org}/${agentName}@${version}...\n`);
|
|
193
181
|
let agent;
|
|
194
182
|
try {
|
|
195
|
-
agent = await downloadAgentWithFallback(resolved, org,
|
|
183
|
+
agent = await downloadAgentWithFallback(resolved, org, agentName, version, workspaceId);
|
|
196
184
|
}
|
|
197
185
|
catch (err) {
|
|
198
186
|
if (jsonMode) {
|
|
@@ -273,15 +261,15 @@ function registerInstallCommand(program) {
|
|
|
273
261
|
catch {
|
|
274
262
|
// File doesn't exist, will create new
|
|
275
263
|
}
|
|
276
|
-
const agentRef = `${org}/${
|
|
264
|
+
const agentRef = `${org}/${agentName}`;
|
|
277
265
|
file.content = (0, agents_md_utils_1.mergeAgentsMdContent)(existingContent, file.content, agentRef);
|
|
278
266
|
}
|
|
279
267
|
await promises_1.default.writeFile(fullPath, file.content);
|
|
280
268
|
filesWritten++;
|
|
281
269
|
// Track installation
|
|
282
270
|
const installedAgent = {
|
|
283
|
-
agent: `${org}/${
|
|
284
|
-
version:
|
|
271
|
+
agent: `${org}/${agentName}`,
|
|
272
|
+
version: version,
|
|
285
273
|
format: formatId,
|
|
286
274
|
scope: effectiveScope,
|
|
287
275
|
path: fullPath,
|
|
@@ -297,7 +285,7 @@ function registerInstallCommand(program) {
|
|
|
297
285
|
if (!options.dryRun) {
|
|
298
286
|
if (filesWritten > 0) {
|
|
299
287
|
await (0, analytics_1.track)('cli_agent_install', {
|
|
300
|
-
agent: `${org}/${
|
|
288
|
+
agent: `${org}/${agentName}`,
|
|
301
289
|
formats: targetFormats,
|
|
302
290
|
scope,
|
|
303
291
|
});
|
package/dist/commands/logs.js
CHANGED
|
@@ -182,7 +182,7 @@ async function listRuns(config, workspaceId, agentName, options) {
|
|
|
182
182
|
if (result.total > result.runs.length) {
|
|
183
183
|
process.stdout.write(chalk_1.default.gray(`\nShowing ${result.runs.length} of ${result.total} runs. Use --limit to see more.\n`));
|
|
184
184
|
}
|
|
185
|
-
process.stdout.write(chalk_1.default.gray('\nView detailed logs for a run: orch logs <run-id>\n'));
|
|
185
|
+
process.stdout.write(chalk_1.default.gray('\nView detailed logs for a run: orch logs <run-id> · Replay a run: orch replay <run-id>\n'));
|
|
186
186
|
}
|
|
187
187
|
// ============================================
|
|
188
188
|
// SHOW RUN LOGS
|
|
@@ -229,5 +229,7 @@ async function showRunLogs(config, workspaceId, runId, json) {
|
|
|
229
229
|
process.stdout.write(chalk_1.default.gray('\nNo sandbox output available for this run. ' +
|
|
230
230
|
'Execution logs are captured for agents with a code runtime (tool/agent types with runtime.command).\n'));
|
|
231
231
|
}
|
|
232
|
+
// Footer hints
|
|
233
|
+
process.stdout.write(chalk_1.default.gray(`\nReplay: orch replay ${runId.slice(0, 8)} · Trace: orch trace ${runId.slice(0, 8)}\n`));
|
|
232
234
|
process.stdout.write('\n');
|
|
233
235
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -42,6 +42,7 @@ exports.scanUndeclaredEnvVars = scanUndeclaredEnvVars;
|
|
|
42
42
|
exports.scanReservedPort = scanReservedPort;
|
|
43
43
|
exports.detectSdkCompatible = detectSdkCompatible;
|
|
44
44
|
exports.checkDependencies = checkDependencies;
|
|
45
|
+
exports.checkWorkspaceLlmKeys = checkWorkspaceLlmKeys;
|
|
45
46
|
exports.batchPublish = batchPublish;
|
|
46
47
|
exports.registerPublishCommand = registerPublishCommand;
|
|
47
48
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
@@ -394,7 +395,7 @@ function commandForEntrypoint(entrypoint) {
|
|
|
394
395
|
if (entrypoint.endsWith('.js') || entrypoint.endsWith('.mjs') || entrypoint.endsWith('.cjs') || entrypoint.endsWith('.ts')) {
|
|
395
396
|
return `node ${entrypoint}`;
|
|
396
397
|
}
|
|
397
|
-
return `
|
|
398
|
+
return `python3 ${entrypoint}`;
|
|
398
399
|
}
|
|
399
400
|
/**
|
|
400
401
|
* Check if manifest dependencies are published and callable.
|
|
@@ -439,13 +440,71 @@ async function checkDependencies(config, dependencies, publishingOrgSlug, worksp
|
|
|
439
440
|
}
|
|
440
441
|
catch (err) {
|
|
441
442
|
if (err?.status === 404) {
|
|
442
|
-
|
|
443
|
+
// Could be unpublished OR published-but-private — we can't tell from a 404
|
|
444
|
+
return { ref, status: 'not_found_cross_org' };
|
|
443
445
|
}
|
|
444
446
|
// Network/unexpected error — don't false alarm
|
|
445
447
|
return { ref, status: 'found_callable' };
|
|
446
448
|
}
|
|
447
449
|
}));
|
|
448
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Provider names the platform supports for LLM vault keys.
|
|
453
|
+
* Must stay in sync with gateway's _PROVIDER_TO_SECRET_NAME (db.py).
|
|
454
|
+
*/
|
|
455
|
+
const PROVIDER_TO_SECRET_NAME = {
|
|
456
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
457
|
+
openai: 'OPENAI_API_KEY',
|
|
458
|
+
gemini: 'GEMINI_API_KEY',
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* After a successful publish, check whether the target workspace has LLM vault
|
|
462
|
+
* keys that match the agent's supported_providers. If not, print a warning so
|
|
463
|
+
* the user knows cloud runs will fail.
|
|
464
|
+
*
|
|
465
|
+
* Best-effort: API errors are silently swallowed (the agent is already published).
|
|
466
|
+
*/
|
|
467
|
+
async function checkWorkspaceLlmKeys(config, workspaceId, workspaceSlug, executionEngine, supportedProviders) {
|
|
468
|
+
// Only direct_llm and managed_loop engines need LLM keys
|
|
469
|
+
if (executionEngine !== 'direct_llm' && executionEngine !== 'managed_loop') {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
let secrets;
|
|
473
|
+
try {
|
|
474
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/secrets`);
|
|
475
|
+
secrets = result.secrets;
|
|
476
|
+
if (!Array.isArray(secrets))
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
return; // Can't reach API or unexpected response — skip warning silently
|
|
481
|
+
}
|
|
482
|
+
const llmKeys = secrets.filter(s => s.secret_type === 'llm_key' && s.llm_provider);
|
|
483
|
+
const availableProviders = new Set(llmKeys.map(s => s.llm_provider));
|
|
484
|
+
// Determine which providers the agent needs
|
|
485
|
+
const needsAny = supportedProviders.includes('any');
|
|
486
|
+
if (needsAny) {
|
|
487
|
+
// 'any' means any single provider works
|
|
488
|
+
if (availableProviders.size > 0)
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
// Check if at least one of the supported providers has a key
|
|
493
|
+
const hasMatch = supportedProviders.some(p => availableProviders.has(p));
|
|
494
|
+
if (hasMatch)
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
// Build the warning message
|
|
498
|
+
const providerList = needsAny
|
|
499
|
+
? Object.keys(PROVIDER_TO_SECRET_NAME).join(', ')
|
|
500
|
+
: supportedProviders.join(', ');
|
|
501
|
+
const exampleSecretName = needsAny
|
|
502
|
+
? 'ANTHROPIC_API_KEY'
|
|
503
|
+
: (PROVIDER_TO_SECRET_NAME[supportedProviders[0]] || `${supportedProviders[0].toUpperCase()}_API_KEY`);
|
|
504
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ No LLM vault keys found in workspace '${workspaceSlug}' for providers: ${providerList}\n`) +
|
|
505
|
+
` Cloud runs will fail until you add keys.\n` +
|
|
506
|
+
` Add a key: ${chalk_1.default.cyan(`orch secrets set ${exampleSecretName} <key>`)}\n`);
|
|
507
|
+
}
|
|
449
508
|
/**
|
|
450
509
|
* Batch publish all agents found in subdirectories, in dependency order.
|
|
451
510
|
* Discovers orchagent.json/SKILL.md in immediate subdirectories,
|
|
@@ -510,6 +569,8 @@ async function batchPublish(rootDir, options) {
|
|
|
510
569
|
forwardArgs.push('--no-local-download');
|
|
511
570
|
if (options.requiredSecrets === false)
|
|
512
571
|
forwardArgs.push('--no-required-secrets');
|
|
572
|
+
if (options.verbose)
|
|
573
|
+
forwardArgs.push('--verbose');
|
|
513
574
|
const results = [];
|
|
514
575
|
for (let i = 0; i < sorted.length; i++) {
|
|
515
576
|
const agent = sorted[i];
|
|
@@ -586,6 +647,7 @@ function registerPublishCommand(program) {
|
|
|
586
647
|
.option('--no-local-download', 'Prevent users from downloading and running locally (default: allowed)')
|
|
587
648
|
.option('--no-required-secrets', '(deprecated) No longer needed — required_secrets defaults to []')
|
|
588
649
|
.option('--all', 'Publish all agents in subdirectories (dependency order)')
|
|
650
|
+
.option('--verbose', 'Show detailed bundle contents (file list)')
|
|
589
651
|
.action(async (options) => {
|
|
590
652
|
const cwd = process.cwd();
|
|
591
653
|
// --all: batch publish all agents in subdirectories
|
|
@@ -696,6 +758,11 @@ function registerPublishCommand(program) {
|
|
|
696
758
|
process.stdout.write(`\n${chalk_1.default.green('✔')} Published ${org.slug}/${skillData.frontmatter.name}@${skillVersion} successfully!\n\n`);
|
|
697
759
|
if (hasMultipleFiles) {
|
|
698
760
|
process.stdout.write(`Files: ${skillFiles.length} files included\n`);
|
|
761
|
+
if (options.verbose) {
|
|
762
|
+
for (const f of skillFiles) {
|
|
763
|
+
process.stdout.write(` ${f.path}\n`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
699
766
|
}
|
|
700
767
|
process.stdout.write(`Visibility: private\n`);
|
|
701
768
|
process.stdout.write(`\nView analytics and usage: https://orchagent.io/dashboard\n`);
|
|
@@ -971,6 +1038,7 @@ function registerPublishCommand(program) {
|
|
|
971
1038
|
if (manifestDeps?.length) {
|
|
972
1039
|
const depResults = await checkDependencies(config, manifestDeps, org.slug, workspaceId);
|
|
973
1040
|
const notFound = depResults.filter(r => r.status === 'not_found');
|
|
1041
|
+
const notFoundCrossOrg = depResults.filter(r => r.status === 'not_found_cross_org');
|
|
974
1042
|
const notCallable = depResults.filter(r => r.status === 'found_not_callable');
|
|
975
1043
|
if (notFound.length > 0) {
|
|
976
1044
|
process.stderr.write(chalk_1.default.yellow(`\n⚠ Unpublished dependencies:\n`));
|
|
@@ -980,6 +1048,14 @@ function registerPublishCommand(program) {
|
|
|
980
1048
|
process.stderr.write(`\n These agents must be published before this orchestrator can call them.\n` +
|
|
981
1049
|
` Publish each dependency first, then re-run this publish.\n\n`);
|
|
982
1050
|
}
|
|
1051
|
+
if (notFoundCrossOrg.length > 0) {
|
|
1052
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ Dependencies not found (unpublished or not accessible from this workspace):\n`));
|
|
1053
|
+
for (const dep of notFoundCrossOrg) {
|
|
1054
|
+
process.stderr.write(chalk_1.default.yellow(` - ${dep.ref}\n`));
|
|
1055
|
+
}
|
|
1056
|
+
process.stderr.write(`\n If the dependency is published in another workspace, ensure it's in the same\n` +
|
|
1057
|
+
` workspace as this orchestrator, or use agent access grants.\n\n`);
|
|
1058
|
+
}
|
|
983
1059
|
if (notCallable.length > 0) {
|
|
984
1060
|
process.stderr.write(chalk_1.default.yellow(`\n⚠ Dependencies have callable: false:\n`));
|
|
985
1061
|
for (const dep of notCallable) {
|
|
@@ -990,6 +1066,23 @@ function registerPublishCommand(program) {
|
|
|
990
1066
|
` the field to use the default) and republish each dependency.\n\n`);
|
|
991
1067
|
}
|
|
992
1068
|
}
|
|
1069
|
+
// UX-13-02: Warn when managed-loop orchestrator has dependencies but no custom_tools.
|
|
1070
|
+
// Without custom_tools, the LLM has no way to call its declared dependencies.
|
|
1071
|
+
if (executionEngine === 'managed_loop' && manifestDeps?.length) {
|
|
1072
|
+
const mergedTools = Array.isArray(loopConfig?.custom_tools) ? loopConfig.custom_tools : [];
|
|
1073
|
+
if (mergedTools.length === 0) {
|
|
1074
|
+
process.stderr.write(chalk_1.default.yellow(`\n⚠ This managed-loop agent declares dependencies but no custom_tools.\n` +
|
|
1075
|
+
` Without custom_tools, the LLM cannot call dependencies at runtime —\n` +
|
|
1076
|
+
` it will waste turns exploring the filesystem instead.\n\n` +
|
|
1077
|
+
` Use 'orch scaffold orchestration' to auto-generate the correct\n` +
|
|
1078
|
+
` custom_tools configuration, or add them manually to your loop config:\n\n` +
|
|
1079
|
+
` "loop": {\n` +
|
|
1080
|
+
` "custom_tools": [\n` +
|
|
1081
|
+
` { "name": "call_worker", "description": "...", "command": "python3 /home/user/helpers/orch_call.py org/worker@v1" }\n` +
|
|
1082
|
+
` ]\n` +
|
|
1083
|
+
` }\n\n`));
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
993
1086
|
// UX-2: Default required_secrets to [] when omitted for tool/agent types.
|
|
994
1087
|
// Prompt and skill types are exempt (prompt agents get LLM keys from platform,
|
|
995
1088
|
// skills don't run standalone).
|
|
@@ -1061,6 +1154,12 @@ function registerPublishCommand(program) {
|
|
|
1061
1154
|
process.stderr.write(` Files: ${bundlePreview.fileCount} files\n`);
|
|
1062
1155
|
process.stderr.write(` Size: ${(bundlePreview.totalSizeBytes / 1024).toFixed(1)} KB\n`);
|
|
1063
1156
|
process.stderr.write(` Entrypoint: ${bundlePreview.entrypoint}\n`);
|
|
1157
|
+
if (options.verbose && bundlePreview.files?.length) {
|
|
1158
|
+
process.stderr.write(` Bundled files:\n`);
|
|
1159
|
+
for (const f of bundlePreview.files) {
|
|
1160
|
+
process.stderr.write(` ${f}\n`);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1064
1163
|
}
|
|
1065
1164
|
process.stderr.write('\nAgent Preview:\n');
|
|
1066
1165
|
process.stderr.write(` Name: ${manifest.name}\n`);
|
|
@@ -1324,6 +1423,12 @@ function registerPublishCommand(program) {
|
|
|
1324
1423
|
skipEntrypointCheck: false,
|
|
1325
1424
|
});
|
|
1326
1425
|
process.stdout.write(` Created bundle: ${bundleResult.fileCount} files, ${(bundleResult.sizeBytes / 1024).toFixed(1)}KB\n`);
|
|
1426
|
+
if (options.verbose && bundleResult.files?.length) {
|
|
1427
|
+
process.stdout.write(` Bundled files:\n`);
|
|
1428
|
+
for (const f of bundleResult.files) {
|
|
1429
|
+
process.stdout.write(` ${f}\n`);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1327
1432
|
// Validate bundle size
|
|
1328
1433
|
const validation = await (0, bundle_1.validateBundle)(bundlePath);
|
|
1329
1434
|
if (!validation.valid) {
|
|
@@ -1389,6 +1494,8 @@ function registerPublishCommand(program) {
|
|
|
1389
1494
|
process.stderr.write(chalk_1.default.yellow(`⚠ ${warning}\n`));
|
|
1390
1495
|
}
|
|
1391
1496
|
}
|
|
1497
|
+
// Warn if workspace has no LLM vault keys for this agent's providers (UX-13-01)
|
|
1498
|
+
await checkWorkspaceLlmKeys(config, workspaceId || org.id, org.slug, executionEngine, supportedProviders);
|
|
1392
1499
|
// Show required secrets with setup instructions (F-18)
|
|
1393
1500
|
if (manifest.required_secrets?.length) {
|
|
1394
1501
|
process.stdout.write(`\nRequired secrets:\n`);
|
|
@@ -1404,14 +1511,20 @@ function registerPublishCommand(program) {
|
|
|
1404
1511
|
// Show security review result if available
|
|
1405
1512
|
const secReview = result.security_review;
|
|
1406
1513
|
if (secReview?.verdict) {
|
|
1407
|
-
if (secReview.verdict === '
|
|
1514
|
+
if (secReview.verdict === 'approved') {
|
|
1408
1515
|
process.stdout.write(`Security: ${chalk_1.default.green('passed')}\n`);
|
|
1409
1516
|
}
|
|
1410
1517
|
else if (secReview.verdict === 'flagged') {
|
|
1411
1518
|
process.stdout.write(`Security: ${chalk_1.default.yellow('flagged')} — ${secReview.summary || 'review recommended'}\n`);
|
|
1412
1519
|
}
|
|
1520
|
+
else if (secReview.verdict === 'error') {
|
|
1521
|
+
process.stdout.write(`Security: ${chalk_1.default.gray('review unavailable')} — publish succeeded, review will be retried\n`);
|
|
1522
|
+
}
|
|
1523
|
+
else if (secReview.verdict === 'skipped') {
|
|
1524
|
+
process.stdout.write(`Security: ${chalk_1.default.gray('review skipped')} — ${secReview.summary || 'content not eligible for review'}\n`);
|
|
1525
|
+
}
|
|
1413
1526
|
else {
|
|
1414
|
-
process.stdout.write(`Security: ${secReview.verdict}\n`);
|
|
1527
|
+
process.stdout.write(`Security: ${chalk_1.default.gray(secReview.verdict)} — ${secReview.summary || ''}\n`);
|
|
1415
1528
|
}
|
|
1416
1529
|
}
|
|
1417
1530
|
if (result.service_key) {
|