@orchagent/cli 0.3.69 → 0.3.71
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 +6 -1
- package/dist/commands/agents.js +5 -1
- package/dist/commands/delete.js +3 -1
- package/dist/commands/info.js +6 -4
- package/dist/commands/init.js +286 -10
- package/dist/commands/install.js +10 -8
- package/dist/commands/publish.js +49 -1
- package/dist/commands/pull.js +11 -9
- package/dist/commands/run.js +70 -24
- package/dist/commands/skill.js +10 -8
- package/dist/lib/api.js +3 -4
- package/dist/lib/bundle.js +1 -1
- package/dist/lib/doctor/checks/llm.js +1 -1
- package/dist/lib/llm.js +4 -64
- package/package.json +1 -1
|
@@ -15,7 +15,12 @@ const errors_1 = require("../lib/errors");
|
|
|
15
15
|
async function resolveAgentId(config, ref) {
|
|
16
16
|
const parts = ref.split('/');
|
|
17
17
|
const agentName = parts.length >= 2 ? parts[1] : parts[0];
|
|
18
|
-
const
|
|
18
|
+
const orgSlug = parts.length >= 2 ? parts[0] : undefined;
|
|
19
|
+
// Resolve workspace context from org slug or config
|
|
20
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
21
|
+
const resolvedOrg = orgSlug ?? configFile.workspace ?? config.defaultOrg;
|
|
22
|
+
const workspaceId = resolvedOrg ? await (0, api_1.resolveWorkspaceIdForOrg)(config, resolvedOrg) : undefined;
|
|
23
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
19
24
|
const matching = agents.filter(a => a.name === agentName);
|
|
20
25
|
if (matching.length === 0) {
|
|
21
26
|
throw new errors_1.CliError(`Agent '${ref}' not found. Run 'orchagent agents' to list your agents.`);
|
package/dist/commands/agents.js
CHANGED
|
@@ -49,7 +49,11 @@ function registerAgentsCommand(program) {
|
|
|
49
49
|
.option('--json', 'Output raw JSON')
|
|
50
50
|
.action(async (options) => {
|
|
51
51
|
const config = await (0, config_1.getResolvedConfig)();
|
|
52
|
-
|
|
52
|
+
// Resolve workspace context
|
|
53
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
54
|
+
const orgSlug = configFile.workspace ?? config.defaultOrg;
|
|
55
|
+
const workspaceId = orgSlug ? await (0, api_1.resolveWorkspaceIdForOrg)(config, orgSlug) : undefined;
|
|
56
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
53
57
|
// Apply filter if provided
|
|
54
58
|
const filteredAgents = options.filter
|
|
55
59
|
? agents.filter(a => a.name.toLowerCase().includes(options.filter.toLowerCase()))
|
package/dist/commands/delete.js
CHANGED
|
@@ -42,9 +42,11 @@ Examples:
|
|
|
42
42
|
if (!config.apiKey) {
|
|
43
43
|
throw new errors_1.CliError('Not logged in. Run `orchagent login` first.');
|
|
44
44
|
}
|
|
45
|
+
// Resolve workspace context for the target org
|
|
46
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, ref.org);
|
|
45
47
|
process.stdout.write('Finding agent...\n');
|
|
46
48
|
// Find the agent by name, filtering by org if provided
|
|
47
|
-
const agents = await (0, api_1.listMyAgents)(config);
|
|
49
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
48
50
|
const matching = agents.filter(a => a.name === ref.agent && (!a.org_slug || a.org_slug === ref.org));
|
|
49
51
|
if (matching.length === 0) {
|
|
50
52
|
throw new errors_1.CliError(`Agent '${ref.org}/${ref.agent}' not found`);
|
package/dist/commands/info.js
CHANGED
|
@@ -49,7 +49,7 @@ async function fetchReadme(url) {
|
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
async function getAgentInfo(config, org, agent, version) {
|
|
52
|
+
async function getAgentInfo(config, org, agent, version, workspaceId) {
|
|
53
53
|
// Use public metadata endpoint as primary source — never blocked by download restrictions
|
|
54
54
|
try {
|
|
55
55
|
const publicMeta = await (0, api_1.getPublicAgent)(config, org, agent, version);
|
|
@@ -78,11 +78,11 @@ async function getAgentInfo(config, org, agent, version) {
|
|
|
78
78
|
if (!config.apiKey) {
|
|
79
79
|
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
80
80
|
}
|
|
81
|
-
const userOrg = await (0, api_1.getOrg)(config);
|
|
81
|
+
const userOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
82
82
|
if (userOrg.slug !== org) {
|
|
83
83
|
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
84
84
|
}
|
|
85
|
-
const agents = await (0, api_1.listMyAgents)(config);
|
|
85
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
86
86
|
const matching = agents.filter(a => a.name === agent);
|
|
87
87
|
if (matching.length === 0) {
|
|
88
88
|
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
@@ -123,8 +123,10 @@ function registerInfoCommand(program) {
|
|
|
123
123
|
.action(async (agentArg, options) => {
|
|
124
124
|
const config = await (0, config_1.getResolvedConfig)();
|
|
125
125
|
const { org, agent, version } = (0, agent_ref_1.parseAgentRef)(agentArg);
|
|
126
|
+
// Resolve workspace context for the target org
|
|
127
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
|
|
126
128
|
// Fetch agent metadata
|
|
127
|
-
const agentData = await getAgentInfo(config, org, agent, version);
|
|
129
|
+
const agentData = await getAgentInfo(config, org, agent, version, workspaceId);
|
|
128
130
|
if (options.json) {
|
|
129
131
|
// Don't expose internal routing URLs in JSON output
|
|
130
132
|
const output = { ...agentData };
|
package/dist/commands/init.js
CHANGED
|
@@ -87,6 +87,185 @@ def main():
|
|
|
87
87
|
if __name__ == "__main__":
|
|
88
88
|
main()
|
|
89
89
|
`;
|
|
90
|
+
const CODE_TEMPLATE_JS = `/**
|
|
91
|
+
* orchagent tool entrypoint.
|
|
92
|
+
*
|
|
93
|
+
* Reads JSON input from stdin, processes it, and writes JSON output to stdout.
|
|
94
|
+
* This is the standard orchagent tool protocol.
|
|
95
|
+
*
|
|
96
|
+
* Usage:
|
|
97
|
+
* echo '{"input": "hello"}' | node main.js
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
const fs = require('fs');
|
|
101
|
+
|
|
102
|
+
function main() {
|
|
103
|
+
const raw = fs.readFileSync('/dev/stdin', 'utf-8');
|
|
104
|
+
let data;
|
|
105
|
+
try {
|
|
106
|
+
data = raw.trim() ? JSON.parse(raw) : {};
|
|
107
|
+
} catch {
|
|
108
|
+
console.log(JSON.stringify({ error: 'Invalid JSON input' }));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const input = data.input || '';
|
|
113
|
+
|
|
114
|
+
// --- Your logic here ---
|
|
115
|
+
// To use workspace secrets, add them to "required_secrets" in orchagent.json:
|
|
116
|
+
// "required_secrets": ["MY_API_KEY"]
|
|
117
|
+
// Then access via: process.env.MY_API_KEY
|
|
118
|
+
const result = \`Received: \${input}\`;
|
|
119
|
+
// --- End your logic ---
|
|
120
|
+
|
|
121
|
+
console.log(JSON.stringify({ result }));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
main();
|
|
125
|
+
`;
|
|
126
|
+
const DISCORD_MAIN_JS = `/**
|
|
127
|
+
* Discord bot agent — powered by Claude.
|
|
128
|
+
*
|
|
129
|
+
* Listens for messages in configured channels and responds using the Anthropic API.
|
|
130
|
+
*
|
|
131
|
+
* Local development:
|
|
132
|
+
* 1. Copy .env.example to .env and fill in your tokens
|
|
133
|
+
* 2. npm install
|
|
134
|
+
* 3. node main.js
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
const { Client, GatewayIntentBits } = require('discord.js');
|
|
138
|
+
const Anthropic = require('@anthropic-ai/sdk');
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Configuration
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN || '';
|
|
145
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || '';
|
|
146
|
+
const DISCORD_CHANNEL_IDS = process.env.DISCORD_CHANNEL_IDS || '';
|
|
147
|
+
|
|
148
|
+
const MODEL = process.env.MODEL || 'claude-sonnet-4-5-20250929';
|
|
149
|
+
const MAX_TOKENS = parseInt(process.env.MAX_TOKENS || '1024', 10);
|
|
150
|
+
|
|
151
|
+
const SYSTEM_PROMPT = \`\\
|
|
152
|
+
You are a helpful assistant in a Discord server.
|
|
153
|
+
|
|
154
|
+
Be concise and friendly. Use code blocks for code examples.
|
|
155
|
+
Keep responses under 1800 characters (Discord limit is 2000).\`;
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Anthropic API
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
async function askClaude(client, userMessage) {
|
|
163
|
+
const response = await client.messages.create({
|
|
164
|
+
model: MODEL,
|
|
165
|
+
max_tokens: MAX_TOKENS,
|
|
166
|
+
system: SYSTEM_PROMPT,
|
|
167
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
168
|
+
});
|
|
169
|
+
return response.content[0].text;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Discord bot
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
function parseChannelIds(raw) {
|
|
178
|
+
return new Set(
|
|
179
|
+
raw.split(',')
|
|
180
|
+
.map(s => s.trim())
|
|
181
|
+
.filter(s => /^\\d+$/.test(s))
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function main() {
|
|
186
|
+
if (!DISCORD_BOT_TOKEN) {
|
|
187
|
+
console.error('DISCORD_BOT_TOKEN not set');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
if (!ANTHROPIC_API_KEY) {
|
|
191
|
+
console.error('ANTHROPIC_API_KEY not set');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
if (!DISCORD_CHANNEL_IDS) {
|
|
195
|
+
console.error('DISCORD_CHANNEL_IDS not set — add comma-separated channel IDs');
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const allowedChannels = parseChannelIds(DISCORD_CHANNEL_IDS);
|
|
200
|
+
if (allowedChannels.size === 0) {
|
|
201
|
+
console.error('No valid channel IDs in DISCORD_CHANNEL_IDS:', DISCORD_CHANNEL_IDS);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(\`Starting bot — model: \${MODEL}, channels: \${[...allowedChannels].join(', ')}\`);
|
|
206
|
+
|
|
207
|
+
const anthropic = new Anthropic({ apiKey: ANTHROPIC_API_KEY });
|
|
208
|
+
|
|
209
|
+
const bot = new Client({
|
|
210
|
+
intents: [
|
|
211
|
+
GatewayIntentBits.Guilds,
|
|
212
|
+
GatewayIntentBits.GuildMessages,
|
|
213
|
+
GatewayIntentBits.MessageContent,
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
bot.on('ready', () => {
|
|
218
|
+
console.log(\`Bot connected as \${bot.user.tag}\`);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
bot.on('messageCreate', async (message) => {
|
|
222
|
+
if (message.author.bot || !message.content.trim()) return;
|
|
223
|
+
|
|
224
|
+
const channelId = message.channelId;
|
|
225
|
+
const parentId = message.channel.parentId;
|
|
226
|
+
if (!allowedChannels.has(channelId) && !allowedChannels.has(parentId)) return;
|
|
227
|
+
|
|
228
|
+
console.log(\`Message from \${message.author.tag}: \${message.content.slice(0, 100)}\`);
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
await message.channel.sendTyping();
|
|
232
|
+
const answer = await askClaude(anthropic, message.content);
|
|
233
|
+
const trimmed = answer.length > 1900 ? answer.slice(0, 1897) + '...' : answer;
|
|
234
|
+
await message.reply(trimmed);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
console.error('Anthropic API error:', err.message || err);
|
|
237
|
+
await message.reply('Sorry, I ran into an issue. Please try again.');
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
bot.login(DISCORD_BOT_TOKEN);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
main();
|
|
245
|
+
`;
|
|
246
|
+
const DISCORD_PACKAGE_JSON = `{
|
|
247
|
+
"name": "discord-bot",
|
|
248
|
+
"private": true,
|
|
249
|
+
"type": "commonjs",
|
|
250
|
+
"dependencies": {
|
|
251
|
+
"discord.js": "^14.16.0",
|
|
252
|
+
"@anthropic-ai/sdk": "^0.30.0"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
`;
|
|
256
|
+
const DISCORD_JS_ENV_EXAMPLE = `# Required — get your bot token from https://discord.com/developers/applications
|
|
257
|
+
DISCORD_BOT_TOKEN=
|
|
258
|
+
|
|
259
|
+
# Required for local dev — auto-injected in production via supported_providers
|
|
260
|
+
ANTHROPIC_API_KEY=
|
|
261
|
+
|
|
262
|
+
# Required — comma-separated Discord channel IDs where the bot should respond
|
|
263
|
+
DISCORD_CHANNEL_IDS=
|
|
264
|
+
|
|
265
|
+
# Optional — customize the model and response length
|
|
266
|
+
# MODEL=claude-sonnet-4-5-20250929
|
|
267
|
+
# MAX_TOKENS=1024
|
|
268
|
+
`;
|
|
90
269
|
function readmeTemplate(agentName, flavor) {
|
|
91
270
|
if (flavor === 'support_agent') {
|
|
92
271
|
return `# ${agentName}
|
|
@@ -525,7 +704,8 @@ function registerInitCommand(program) {
|
|
|
525
704
|
.option('--type <type>', 'Type: prompt, tool, agent, or skill (legacy aliases: agentic, code)', 'prompt')
|
|
526
705
|
.option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
|
|
527
706
|
.option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
|
|
528
|
-
.option('--
|
|
707
|
+
.option('--language <lang>', 'Language: python or javascript (default: python)', 'python')
|
|
708
|
+
.option('--template <name>', 'Start from a template (available: support-agent, discord, discord-js, github-weekly-summary)')
|
|
529
709
|
.action(async (name, options) => {
|
|
530
710
|
const cwd = process.cwd();
|
|
531
711
|
let runMode = (options.runMode || 'on_demand').trim().toLowerCase();
|
|
@@ -539,9 +719,22 @@ function registerInitCommand(program) {
|
|
|
539
719
|
}
|
|
540
720
|
initMode = { type: 'agent', flavor: 'orchestrator' };
|
|
541
721
|
}
|
|
722
|
+
// Validate --language option
|
|
723
|
+
const language = (options.language || 'python').trim().toLowerCase();
|
|
724
|
+
if (!['python', 'javascript', 'js', 'typescript', 'ts'].includes(language)) {
|
|
725
|
+
throw new errors_1.CliError(`Invalid --language '${options.language}'. Use 'python' or 'javascript'.`);
|
|
726
|
+
}
|
|
727
|
+
const isJavaScript = ['javascript', 'js', 'typescript', 'ts'].includes(language);
|
|
728
|
+
// Block unsupported JS flavors
|
|
729
|
+
if (isJavaScript && initMode.flavor === 'managed_loop') {
|
|
730
|
+
throw new errors_1.CliError('JavaScript agent-type agents are not yet supported. Use --type tool for JavaScript agents.');
|
|
731
|
+
}
|
|
732
|
+
if (isJavaScript && options.orchestrator) {
|
|
733
|
+
throw new errors_1.CliError('JavaScript orchestrators are not yet supported. Use Python for orchestrator agents.');
|
|
734
|
+
}
|
|
542
735
|
if (options.template) {
|
|
543
736
|
const template = options.template.trim().toLowerCase();
|
|
544
|
-
const validTemplates = ['support-agent', 'discord', 'github-weekly-summary'];
|
|
737
|
+
const validTemplates = ['support-agent', 'discord', 'discord-js', 'github-weekly-summary'];
|
|
545
738
|
if (!validTemplates.includes(template)) {
|
|
546
739
|
throw new errors_1.CliError(`Unknown --template '${template}'. Available templates: ${validTemplates.join(', ')}`);
|
|
547
740
|
}
|
|
@@ -559,6 +752,10 @@ function registerInitCommand(program) {
|
|
|
559
752
|
initMode = { type: 'agent', flavor: 'discord' };
|
|
560
753
|
runMode = 'always_on';
|
|
561
754
|
}
|
|
755
|
+
else if (template === 'discord-js') {
|
|
756
|
+
initMode = { type: 'agent', flavor: 'discord_js' };
|
|
757
|
+
runMode = 'always_on';
|
|
758
|
+
}
|
|
562
759
|
else if (template === 'github-weekly-summary') {
|
|
563
760
|
initMode = { type: 'agent', flavor: 'github_weekly_summary' };
|
|
564
761
|
}
|
|
@@ -729,6 +926,55 @@ function registerInitCommand(program) {
|
|
|
729
926
|
process.stdout.write(`\n See README.md for full setup guide.\n`);
|
|
730
927
|
return;
|
|
731
928
|
}
|
|
929
|
+
// Handle discord-js template separately (JS Discord bot)
|
|
930
|
+
if (initMode.flavor === 'discord_js') {
|
|
931
|
+
const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
|
|
932
|
+
// Check if already initialized
|
|
933
|
+
try {
|
|
934
|
+
await promises_1.default.access(manifestPath);
|
|
935
|
+
throw new errors_1.CliError(`Already initialized (orchagent.json exists in ${name ? name + '/' : 'current directory'})`);
|
|
936
|
+
}
|
|
937
|
+
catch (err) {
|
|
938
|
+
if (err.code !== 'ENOENT') {
|
|
939
|
+
throw err;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const manifest = {
|
|
943
|
+
name: agentName,
|
|
944
|
+
type: 'agent',
|
|
945
|
+
description: 'An always-on Discord bot powered by Claude (JavaScript)',
|
|
946
|
+
run_mode: 'always_on',
|
|
947
|
+
runtime: { command: 'node main.js' },
|
|
948
|
+
entrypoint: 'main.js',
|
|
949
|
+
supported_providers: ['anthropic'],
|
|
950
|
+
required_secrets: ['DISCORD_BOT_TOKEN', 'DISCORD_CHANNEL_IDS'],
|
|
951
|
+
tags: ['discord', 'always-on', 'javascript'],
|
|
952
|
+
};
|
|
953
|
+
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
954
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), DISCORD_MAIN_JS);
|
|
955
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'package.json'), DISCORD_PACKAGE_JSON);
|
|
956
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, '.env.example'), DISCORD_JS_ENV_EXAMPLE);
|
|
957
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'README.md'), readmeTemplate(agentName, 'discord'));
|
|
958
|
+
const prefix = name ? name + '/' : '';
|
|
959
|
+
process.stdout.write(`\nInitialized JS Discord bot "${agentName}" in ${targetDir}\n`);
|
|
960
|
+
process.stdout.write(`\nFiles created:\n`);
|
|
961
|
+
process.stdout.write(` ${prefix}orchagent.json - Agent configuration\n`);
|
|
962
|
+
process.stdout.write(` ${prefix}main.js - Discord bot (discord.js + Anthropic)\n`);
|
|
963
|
+
process.stdout.write(` ${prefix}package.json - npm dependencies\n`);
|
|
964
|
+
process.stdout.write(` ${prefix}.env.example - Environment variables template\n`);
|
|
965
|
+
process.stdout.write(` ${prefix}README.md - Setup guide\n`);
|
|
966
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
967
|
+
const stepNum = name ? 2 : 1;
|
|
968
|
+
if (name) {
|
|
969
|
+
process.stdout.write(` 1. cd ${name}\n`);
|
|
970
|
+
}
|
|
971
|
+
process.stdout.write(` ${stepNum}. Create a Discord bot at https://discord.com/developers/applications\n`);
|
|
972
|
+
process.stdout.write(` ${stepNum + 1}. Enable Message Content Intent in bot settings\n`);
|
|
973
|
+
process.stdout.write(` ${stepNum + 2}. Copy .env.example to .env and fill in your tokens\n`);
|
|
974
|
+
process.stdout.write(` ${stepNum + 3}. Test locally: npm install && node main.js\n`);
|
|
975
|
+
process.stdout.write(` ${stepNum + 4}. Deploy: orch publish\n`);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
732
978
|
const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
|
|
733
979
|
const promptPath = path_1.default.join(targetDir, 'prompt.md');
|
|
734
980
|
const schemaPath = path_1.default.join(targetDir, 'schema.json');
|
|
@@ -777,7 +1023,13 @@ function registerInitCommand(program) {
|
|
|
777
1023
|
}
|
|
778
1024
|
else if (initMode.flavor === 'code_runtime') {
|
|
779
1025
|
manifest.description = 'A code-runtime agent';
|
|
780
|
-
|
|
1026
|
+
if (isJavaScript) {
|
|
1027
|
+
manifest.runtime = { command: 'node main.js' };
|
|
1028
|
+
manifest.entrypoint = 'main.js';
|
|
1029
|
+
}
|
|
1030
|
+
else {
|
|
1031
|
+
manifest.runtime = { command: 'python main.py' };
|
|
1032
|
+
}
|
|
781
1033
|
manifest.required_secrets = [];
|
|
782
1034
|
}
|
|
783
1035
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
@@ -797,8 +1049,18 @@ function registerInitCommand(program) {
|
|
|
797
1049
|
await promises_1.default.writeFile(envExamplePath, DISCORD_ENV_EXAMPLE);
|
|
798
1050
|
}
|
|
799
1051
|
else if (initMode.flavor === 'code_runtime') {
|
|
800
|
-
|
|
801
|
-
|
|
1052
|
+
if (isJavaScript) {
|
|
1053
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), CODE_TEMPLATE_JS);
|
|
1054
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'package.json'), JSON.stringify({
|
|
1055
|
+
name: agentName,
|
|
1056
|
+
private: true,
|
|
1057
|
+
type: 'commonjs',
|
|
1058
|
+
dependencies: {},
|
|
1059
|
+
}, null, 2) + '\n');
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), CODE_TEMPLATE_PY);
|
|
1063
|
+
}
|
|
802
1064
|
await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
|
|
803
1065
|
}
|
|
804
1066
|
else if (initMode.flavor === 'managed_loop') {
|
|
@@ -826,7 +1088,13 @@ function registerInitCommand(program) {
|
|
|
826
1088
|
process.stdout.write(` ${prefix}.env.example - Environment variables template\n`);
|
|
827
1089
|
}
|
|
828
1090
|
else if (initMode.flavor === 'code_runtime') {
|
|
829
|
-
|
|
1091
|
+
if (isJavaScript) {
|
|
1092
|
+
process.stdout.write(` ${prefix}main.js - Agent entrypoint (stdin/stdout JSON)\n`);
|
|
1093
|
+
process.stdout.write(` ${prefix}package.json - npm dependencies\n`);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
process.stdout.write(` ${prefix}main.py - Agent entrypoint (stdin/stdout JSON)\n`);
|
|
1097
|
+
}
|
|
830
1098
|
}
|
|
831
1099
|
else {
|
|
832
1100
|
process.stdout.write(` ${prefix}prompt.md - Prompt template\n`);
|
|
@@ -863,10 +1131,18 @@ function registerInitCommand(program) {
|
|
|
863
1131
|
if (name) {
|
|
864
1132
|
process.stdout.write(` 1. cd ${name}\n`);
|
|
865
1133
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1134
|
+
if (isJavaScript) {
|
|
1135
|
+
process.stdout.write(` ${stepNum}. Edit main.js with your agent logic\n`);
|
|
1136
|
+
process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
|
|
1137
|
+
process.stdout.write(` ${stepNum + 2}. Test: echo '{"input": "test"}' | node main.js\n`);
|
|
1138
|
+
process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
process.stdout.write(` ${stepNum}. Edit main.py with your agent logic\n`);
|
|
1142
|
+
process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
|
|
1143
|
+
process.stdout.write(` ${stepNum + 2}. Test: echo '{"input": "test"}' | python main.py\n`);
|
|
1144
|
+
process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
|
|
1145
|
+
}
|
|
870
1146
|
}
|
|
871
1147
|
else {
|
|
872
1148
|
const stepNum = name ? 2 : 1;
|
package/dist/commands/install.js
CHANGED
|
@@ -29,7 +29,7 @@ function parseAgentRef(value) {
|
|
|
29
29
|
}
|
|
30
30
|
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
31
31
|
}
|
|
32
|
-
async function downloadAgentWithFallback(config, org, name, version) {
|
|
32
|
+
async function downloadAgentWithFallback(config, org, name, version, workspaceId) {
|
|
33
33
|
// Fetch public metadata first to check if paid
|
|
34
34
|
let publicMeta;
|
|
35
35
|
try {
|
|
@@ -48,12 +48,12 @@ async function downloadAgentWithFallback(config, org, name, version) {
|
|
|
48
48
|
if (publicMeta && (0, pricing_1.isPaidAgent)(publicMeta)) {
|
|
49
49
|
// Paid agent - check if owner
|
|
50
50
|
if (config.apiKey) {
|
|
51
|
-
const callerOrg = await (0, api_1.getOrg)(config);
|
|
51
|
+
const callerOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
52
52
|
const isOwner = (publicMeta.org_id && callerOrg.id === publicMeta.org_id) ||
|
|
53
53
|
(publicMeta.org_slug && callerOrg.slug === publicMeta.org_slug);
|
|
54
54
|
if (isOwner) {
|
|
55
55
|
// Owner - fetch from authenticated endpoint with full prompt
|
|
56
|
-
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
56
|
+
const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
57
57
|
const matching = myAgents.filter(a => a.name === name);
|
|
58
58
|
if (matching.length > 0) {
|
|
59
59
|
let targetAgent;
|
|
@@ -90,12 +90,12 @@ async function downloadAgentWithFallback(config, org, name, version) {
|
|
|
90
90
|
if (publicMeta && publicMeta.allow_local_download === false) {
|
|
91
91
|
// Check if owner (can bypass)
|
|
92
92
|
if (config.apiKey) {
|
|
93
|
-
const callerOrg = await (0, api_1.getOrg)(config);
|
|
93
|
+
const callerOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
94
94
|
const isOwner = (publicMeta.org_id && callerOrg.id === publicMeta.org_id) ||
|
|
95
95
|
(publicMeta.org_slug && callerOrg.slug === publicMeta.org_slug);
|
|
96
96
|
if (isOwner) {
|
|
97
97
|
// Owner - fetch from authenticated endpoint
|
|
98
|
-
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
98
|
+
const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
99
99
|
const matching = myAgents.filter(a => a.name === name);
|
|
100
100
|
if (matching.length > 0) {
|
|
101
101
|
let targetAgent;
|
|
@@ -128,11 +128,11 @@ async function downloadAgentWithFallback(config, org, name, version) {
|
|
|
128
128
|
if (!config.apiKey) {
|
|
129
129
|
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
130
130
|
}
|
|
131
|
-
const userOrg = await (0, api_1.getOrg)(config);
|
|
131
|
+
const userOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
132
132
|
if (userOrg.slug !== org) {
|
|
133
133
|
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
134
134
|
}
|
|
135
|
-
const agents = await (0, api_1.listMyAgents)(config);
|
|
135
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
136
136
|
const matching = agents.filter(a => a.name === name);
|
|
137
137
|
if (matching.length === 0) {
|
|
138
138
|
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
@@ -232,11 +232,13 @@ Note: Paid agents cannot be installed locally - they run on server only.
|
|
|
232
232
|
throw new errors_1.CliError(errMsg);
|
|
233
233
|
}
|
|
234
234
|
result.scope = scope;
|
|
235
|
+
// Resolve workspace context for the target org
|
|
236
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
|
|
235
237
|
// Download agent
|
|
236
238
|
log(`Fetching ${org}/${parsed.name}@${parsed.version}...\n`);
|
|
237
239
|
let agent;
|
|
238
240
|
try {
|
|
239
|
-
agent = await downloadAgentWithFallback(resolved, org, parsed.name, parsed.version);
|
|
241
|
+
agent = await downloadAgentWithFallback(resolved, org, parsed.name, parsed.version, workspaceId);
|
|
240
242
|
}
|
|
241
243
|
catch (err) {
|
|
242
244
|
if (jsonMode) {
|
package/dist/commands/publish.js
CHANGED
|
@@ -80,7 +80,9 @@ async function scanUndeclaredEnvVars(agentDir, requiredSecrets) {
|
|
|
80
80
|
/os\.getenv\s*\(\s*['"]([A-Z][A-Z0-9_]*)['"]/g,
|
|
81
81
|
];
|
|
82
82
|
const found = new Set();
|
|
83
|
-
//
|
|
83
|
+
// JS/TS env var access pattern
|
|
84
|
+
const jsEnvPattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
85
|
+
// Scan .py/.js/.ts files in the agent directory (up to 2 levels deep)
|
|
84
86
|
async function scanDir(dir, depth) {
|
|
85
87
|
let entries;
|
|
86
88
|
try {
|
|
@@ -112,6 +114,19 @@ async function scanUndeclaredEnvVars(agentDir, requiredSecrets) {
|
|
|
112
114
|
// Skip unreadable files
|
|
113
115
|
}
|
|
114
116
|
}
|
|
117
|
+
else if (entry.isFile() && (name.endsWith('.js') || name.endsWith('.ts'))) {
|
|
118
|
+
try {
|
|
119
|
+
const content = await promises_1.default.readFile(fullPath, 'utf-8');
|
|
120
|
+
jsEnvPattern.lastIndex = 0;
|
|
121
|
+
let m;
|
|
122
|
+
while ((m = jsEnvPattern.exec(content)) !== null) { // eslint-disable-line no-cond-assign
|
|
123
|
+
found.add(m[1]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Skip unreadable files
|
|
128
|
+
}
|
|
129
|
+
}
|
|
115
130
|
}
|
|
116
131
|
}
|
|
117
132
|
await scanDir(agentDir, 0);
|
|
@@ -147,6 +162,19 @@ async function detectSdkCompatible(agentDir) {
|
|
|
147
162
|
catch {
|
|
148
163
|
// File doesn't exist
|
|
149
164
|
}
|
|
165
|
+
// Check package.json (JS/TS agents)
|
|
166
|
+
const pkgJsonPath = path_1.default.join(agentDir, 'package.json');
|
|
167
|
+
try {
|
|
168
|
+
const content = await promises_1.default.readFile(pkgJsonPath, 'utf-8');
|
|
169
|
+
const pkgJson = JSON.parse(content);
|
|
170
|
+
const deps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
|
|
171
|
+
if (deps['orchagent-sdk'] || deps['@orchagent/sdk']) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// File doesn't exist or invalid JSON
|
|
177
|
+
}
|
|
150
178
|
return false;
|
|
151
179
|
}
|
|
152
180
|
async function parseSkillMd(filePath) {
|
|
@@ -886,6 +914,26 @@ function registerPublishCommand(program) {
|
|
|
886
914
|
catch {
|
|
887
915
|
// Optional
|
|
888
916
|
}
|
|
917
|
+
// Include package.json + lockfile for JS agents (overrides DEFAULT_EXCLUDES)
|
|
918
|
+
const pkgPath = path_1.default.join(cwd, 'package.json');
|
|
919
|
+
try {
|
|
920
|
+
await promises_1.default.access(pkgPath);
|
|
921
|
+
includePatterns.push('package.json');
|
|
922
|
+
process.stdout.write(` Including package.json for sandbox dependencies\n`);
|
|
923
|
+
// Include lockfile for deterministic installs (npm ci)
|
|
924
|
+
const lockPath = path_1.default.join(cwd, 'package-lock.json');
|
|
925
|
+
try {
|
|
926
|
+
await promises_1.default.access(lockPath);
|
|
927
|
+
includePatterns.push('package-lock.json');
|
|
928
|
+
process.stdout.write(` Including package-lock.json for deterministic installs\n`);
|
|
929
|
+
}
|
|
930
|
+
catch {
|
|
931
|
+
// No lockfile — npm install will be used instead of npm ci
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
catch {
|
|
935
|
+
// No package.json — not a JS agent
|
|
936
|
+
}
|
|
889
937
|
}
|
|
890
938
|
const bundleResult = await (0, bundle_1.createCodeBundle)(cwd, bundlePath, {
|
|
891
939
|
entrypoint: bundleEntrypoint,
|
package/dist/commands/pull.js
CHANGED
|
@@ -58,7 +58,7 @@ function commandForEntrypoint(entrypoint) {
|
|
|
58
58
|
return `python ${entrypoint}`;
|
|
59
59
|
}
|
|
60
60
|
// ─── Agent Resolution ───────────────────────────────────────────────────────
|
|
61
|
-
async function resolveAgent(config, org, agent, version) {
|
|
61
|
+
async function resolveAgent(config, org, agent, version, workspaceId) {
|
|
62
62
|
// 1. Try public download endpoint
|
|
63
63
|
try {
|
|
64
64
|
const data = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
@@ -96,7 +96,7 @@ async function resolveAgent(config, org, agent, version) {
|
|
|
96
96
|
if (errorCode === 'PAID_AGENT_SERVER_ONLY' || errorCode === 'DOWNLOAD_DISABLED') {
|
|
97
97
|
// Try authenticated owner path
|
|
98
98
|
if (config.apiKey) {
|
|
99
|
-
const ownerData = await tryOwnerFallback(config, org, agent, version);
|
|
99
|
+
const ownerData = await tryOwnerFallback(config, org, agent, version, workspaceId);
|
|
100
100
|
if (ownerData)
|
|
101
101
|
return { ...ownerData, source: 'owner_authenticated' };
|
|
102
102
|
}
|
|
@@ -117,19 +117,19 @@ async function resolveAgent(config, org, agent, version) {
|
|
|
117
117
|
if (!config.apiKey) {
|
|
118
118
|
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
119
119
|
}
|
|
120
|
-
const userOrg = await (0, api_1.getOrg)(config);
|
|
120
|
+
const userOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
121
121
|
if (userOrg.slug !== org) {
|
|
122
122
|
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
123
123
|
}
|
|
124
|
-
const data = await resolveFromMyAgents(config, agent, version, org);
|
|
124
|
+
const data = await resolveFromMyAgents(config, agent, version, org, workspaceId);
|
|
125
125
|
if (!data) {
|
|
126
126
|
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
127
127
|
}
|
|
128
128
|
return { ...data, source: 'private_authenticated' };
|
|
129
129
|
}
|
|
130
|
-
async function tryOwnerFallback(config, org, agent, version) {
|
|
130
|
+
async function tryOwnerFallback(config, org, agent, version, workspaceId) {
|
|
131
131
|
try {
|
|
132
|
-
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
132
|
+
const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
133
133
|
let match;
|
|
134
134
|
if (version === 'latest') {
|
|
135
135
|
match = myAgents
|
|
@@ -148,8 +148,8 @@ async function tryOwnerFallback(config, org, agent, version) {
|
|
|
148
148
|
return null;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
-
async function resolveFromMyAgents(config, agent, version, org) {
|
|
152
|
-
const agents = await (0, api_1.listMyAgents)(config);
|
|
151
|
+
async function resolveFromMyAgents(config, agent, version, org, workspaceId) {
|
|
152
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
153
153
|
const matching = agents.filter(a => a.name === agent && a.org_slug === org);
|
|
154
154
|
if (matching.length === 0)
|
|
155
155
|
return null;
|
|
@@ -361,9 +361,11 @@ Examples:
|
|
|
361
361
|
throw new errors_1.CliError('Missing org. Use org/agent[@version] format, or set a default org with:\n' +
|
|
362
362
|
' orch config set default-org <org>');
|
|
363
363
|
}
|
|
364
|
+
// Resolve workspace context for the target org
|
|
365
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
|
|
364
366
|
write(`Resolving ${org}/${parsed.agent}@${parsed.version}...\n`);
|
|
365
367
|
// Resolve agent data
|
|
366
|
-
const data = await resolveAgent(config, org, parsed.agent, parsed.version);
|
|
368
|
+
const data = await resolveAgent(config, org, parsed.agent, parsed.version, workspaceId);
|
|
367
369
|
// Reject skills
|
|
368
370
|
if (canonicalType(data.type) === 'skill') {
|
|
369
371
|
throw new errors_1.CliError("This is a skill. Use 'orch skill install <ref>' instead.");
|
package/dist/commands/run.js
CHANGED
|
@@ -69,6 +69,16 @@ const DOWNSTREAM_REMAINING_ENV = 'ORCHAGENT_DOWNSTREAM_REMAINING';
|
|
|
69
69
|
const CONTENT_FIELD_NAMES = ['code', 'content', 'text', 'source', 'input', 'file_content', 'body'];
|
|
70
70
|
// Keys that might indicate local file path references in JSON payloads
|
|
71
71
|
const LOCAL_PATH_KEYS = ['path', 'directory', 'file', 'filepath', 'dir', 'folder', 'local'];
|
|
72
|
+
/**
|
|
73
|
+
* Return the correct local command for a given entrypoint file extension.
|
|
74
|
+
* Uses `python3` (not `python`) to match existing behavior on macOS/Linux.
|
|
75
|
+
*/
|
|
76
|
+
function localCommandForEntrypoint(entrypoint) {
|
|
77
|
+
if (entrypoint.endsWith('.js') || entrypoint.endsWith('.mjs') || entrypoint.endsWith('.cjs')) {
|
|
78
|
+
return 'node';
|
|
79
|
+
}
|
|
80
|
+
return 'python3';
|
|
81
|
+
}
|
|
72
82
|
function parseAgentRef(value) {
|
|
73
83
|
const [ref, versionPart] = value.split('@');
|
|
74
84
|
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
@@ -652,25 +662,6 @@ async function detectAllLlmKeys(supportedProviders, config) {
|
|
|
652
662
|
}
|
|
653
663
|
}
|
|
654
664
|
}
|
|
655
|
-
if (config?.apiKey) {
|
|
656
|
-
try {
|
|
657
|
-
const { fetchLlmKeys } = await Promise.resolve().then(() => __importStar(require('../lib/api')));
|
|
658
|
-
const serverKeys = await fetchLlmKeys(config);
|
|
659
|
-
for (const serverKey of serverKeys) {
|
|
660
|
-
if (!seen.has(serverKey.provider)) {
|
|
661
|
-
seen.add(serverKey.provider);
|
|
662
|
-
providers.push({
|
|
663
|
-
provider: serverKey.provider,
|
|
664
|
-
apiKey: serverKey.api_key,
|
|
665
|
-
model: serverKey.model || (0, llm_1.getDefaultModel)(serverKey.provider),
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
catch {
|
|
671
|
-
// Server fetch failed, continue with what we have
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
665
|
return providers;
|
|
675
666
|
}
|
|
676
667
|
async function executePromptLocally(agentData, inputData, skillPrompts = [], config, providerOverride, modelOverride) {
|
|
@@ -1062,6 +1053,32 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
1062
1053
|
throw err;
|
|
1063
1054
|
}
|
|
1064
1055
|
}
|
|
1056
|
+
// Install npm dependencies if package.json exists (JS agents)
|
|
1057
|
+
const pkgJsonPath = path_1.default.join(extractDir, 'package.json');
|
|
1058
|
+
try {
|
|
1059
|
+
await promises_1.default.access(pkgJsonPath);
|
|
1060
|
+
await (0, spinner_1.withSpinner)('Installing npm dependencies...', async () => {
|
|
1061
|
+
const lockfilePath = path_1.default.join(extractDir, 'package-lock.json');
|
|
1062
|
+
let useNpmCi = false;
|
|
1063
|
+
try {
|
|
1064
|
+
await promises_1.default.access(lockfilePath);
|
|
1065
|
+
useNpmCi = true;
|
|
1066
|
+
}
|
|
1067
|
+
catch { /* no lockfile */ }
|
|
1068
|
+
const npmArgs = useNpmCi
|
|
1069
|
+
? ['ci', '--no-audit', '--no-fund']
|
|
1070
|
+
: ['install', '--production', '--no-audit', '--no-fund'];
|
|
1071
|
+
const { code } = await runCommand('npm', npmArgs);
|
|
1072
|
+
if (code !== 0) {
|
|
1073
|
+
throw new errors_1.CliError('Failed to install npm dependencies');
|
|
1074
|
+
}
|
|
1075
|
+
}, { successText: 'npm dependencies installed' });
|
|
1076
|
+
}
|
|
1077
|
+
catch (err) {
|
|
1078
|
+
if (err.code !== 'ENOENT') {
|
|
1079
|
+
throw err;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1065
1082
|
const entrypoint = agentData.entrypoint || 'sandbox_main.py';
|
|
1066
1083
|
const entrypointPath = path_1.default.join(extractDir, entrypoint);
|
|
1067
1084
|
try {
|
|
@@ -1116,7 +1133,8 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
1116
1133
|
}
|
|
1117
1134
|
}
|
|
1118
1135
|
}
|
|
1119
|
-
|
|
1136
|
+
const execCmd = localCommandForEntrypoint(entrypoint);
|
|
1137
|
+
process.stderr.write(`\nRunning: ${execCmd} ${entrypoint}\n\n`);
|
|
1120
1138
|
const subprocessEnv = { ...process.env };
|
|
1121
1139
|
if (config.apiKey) {
|
|
1122
1140
|
subprocessEnv.ORCHAGENT_SERVICE_KEY = config.apiKey;
|
|
@@ -1133,7 +1151,7 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
1133
1151
|
subprocessEnv[MAX_HOPS_ENV] = String(manifest.manifest?.max_hops || 10);
|
|
1134
1152
|
subprocessEnv[DOWNSTREAM_REMAINING_ENV] = String(manifest.manifest?.per_call_downstream_cap || 100);
|
|
1135
1153
|
}
|
|
1136
|
-
const proc = (0, child_process_1.spawn)(
|
|
1154
|
+
const proc = (0, child_process_1.spawn)(execCmd, [entrypointPath], {
|
|
1137
1155
|
cwd: extractDir,
|
|
1138
1156
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1139
1157
|
env: subprocessEnv,
|
|
@@ -1442,7 +1460,11 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1442
1460
|
return;
|
|
1443
1461
|
}
|
|
1444
1462
|
// Code runtime agents with bundle
|
|
1445
|
-
const
|
|
1463
|
+
const runtimeObj = manifest.runtime;
|
|
1464
|
+
const runtimeCommand = runtimeObj?.command;
|
|
1465
|
+
const entrypoint = manifest.entrypoint
|
|
1466
|
+
|| (runtimeCommand ? runtimeCommand.split(' ').pop() || 'sandbox_main.py' : null)
|
|
1467
|
+
|| 'sandbox_main.py';
|
|
1446
1468
|
const entrypointPath = path_1.default.join(resolved, entrypoint);
|
|
1447
1469
|
try {
|
|
1448
1470
|
await promises_1.default.access(entrypointPath);
|
|
@@ -1492,6 +1514,29 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1492
1514
|
catch {
|
|
1493
1515
|
// No requirements.txt
|
|
1494
1516
|
}
|
|
1517
|
+
// Install npm dependencies if package.json exists (JS agents)
|
|
1518
|
+
const localPkgJsonPath = path_1.default.join(resolved, 'package.json');
|
|
1519
|
+
try {
|
|
1520
|
+
await promises_1.default.access(localPkgJsonPath);
|
|
1521
|
+
const lockfilePath = path_1.default.join(resolved, 'package-lock.json');
|
|
1522
|
+
let useNpmCi = false;
|
|
1523
|
+
try {
|
|
1524
|
+
await promises_1.default.access(lockfilePath);
|
|
1525
|
+
useNpmCi = true;
|
|
1526
|
+
}
|
|
1527
|
+
catch { /* no lockfile */ }
|
|
1528
|
+
const npmArgs = useNpmCi
|
|
1529
|
+
? ['ci', '--no-audit', '--no-fund']
|
|
1530
|
+
: ['install', '--production', '--no-audit', '--no-fund'];
|
|
1531
|
+
process.stderr.write('Installing npm dependencies...\n');
|
|
1532
|
+
const { code } = await runCommand('npm', npmArgs);
|
|
1533
|
+
if (code !== 0) {
|
|
1534
|
+
process.stderr.write('Warning: Failed to install npm dependencies\n');
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
catch {
|
|
1538
|
+
// No package.json
|
|
1539
|
+
}
|
|
1495
1540
|
// Check for keyed file/mount injection (tool path)
|
|
1496
1541
|
const toolFileArgs = options.file ?? [];
|
|
1497
1542
|
const toolKeyedFiles = toolFileArgs.filter(a => isKeyedFileArg(a) !== null);
|
|
@@ -1517,13 +1562,14 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1517
1562
|
else if (args.length > 0) {
|
|
1518
1563
|
inputJson = JSON.stringify({ input: args[0] });
|
|
1519
1564
|
}
|
|
1520
|
-
|
|
1565
|
+
const localExecCmd = localCommandForEntrypoint(entrypoint);
|
|
1566
|
+
process.stderr.write(`\nRunning: ${localExecCmd} ${entrypoint}\n\n`);
|
|
1521
1567
|
const subprocessEnv = { ...process.env };
|
|
1522
1568
|
if (config.apiKey) {
|
|
1523
1569
|
subprocessEnv.ORCHAGENT_SERVICE_KEY = config.apiKey;
|
|
1524
1570
|
subprocessEnv.ORCHAGENT_API_URL = config.apiUrl;
|
|
1525
1571
|
}
|
|
1526
|
-
const proc = (0, child_process_1.spawn)(
|
|
1572
|
+
const proc = (0, child_process_1.spawn)(localExecCmd, [entrypointPath], {
|
|
1527
1573
|
cwd: resolved,
|
|
1528
1574
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1529
1575
|
env: subprocessEnv,
|
package/dist/commands/skill.js
CHANGED
|
@@ -88,7 +88,7 @@ function parseSkillRef(value) {
|
|
|
88
88
|
}
|
|
89
89
|
throw new errors_1.CliError('Invalid skill reference. Use org/skill or skill format.');
|
|
90
90
|
}
|
|
91
|
-
async function downloadSkillWithFallback(config, org, skill, version) {
|
|
91
|
+
async function downloadSkillWithFallback(config, org, skill, version, workspaceId) {
|
|
92
92
|
// Fetch metadata first to check if paid
|
|
93
93
|
let skillMeta;
|
|
94
94
|
try {
|
|
@@ -114,12 +114,12 @@ async function downloadSkillWithFallback(config, org, skill, version) {
|
|
|
114
114
|
if (skillMeta && (0, pricing_1.isPaidAgent)(skillMeta)) {
|
|
115
115
|
// Paid skill - check ownership
|
|
116
116
|
if (config.apiKey) {
|
|
117
|
-
const callerOrg = await (0, api_1.getOrg)(config);
|
|
117
|
+
const callerOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
118
118
|
const isOwner = (skillMeta.org_id && callerOrg.id === skillMeta.org_id) ||
|
|
119
119
|
(skillMeta.org_slug && callerOrg.slug === skillMeta.org_slug);
|
|
120
120
|
if (isOwner) {
|
|
121
121
|
// Owner - fetch from authenticated endpoint with full content
|
|
122
|
-
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
122
|
+
const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
123
123
|
const matching = myAgents.filter(a => a.name === skill && a.type === 'skill');
|
|
124
124
|
if (matching.length > 0) {
|
|
125
125
|
let targetAgent;
|
|
@@ -162,12 +162,12 @@ async function downloadSkillWithFallback(config, org, skill, version) {
|
|
|
162
162
|
// Check if download is disabled (server-only skill)
|
|
163
163
|
if (skillMeta && skillMeta.allow_local_download === false) {
|
|
164
164
|
if (config.apiKey) {
|
|
165
|
-
const callerOrg = await (0, api_1.getOrg)(config);
|
|
165
|
+
const callerOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
166
166
|
const isOwner = (skillMeta.org_id && callerOrg.id === skillMeta.org_id) ||
|
|
167
167
|
(skillMeta.org_slug && callerOrg.slug === skillMeta.org_slug);
|
|
168
168
|
if (isOwner) {
|
|
169
169
|
// Owner - fetch from authenticated endpoint
|
|
170
|
-
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
170
|
+
const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
171
171
|
const matching = myAgents.filter(a => a.name === skill && a.type === 'skill');
|
|
172
172
|
if (matching.length > 0) {
|
|
173
173
|
let targetAgent;
|
|
@@ -221,12 +221,12 @@ async function downloadSkillWithFallback(config, org, skill, version) {
|
|
|
221
221
|
if (!config.apiKey) {
|
|
222
222
|
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
223
223
|
}
|
|
224
|
-
const userOrg = await (0, api_1.getOrg)(config);
|
|
224
|
+
const userOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
225
225
|
if (userOrg.slug !== org) {
|
|
226
226
|
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
227
227
|
}
|
|
228
228
|
// Find skill in user's list
|
|
229
|
-
const agents = await (0, api_1.listMyAgents)(config);
|
|
229
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
230
230
|
const matching = agents.filter(a => a.name === skill && a.type === 'skill');
|
|
231
231
|
if (matching.length === 0) {
|
|
232
232
|
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
@@ -420,10 +420,12 @@ Instructions and guidance for AI agents...
|
|
|
420
420
|
}
|
|
421
421
|
result.skill = `${org}/${parsed.skill}`;
|
|
422
422
|
result.version = parsed.version;
|
|
423
|
+
// Resolve workspace context for the target org
|
|
424
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
|
|
423
425
|
// Download skill (tries public first, falls back to authenticated for private)
|
|
424
426
|
let skillData;
|
|
425
427
|
try {
|
|
426
|
-
skillData = await downloadSkillWithFallback(resolved, org, parsed.skill, parsed.version);
|
|
428
|
+
skillData = await downloadSkillWithFallback(resolved, org, parsed.skill, parsed.version, workspaceId);
|
|
427
429
|
}
|
|
428
430
|
catch (err) {
|
|
429
431
|
if (jsonMode) {
|
package/dist/lib/api.js
CHANGED
|
@@ -46,7 +46,7 @@ exports.updateOrg = updateOrg;
|
|
|
46
46
|
exports.getPublicAgent = getPublicAgent;
|
|
47
47
|
exports.listMyAgents = listMyAgents;
|
|
48
48
|
exports.createAgent = createAgent;
|
|
49
|
-
exports.
|
|
49
|
+
exports.listLlmKeys = listLlmKeys;
|
|
50
50
|
exports.downloadCodeBundle = downloadCodeBundle;
|
|
51
51
|
exports.uploadCodeBundle = uploadCodeBundle;
|
|
52
52
|
exports.getMyAgent = getMyAgent;
|
|
@@ -306,9 +306,8 @@ async function createAgent(config, data, workspaceId) {
|
|
|
306
306
|
headers,
|
|
307
307
|
});
|
|
308
308
|
}
|
|
309
|
-
async function
|
|
310
|
-
|
|
311
|
-
return result.keys;
|
|
309
|
+
async function listLlmKeys(config) {
|
|
310
|
+
return request(config, 'GET', '/llm-keys');
|
|
312
311
|
}
|
|
313
312
|
/**
|
|
314
313
|
* Download a code-runtime bundle for local execution.
|
package/dist/lib/bundle.js
CHANGED
|
@@ -87,7 +87,7 @@ async function runLlmChecks(options) {
|
|
|
87
87
|
try {
|
|
88
88
|
const config = await (0, config_1.getResolvedConfig)();
|
|
89
89
|
if (config.apiKey) {
|
|
90
|
-
const keys = await (0, api_1.
|
|
90
|
+
const keys = await (0, api_1.listLlmKeys)(config);
|
|
91
91
|
serverProviders = keys.map((k) => k.provider);
|
|
92
92
|
}
|
|
93
93
|
}
|
package/dist/lib/llm.js
CHANGED
|
@@ -5,39 +5,6 @@
|
|
|
5
5
|
* Centralized LLM provider configuration and utilities.
|
|
6
6
|
* Used by run, call, and skill commands.
|
|
7
7
|
*/
|
|
8
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
-
if (k2 === undefined) k2 = k;
|
|
10
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
-
}
|
|
14
|
-
Object.defineProperty(o, k2, desc);
|
|
15
|
-
}) : (function(o, m, k, k2) {
|
|
16
|
-
if (k2 === undefined) k2 = k;
|
|
17
|
-
o[k2] = m[k];
|
|
18
|
-
}));
|
|
19
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
-
}) : function(o, v) {
|
|
22
|
-
o["default"] = v;
|
|
23
|
-
});
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
9
|
exports.DEFAULT_MODELS = exports.PROVIDER_ENV_VARS = exports.LlmError = void 0;
|
|
43
10
|
exports.isRateLimitError = isRateLimitError;
|
|
@@ -107,37 +74,10 @@ function detectLlmKeyFromEnv(supportedProviders) {
|
|
|
107
74
|
* Checks local env vars first, then fetches from server if available.
|
|
108
75
|
* Returns provider, key, and optionally the model from server config.
|
|
109
76
|
*/
|
|
110
|
-
async function detectLlmKey(supportedProviders,
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return envKey;
|
|
115
|
-
// 2. If no env var, try server
|
|
116
|
-
if (config?.apiKey) {
|
|
117
|
-
try {
|
|
118
|
-
const { fetchLlmKeys } = await Promise.resolve().then(() => __importStar(require('./api')));
|
|
119
|
-
const serverKeys = await fetchLlmKeys(config);
|
|
120
|
-
for (const provider of supportedProviders) {
|
|
121
|
-
if (provider === 'any') {
|
|
122
|
-
// Return first available key
|
|
123
|
-
if (serverKeys.length > 0) {
|
|
124
|
-
const first = serverKeys[0];
|
|
125
|
-
return { provider: first.provider, key: first.api_key, model: first.model };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
const match = serverKeys.find((k) => k.provider === provider);
|
|
130
|
-
if (match) {
|
|
131
|
-
return { provider: match.provider, key: match.api_key, model: match.model };
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
// Server fetch failed, continue without
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return null;
|
|
77
|
+
async function detectLlmKey(supportedProviders, _config) {
|
|
78
|
+
// LLM keys are only available from local env vars.
|
|
79
|
+
// Server-stored keys are never exported (security best practice).
|
|
80
|
+
return detectLlmKeyFromEnv(supportedProviders);
|
|
141
81
|
}
|
|
142
82
|
/**
|
|
143
83
|
* Get the default model for a provider.
|
package/package.json
CHANGED