@orchagent/cli 0.2.9 → 0.2.10
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/call.js +1 -1
- package/dist/commands/info.js +56 -1
- package/dist/commands/init.js +25 -2
- package/dist/commands/publish.js +50 -6
- package/dist/commands/run.js +71 -6
- package/dist/commands/skill.js +91 -8
- package/dist/lib/api.js +76 -4
- package/package.json +1 -1
package/dist/commands/call.js
CHANGED
|
@@ -128,7 +128,7 @@ Note: Use 'call' for server-side execution (requires login), 'run' for local exe
|
|
|
128
128
|
if (!org) {
|
|
129
129
|
throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
|
|
130
130
|
}
|
|
131
|
-
const agentMeta = await (0, api_1.
|
|
131
|
+
const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, parsed.agent, parsed.version);
|
|
132
132
|
const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
|
|
133
133
|
const headers = {
|
|
134
134
|
Authorization: `Bearer ${resolved.apiKey}`,
|
package/dist/commands/info.js
CHANGED
|
@@ -54,6 +54,57 @@ async function fetchReadme(url) {
|
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
async function downloadAgentWithFallback(config, org, agent, version) {
|
|
58
|
+
// Try public endpoint first
|
|
59
|
+
try {
|
|
60
|
+
return await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
// Fallback to authenticated endpoint for private agents
|
|
67
|
+
if (!config.apiKey) {
|
|
68
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
69
|
+
}
|
|
70
|
+
const userOrg = await (0, api_1.getOrg)(config);
|
|
71
|
+
if (userOrg.slug !== org) {
|
|
72
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
73
|
+
}
|
|
74
|
+
// Find agent in user's list and construct download data
|
|
75
|
+
const agents = await (0, api_1.listMyAgents)(config);
|
|
76
|
+
const matching = agents.filter(a => a.name === agent);
|
|
77
|
+
if (matching.length === 0) {
|
|
78
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
79
|
+
}
|
|
80
|
+
let targetAgent = matching[0];
|
|
81
|
+
if (version !== 'latest') {
|
|
82
|
+
const found = matching.find(a => a.version === version);
|
|
83
|
+
if (!found) {
|
|
84
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
85
|
+
}
|
|
86
|
+
targetAgent = found;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Get most recent
|
|
90
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
91
|
+
}
|
|
92
|
+
// Convert Agent to AgentDownload format
|
|
93
|
+
return {
|
|
94
|
+
type: targetAgent.type,
|
|
95
|
+
name: targetAgent.name,
|
|
96
|
+
version: targetAgent.version,
|
|
97
|
+
description: targetAgent.description,
|
|
98
|
+
prompt: targetAgent.prompt,
|
|
99
|
+
input_schema: targetAgent.input_schema,
|
|
100
|
+
output_schema: targetAgent.output_schema,
|
|
101
|
+
supported_providers: targetAgent.supported_providers || ['any'],
|
|
102
|
+
source_url: targetAgent.source_url,
|
|
103
|
+
run_command: targetAgent.run_command,
|
|
104
|
+
url: targetAgent.url,
|
|
105
|
+
sdk_compatible: false,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
57
108
|
function registerInfoCommand(program) {
|
|
58
109
|
program
|
|
59
110
|
.command('info <agent>')
|
|
@@ -63,7 +114,7 @@ function registerInfoCommand(program) {
|
|
|
63
114
|
const config = await (0, config_1.getResolvedConfig)();
|
|
64
115
|
const { org, agent, version } = parseAgentRef(agentArg);
|
|
65
116
|
// Fetch agent metadata
|
|
66
|
-
const agentData = await (
|
|
117
|
+
const agentData = await downloadAgentWithFallback(config, org, agent, version);
|
|
67
118
|
if (options.json) {
|
|
68
119
|
// Don't expose internal routing URLs in JSON output
|
|
69
120
|
const output = { ...agentData };
|
|
@@ -93,6 +144,10 @@ function registerInfoCommand(program) {
|
|
|
93
144
|
if (agentData.run_command) {
|
|
94
145
|
process.stdout.write(`Run: ${agentData.run_command}\n`);
|
|
95
146
|
}
|
|
147
|
+
// Display SDK/Local Ready status
|
|
148
|
+
if (agentData.sdk_compatible) {
|
|
149
|
+
process.stdout.write(`Local Ready: yes (uses orchagent-sdk)\n`);
|
|
150
|
+
}
|
|
96
151
|
}
|
|
97
152
|
// Display input schema if available
|
|
98
153
|
if (agentData.input_schema?.properties && Object.keys(agentData.input_schema.properties).length > 0) {
|
package/dist/commands/init.js
CHANGED
|
@@ -46,15 +46,38 @@ const SCHEMA_TEMPLATE = `{
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
`;
|
|
49
|
+
const SKILL_TEMPLATE = `---
|
|
50
|
+
name: my-skill
|
|
51
|
+
description: When to use this skill
|
|
52
|
+
license: MIT
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
# My Skill
|
|
56
|
+
|
|
57
|
+
Instructions and guidance for AI agents...
|
|
58
|
+
`;
|
|
49
59
|
function registerInitCommand(program) {
|
|
50
60
|
program
|
|
51
61
|
.command('init')
|
|
52
62
|
.description('Initialize a new agent project')
|
|
53
63
|
.argument('[name]', 'Agent name (default: current directory name)')
|
|
54
|
-
.option('--type <type>', '
|
|
64
|
+
.option('--type <type>', 'Type: prompt, code, or skill (default: prompt)', 'prompt')
|
|
55
65
|
.action(async (name, options) => {
|
|
56
66
|
const cwd = process.cwd();
|
|
57
67
|
const agentName = name || path_1.default.basename(cwd);
|
|
68
|
+
// Handle skill type separately
|
|
69
|
+
if (options.type === 'skill') {
|
|
70
|
+
const skillPath = path_1.default.join(cwd, 'SKILL.md');
|
|
71
|
+
const skillContent = SKILL_TEMPLATE.replace('my-skill', agentName);
|
|
72
|
+
await promises_1.default.writeFile(skillPath, skillContent);
|
|
73
|
+
process.stdout.write(`Initialized skill "${agentName}" in ${cwd}\n`);
|
|
74
|
+
process.stdout.write(`\nFiles created:\n`);
|
|
75
|
+
process.stdout.write(` SKILL.md - Skill content with frontmatter\n`);
|
|
76
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
77
|
+
process.stdout.write(` 1. Edit SKILL.md with your skill content\n`);
|
|
78
|
+
process.stdout.write(` 2. Run: orchagent publish\n`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
58
81
|
const manifestPath = path_1.default.join(cwd, 'orchagent.json');
|
|
59
82
|
const promptPath = path_1.default.join(cwd, 'prompt.md');
|
|
60
83
|
const schemaPath = path_1.default.join(cwd, 'schema.json');
|
|
@@ -71,7 +94,7 @@ function registerInitCommand(program) {
|
|
|
71
94
|
// Create manifest
|
|
72
95
|
const manifest = JSON.parse(MANIFEST_TEMPLATE);
|
|
73
96
|
manifest.name = agentName;
|
|
74
|
-
manifest.type =
|
|
97
|
+
manifest.type = ['code', 'skill'].includes(options.type) ? options.type : 'prompt';
|
|
75
98
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
76
99
|
// Create prompt template (for prompt-based agents)
|
|
77
100
|
if (options.type !== 'code') {
|
package/dist/commands/publish.js
CHANGED
|
@@ -13,6 +13,37 @@ const api_1 = require("../lib/api");
|
|
|
13
13
|
const errors_1 = require("../lib/errors");
|
|
14
14
|
const analytics_1 = require("../lib/analytics");
|
|
15
15
|
const bundle_1 = require("../lib/bundle");
|
|
16
|
+
/**
|
|
17
|
+
* Check if orchagent-sdk is listed in requirements.txt or pyproject.toml
|
|
18
|
+
*/
|
|
19
|
+
async function detectSdkCompatible(agentDir) {
|
|
20
|
+
// Check requirements.txt
|
|
21
|
+
const requirementsPath = path_1.default.join(agentDir, 'requirements.txt');
|
|
22
|
+
try {
|
|
23
|
+
const content = await promises_1.default.readFile(requirementsPath, 'utf-8');
|
|
24
|
+
// Match orchagent-sdk with or without version specifier (e.g., orchagent-sdk, orchagent-sdk>=0.1.0)
|
|
25
|
+
if (/^orchagent-sdk\b/m.test(content)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// File doesn't exist, continue checking pyproject.toml
|
|
31
|
+
}
|
|
32
|
+
// Check pyproject.toml
|
|
33
|
+
const pyprojectPath = path_1.default.join(agentDir, 'pyproject.toml');
|
|
34
|
+
try {
|
|
35
|
+
const content = await promises_1.default.readFile(pyprojectPath, 'utf-8');
|
|
36
|
+
// Match orchagent-sdk in dependencies (various formats in TOML)
|
|
37
|
+
// e.g., "orchagent-sdk", 'orchagent-sdk>=0.1.0', orchagent-sdk = ">=0.1.0"
|
|
38
|
+
if (/["']?orchagent-sdk["']?\s*[=<>~!]?/m.test(content)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// File doesn't exist
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
16
47
|
async function parseSkillMd(filePath) {
|
|
17
48
|
try {
|
|
18
49
|
const content = await promises_1.default.readFile(filePath, 'utf-8');
|
|
@@ -32,7 +63,7 @@ async function parseSkillMd(filePath) {
|
|
|
32
63
|
function registerPublishCommand(program) {
|
|
33
64
|
program
|
|
34
65
|
.command('publish')
|
|
35
|
-
.description('Publish agent from local files')
|
|
66
|
+
.description('Publish agent or skill from local files')
|
|
36
67
|
.option('--url <url>', 'Agent URL (for code-based agents)')
|
|
37
68
|
.option('--public', 'Make agent public (default: true)', true)
|
|
38
69
|
.option('--private', 'Make agent private')
|
|
@@ -77,16 +108,17 @@ function registerPublishCommand(program) {
|
|
|
77
108
|
if (!manifest.name) {
|
|
78
109
|
throw new errors_1.CliError('orchagent.json must have name');
|
|
79
110
|
}
|
|
80
|
-
// Read prompt (for prompt-based agents)
|
|
111
|
+
// Read prompt (for prompt-based agents and skills)
|
|
81
112
|
let prompt;
|
|
82
|
-
if (manifest.type === 'prompt') {
|
|
113
|
+
if (manifest.type === 'prompt' || manifest.type === 'skill') {
|
|
83
114
|
const promptPath = path_1.default.join(cwd, 'prompt.md');
|
|
84
115
|
try {
|
|
85
116
|
prompt = await promises_1.default.readFile(promptPath, 'utf-8');
|
|
86
117
|
}
|
|
87
118
|
catch (err) {
|
|
88
119
|
if (err.code === 'ENOENT') {
|
|
89
|
-
|
|
120
|
+
const agentTypeName = manifest.type === 'skill' ? 'skill' : 'prompt-based agent';
|
|
121
|
+
throw new errors_1.CliError(`No prompt.md found for ${agentTypeName}`);
|
|
90
122
|
}
|
|
91
123
|
throw err;
|
|
92
124
|
}
|
|
@@ -128,6 +160,14 @@ function registerPublishCommand(program) {
|
|
|
128
160
|
const org = await (0, api_1.getOrg)(config);
|
|
129
161
|
// Default to 'any' provider if not specified
|
|
130
162
|
const supportedProviders = manifest.supported_providers || ['any'];
|
|
163
|
+
// Detect SDK compatibility for code agents
|
|
164
|
+
let sdkCompatible = false;
|
|
165
|
+
if (manifest.type === 'code') {
|
|
166
|
+
sdkCompatible = await detectSdkCompatible(cwd);
|
|
167
|
+
if (sdkCompatible) {
|
|
168
|
+
process.stdout.write(`SDK detected - agent will be marked as Local Ready\n`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
131
171
|
// Create the agent (server auto-assigns version)
|
|
132
172
|
const result = await (0, api_1.createAgent)(config, {
|
|
133
173
|
name: manifest.name,
|
|
@@ -145,6 +185,10 @@ function registerPublishCommand(program) {
|
|
|
145
185
|
source_url: manifest.source_url,
|
|
146
186
|
pip_package: manifest.pip_package,
|
|
147
187
|
run_command: manifest.run_command,
|
|
188
|
+
// SDK compatibility flag
|
|
189
|
+
sdk_compatible: sdkCompatible || undefined,
|
|
190
|
+
// Orchestration manifest (includes dependencies)
|
|
191
|
+
manifest: manifest.manifest,
|
|
148
192
|
});
|
|
149
193
|
const assignedVersion = result.agent?.version || 'v1';
|
|
150
194
|
const agentId = result.agent?.id;
|
|
@@ -165,9 +209,9 @@ function registerPublishCommand(program) {
|
|
|
165
209
|
if (!validation.valid) {
|
|
166
210
|
throw new errors_1.CliError(`Bundle validation failed: ${validation.error}`);
|
|
167
211
|
}
|
|
168
|
-
// Upload the bundle
|
|
212
|
+
// Upload the bundle with entrypoint
|
|
169
213
|
process.stdout.write(` Uploading bundle...\n`);
|
|
170
|
-
const uploadResult = await (0, api_1.uploadCodeBundle)(config, agentId, bundlePath);
|
|
214
|
+
const uploadResult = await (0, api_1.uploadCodeBundle)(config, agentId, bundlePath, manifest.entrypoint);
|
|
171
215
|
process.stdout.write(` Uploaded: ${uploadResult.code_hash.substring(0, 12)}...\n`);
|
|
172
216
|
}
|
|
173
217
|
finally {
|
package/dist/commands/run.js
CHANGED
|
@@ -68,8 +68,73 @@ function parseAgentRef(value) {
|
|
|
68
68
|
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
69
69
|
}
|
|
70
70
|
async function downloadAgent(config, org, agent, version) {
|
|
71
|
-
//
|
|
72
|
-
|
|
71
|
+
// Try public endpoint first
|
|
72
|
+
try {
|
|
73
|
+
return await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
// Fallback to authenticated endpoint for private agents
|
|
80
|
+
if (!config.apiKey) {
|
|
81
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
82
|
+
}
|
|
83
|
+
const userOrg = await (0, api_1.getOrg)(config);
|
|
84
|
+
if (userOrg.slug !== org) {
|
|
85
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
86
|
+
}
|
|
87
|
+
// Find agent in user's list
|
|
88
|
+
const agents = await (0, api_1.listMyAgents)(config);
|
|
89
|
+
const matching = agents.filter(a => a.name === agent);
|
|
90
|
+
if (matching.length === 0) {
|
|
91
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
92
|
+
}
|
|
93
|
+
let targetAgent = matching[0];
|
|
94
|
+
if (version !== 'latest') {
|
|
95
|
+
const found = matching.find(a => a.version === version);
|
|
96
|
+
if (!found) {
|
|
97
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
98
|
+
}
|
|
99
|
+
targetAgent = found;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
103
|
+
}
|
|
104
|
+
// Convert Agent to AgentDownload format
|
|
105
|
+
return {
|
|
106
|
+
id: targetAgent.id,
|
|
107
|
+
type: targetAgent.type,
|
|
108
|
+
name: targetAgent.name,
|
|
109
|
+
version: targetAgent.version,
|
|
110
|
+
description: targetAgent.description,
|
|
111
|
+
prompt: targetAgent.prompt,
|
|
112
|
+
input_schema: targetAgent.input_schema,
|
|
113
|
+
output_schema: targetAgent.output_schema,
|
|
114
|
+
supported_providers: targetAgent.supported_providers || ['any'],
|
|
115
|
+
default_models: targetAgent.default_models,
|
|
116
|
+
source_url: targetAgent.source_url,
|
|
117
|
+
pip_package: targetAgent.pip_package,
|
|
118
|
+
run_command: targetAgent.run_command,
|
|
119
|
+
url: targetAgent.url,
|
|
120
|
+
has_bundle: !!targetAgent.code_bundle_url,
|
|
121
|
+
entrypoint: targetAgent.entrypoint,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function downloadBundleWithFallback(config, org, agentName, version, agentId) {
|
|
125
|
+
// Try public endpoint first
|
|
126
|
+
try {
|
|
127
|
+
return await (0, api_1.downloadCodeBundle)(config, org, agentName, version);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
// Fallback to authenticated endpoint
|
|
134
|
+
if (!config.apiKey || !agentId) {
|
|
135
|
+
throw new api_1.ApiError(`Bundle for '${org}/${agentName}@${version}' not found`, 404);
|
|
136
|
+
}
|
|
137
|
+
return await (0, api_1.downloadCodeBundleAuthenticated)(config, agentId);
|
|
73
138
|
}
|
|
74
139
|
async function checkDependencies(config, dependencies) {
|
|
75
140
|
const results = [];
|
|
@@ -144,7 +209,7 @@ async function downloadDependenciesRecursively(config, depStatuses, visited = ne
|
|
|
144
209
|
await saveAgentLocally(org, agent, status.agentData);
|
|
145
210
|
// For bundle-based agents, also extract the bundle
|
|
146
211
|
if (status.agentData.has_bundle) {
|
|
147
|
-
await saveBundleLocally(config, org, agent, status.dep.version);
|
|
212
|
+
await saveBundleLocally(config, org, agent, status.dep.version, status.agentData.id);
|
|
148
213
|
}
|
|
149
214
|
// Install if it's a pip/source code agent
|
|
150
215
|
if (status.agentData.type === 'code' && (status.agentData.source_url || status.agentData.pip_package)) {
|
|
@@ -328,7 +393,7 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
328
393
|
try {
|
|
329
394
|
// Download the bundle
|
|
330
395
|
process.stderr.write(`Downloading bundle...\n`);
|
|
331
|
-
const bundleBuffer = await (
|
|
396
|
+
const bundleBuffer = await downloadBundleWithFallback(config, org, agentName, version, agentData.id);
|
|
332
397
|
await promises_1.default.writeFile(bundleZip, bundleBuffer);
|
|
333
398
|
process.stderr.write(`Bundle downloaded (${bundleBuffer.length} bytes)\n`);
|
|
334
399
|
// Extract the bundle
|
|
@@ -535,7 +600,7 @@ async function saveAgentLocally(org, agent, agentData) {
|
|
|
535
600
|
}
|
|
536
601
|
return agentDir;
|
|
537
602
|
}
|
|
538
|
-
async function saveBundleLocally(config, org, agent, version) {
|
|
603
|
+
async function saveBundleLocally(config, org, agent, version, agentId) {
|
|
539
604
|
const agentDir = path_1.default.join(AGENTS_DIR, org, agent);
|
|
540
605
|
const bundleDir = path_1.default.join(agentDir, 'bundle');
|
|
541
606
|
// Check if already extracted with same version
|
|
@@ -559,7 +624,7 @@ async function saveBundleLocally(config, org, agent, version) {
|
|
|
559
624
|
}
|
|
560
625
|
// Download and extract bundle
|
|
561
626
|
process.stderr.write(`Downloading bundle for ${org}/${agent}@${version}...\n`);
|
|
562
|
-
const bundleBuffer = await (
|
|
627
|
+
const bundleBuffer = await downloadBundleWithFallback(config, org, agent, version, agentId);
|
|
563
628
|
const tempZip = path_1.default.join(os_1.default.tmpdir(), `bundle-${Date.now()}.zip`);
|
|
564
629
|
await promises_1.default.writeFile(tempZip, bundleBuffer);
|
|
565
630
|
// Clean and recreate bundle directory
|
package/dist/commands/skill.js
CHANGED
|
@@ -49,6 +49,61 @@ function parseSkillRef(value) {
|
|
|
49
49
|
}
|
|
50
50
|
throw new errors_1.CliError('Invalid skill reference. Use org/skill or skill format.');
|
|
51
51
|
}
|
|
52
|
+
async function downloadSkillWithFallback(config, org, skill, version) {
|
|
53
|
+
// Try public endpoint first
|
|
54
|
+
try {
|
|
55
|
+
const meta = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${skill}/${version}`);
|
|
56
|
+
// Verify it's a skill type before downloading
|
|
57
|
+
const skillType = meta.type;
|
|
58
|
+
if (skillType !== 'skill') {
|
|
59
|
+
throw new errors_1.CliError(`${org}/${skill} is not a skill (type: ${skillType || 'prompt'})`);
|
|
60
|
+
}
|
|
61
|
+
// Download content from public endpoint
|
|
62
|
+
return await (0, api_1.publicRequest)(config, `/public/agents/${org}/${skill}/${version}/download`);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
// Fallback to authenticated endpoint for private skills
|
|
69
|
+
if (!config.apiKey) {
|
|
70
|
+
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
71
|
+
}
|
|
72
|
+
const userOrg = await (0, api_1.getOrg)(config);
|
|
73
|
+
if (userOrg.slug !== org) {
|
|
74
|
+
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
75
|
+
}
|
|
76
|
+
// Find skill in user's list
|
|
77
|
+
const agents = await (0, api_1.listMyAgents)(config);
|
|
78
|
+
const matching = agents.filter(a => a.name === skill && a.type === 'skill');
|
|
79
|
+
if (matching.length === 0) {
|
|
80
|
+
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
81
|
+
}
|
|
82
|
+
let targetAgent = matching[0];
|
|
83
|
+
if (version !== 'v1' && version !== 'latest') {
|
|
84
|
+
const found = matching.find(a => a.version === version);
|
|
85
|
+
if (!found) {
|
|
86
|
+
throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
|
|
87
|
+
}
|
|
88
|
+
targetAgent = found;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Get most recent
|
|
92
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
93
|
+
}
|
|
94
|
+
// Verify it's a skill type
|
|
95
|
+
if (targetAgent.type !== 'skill') {
|
|
96
|
+
throw new errors_1.CliError(`${org}/${skill} is not a skill (type: ${targetAgent.type || 'prompt'})`);
|
|
97
|
+
}
|
|
98
|
+
// Convert Agent to SkillDownload format
|
|
99
|
+
return {
|
|
100
|
+
type: targetAgent.type,
|
|
101
|
+
name: targetAgent.name,
|
|
102
|
+
version: targetAgent.version,
|
|
103
|
+
description: targetAgent.description,
|
|
104
|
+
prompt: targetAgent.prompt,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
52
107
|
function registerSkillCommand(program) {
|
|
53
108
|
const skill = program.command('skill').description('Manage and install skills');
|
|
54
109
|
// orch skill list (deprecated)
|
|
@@ -65,6 +120,40 @@ function registerSkillCommand(program) {
|
|
|
65
120
|
'Browse all skills at: https://orchagent.io/explore\n');
|
|
66
121
|
process.exit(0);
|
|
67
122
|
});
|
|
123
|
+
// orch skill create [name]
|
|
124
|
+
skill
|
|
125
|
+
.command('create [name]')
|
|
126
|
+
.description('Create a new skill from template')
|
|
127
|
+
.action(async (name) => {
|
|
128
|
+
const cwd = process.cwd();
|
|
129
|
+
const skillName = name || path_1.default.basename(cwd);
|
|
130
|
+
const skillPath = path_1.default.join(cwd, 'SKILL.md');
|
|
131
|
+
// Check if SKILL.md already exists
|
|
132
|
+
try {
|
|
133
|
+
await promises_1.default.access(skillPath);
|
|
134
|
+
throw new errors_1.CliError('SKILL.md already exists');
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
if (err.code !== 'ENOENT')
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
const template = `---
|
|
141
|
+
name: ${skillName}
|
|
142
|
+
description: When to use this skill
|
|
143
|
+
license: MIT
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
# ${skillName}
|
|
147
|
+
|
|
148
|
+
Instructions and guidance for AI agents...
|
|
149
|
+
`;
|
|
150
|
+
await promises_1.default.writeFile(skillPath, template);
|
|
151
|
+
await (0, analytics_1.track)('cli_skill_create', { name: skillName });
|
|
152
|
+
process.stdout.write(`Created skill: ${skillPath}\n`);
|
|
153
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
154
|
+
process.stdout.write(` 1. Edit SKILL.md with your skill content\n`);
|
|
155
|
+
process.stdout.write(` 2. Run: orchagent publish\n`);
|
|
156
|
+
});
|
|
68
157
|
// orch skill install <skill>
|
|
69
158
|
skill
|
|
70
159
|
.command('install <skill>')
|
|
@@ -77,14 +166,8 @@ function registerSkillCommand(program) {
|
|
|
77
166
|
if (!org) {
|
|
78
167
|
throw new errors_1.CliError('Missing org. Use org/skill or set default org.');
|
|
79
168
|
}
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
const skillType = skillMeta.type;
|
|
83
|
-
if (skillType !== 'skill') {
|
|
84
|
-
throw new errors_1.CliError(`${org}/${parsed.skill} is not a skill (type: ${skillType || 'prompt'})`);
|
|
85
|
-
}
|
|
86
|
-
// Download skill content
|
|
87
|
-
const skillData = await (0, api_1.publicRequest)(resolved, `/public/agents/${org}/${parsed.skill}/${parsed.version}/download`);
|
|
169
|
+
// Download skill (tries public first, falls back to authenticated for private)
|
|
170
|
+
const skillData = await downloadSkillWithFallback(resolved, org, parsed.skill, parsed.version);
|
|
88
171
|
if (!skillData.prompt) {
|
|
89
172
|
throw new errors_1.CliError('Skill has no content');
|
|
90
173
|
}
|
package/dist/lib/api.js
CHANGED
|
@@ -49,6 +49,9 @@ exports.searchAgents = searchAgents;
|
|
|
49
49
|
exports.fetchLlmKeys = fetchLlmKeys;
|
|
50
50
|
exports.downloadCodeBundle = downloadCodeBundle;
|
|
51
51
|
exports.uploadCodeBundle = uploadCodeBundle;
|
|
52
|
+
exports.getMyAgent = getMyAgent;
|
|
53
|
+
exports.getAgentWithFallback = getAgentWithFallback;
|
|
54
|
+
exports.downloadCodeBundleAuthenticated = downloadCodeBundleAuthenticated;
|
|
52
55
|
class ApiError extends Error {
|
|
53
56
|
status;
|
|
54
57
|
payload;
|
|
@@ -166,7 +169,7 @@ async function downloadCodeBundle(config, org, agent, version) {
|
|
|
166
169
|
/**
|
|
167
170
|
* Upload a code bundle for a hosted code agent.
|
|
168
171
|
*/
|
|
169
|
-
async function uploadCodeBundle(config, agentId, bundlePath) {
|
|
172
|
+
async function uploadCodeBundle(config, agentId, bundlePath, entrypoint) {
|
|
170
173
|
if (!config.apiKey) {
|
|
171
174
|
throw new ApiError('Missing API key. Run `orchagent login` first.', 401);
|
|
172
175
|
}
|
|
@@ -177,11 +180,16 @@ async function uploadCodeBundle(config, agentId, bundlePath) {
|
|
|
177
180
|
// Create form data
|
|
178
181
|
const formData = new FormData();
|
|
179
182
|
formData.append('file', blob, 'bundle.zip');
|
|
183
|
+
// Build headers
|
|
184
|
+
const headers = {
|
|
185
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
186
|
+
};
|
|
187
|
+
if (entrypoint) {
|
|
188
|
+
headers['x-entrypoint'] = entrypoint;
|
|
189
|
+
}
|
|
180
190
|
const response = await fetch(`${config.apiUrl.replace(/\/$/, '')}/agents/${agentId}/upload`, {
|
|
181
191
|
method: 'POST',
|
|
182
|
-
headers
|
|
183
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
184
|
-
},
|
|
192
|
+
headers,
|
|
185
193
|
body: formData,
|
|
186
194
|
});
|
|
187
195
|
if (!response.ok) {
|
|
@@ -189,3 +197,67 @@ async function uploadCodeBundle(config, agentId, bundlePath) {
|
|
|
189
197
|
}
|
|
190
198
|
return (await response.json());
|
|
191
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Get single agent by name/version from authenticated endpoint.
|
|
202
|
+
*/
|
|
203
|
+
async function getMyAgent(config, agentName, version) {
|
|
204
|
+
const agents = await listMyAgents(config);
|
|
205
|
+
const matching = agents.filter(a => a.name === agentName);
|
|
206
|
+
if (matching.length === 0)
|
|
207
|
+
return null;
|
|
208
|
+
if (version === 'latest') {
|
|
209
|
+
return matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
210
|
+
}
|
|
211
|
+
return matching.find(a => a.version === version) ?? null;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Try public endpoint first, fallback to authenticated for private agents.
|
|
215
|
+
*/
|
|
216
|
+
async function getAgentWithFallback(config, org, agentName, version) {
|
|
217
|
+
try {
|
|
218
|
+
return await getPublicAgent(config, org, agentName, version);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
if (!(err instanceof ApiError) || err.status !== 404)
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
if (!config.apiKey) {
|
|
225
|
+
throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
|
|
226
|
+
}
|
|
227
|
+
const userOrg = await getOrg(config);
|
|
228
|
+
if (userOrg.slug !== org) {
|
|
229
|
+
throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
|
|
230
|
+
}
|
|
231
|
+
const myAgent = await getMyAgent(config, agentName, version);
|
|
232
|
+
if (!myAgent) {
|
|
233
|
+
throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
|
|
234
|
+
}
|
|
235
|
+
return myAgent;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Download a code bundle for a private agent using authenticated endpoint.
|
|
239
|
+
*/
|
|
240
|
+
async function downloadCodeBundleAuthenticated(config, agentId) {
|
|
241
|
+
if (!config.apiKey) {
|
|
242
|
+
throw new ApiError('Missing API key for authenticated bundle download', 401);
|
|
243
|
+
}
|
|
244
|
+
const response = await fetch(`${config.apiUrl.replace(/\/$/, '')}/agents/${agentId}/bundle`, {
|
|
245
|
+
headers: {
|
|
246
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
const text = await response.text();
|
|
251
|
+
let message = response.statusText;
|
|
252
|
+
try {
|
|
253
|
+
const payload = JSON.parse(text);
|
|
254
|
+
message = payload.error?.message || payload.message || message;
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Use default message
|
|
258
|
+
}
|
|
259
|
+
throw new ApiError(message, response.status);
|
|
260
|
+
}
|
|
261
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
262
|
+
return Buffer.from(arrayBuffer);
|
|
263
|
+
}
|