@orchagent/cli 0.2.25 → 0.3.0
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/adapters/agents-md.js +51 -0
- package/dist/adapters/claude-code.js +104 -0
- package/dist/adapters/cursor.js +67 -0
- package/dist/adapters/index.js +38 -0
- package/dist/adapters/registry.js +51 -0
- package/dist/adapters/types.js +2 -0
- package/dist/commands/config.js +74 -0
- package/dist/commands/formats.js +44 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/install.js +172 -0
- package/dist/commands/run.js +90 -7
- package/dist/commands/skill.js +24 -3
- package/dist/commands/update.js +146 -0
- package/dist/lib/api.js +37 -2
- package/dist/lib/config.js +23 -0
- package/dist/lib/installed.js +94 -0
- package/dist/lib/llm.js +44 -6
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentsMdAdapter = void 0;
|
|
4
|
+
exports.agentsMdAdapter = {
|
|
5
|
+
id: 'agents-md',
|
|
6
|
+
name: 'AGENTS.md (Universal)',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
formatVersion: '1.0',
|
|
9
|
+
supportsAgentTypes: ['prompt', 'skill'],
|
|
10
|
+
installPaths: [
|
|
11
|
+
{
|
|
12
|
+
scope: 'project',
|
|
13
|
+
path: './',
|
|
14
|
+
description: 'Project root (AGENTS.md)',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
canConvert(agent) {
|
|
18
|
+
const warnings = [];
|
|
19
|
+
const errors = [];
|
|
20
|
+
if (agent.type === 'code') {
|
|
21
|
+
errors.push('Code agents cannot be converted to AGENTS.md');
|
|
22
|
+
return { canConvert: false, warnings, errors };
|
|
23
|
+
}
|
|
24
|
+
if (!agent.prompt) {
|
|
25
|
+
errors.push('Agent has no prompt content');
|
|
26
|
+
return { canConvert: false, warnings, errors };
|
|
27
|
+
}
|
|
28
|
+
warnings.push('AGENTS.md content should be appended to existing file, not replaced');
|
|
29
|
+
return { canConvert: true, warnings, errors };
|
|
30
|
+
},
|
|
31
|
+
convert(agent) {
|
|
32
|
+
const agentRef = `${agent.org_slug}/${agent.name}`;
|
|
33
|
+
const description = agent.description || '';
|
|
34
|
+
// Use orchagent markers for managed section
|
|
35
|
+
const content = `<!-- orchagent:${agentRef} -->
|
|
36
|
+
## ${agent.name}
|
|
37
|
+
|
|
38
|
+
${description}
|
|
39
|
+
|
|
40
|
+
${agent.prompt || ''}
|
|
41
|
+
<!-- /orchagent:${agentRef} -->
|
|
42
|
+
`;
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
filename: 'AGENTS.md',
|
|
46
|
+
content,
|
|
47
|
+
installPath: './',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.claudeCodeAdapter = void 0;
|
|
7
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
8
|
+
// Map Anthropic model names to Claude Code aliases
|
|
9
|
+
function mapModelToAlias(model) {
|
|
10
|
+
if (!model)
|
|
11
|
+
return 'inherit';
|
|
12
|
+
const lower = model.toLowerCase();
|
|
13
|
+
if (lower.includes('opus'))
|
|
14
|
+
return 'opus';
|
|
15
|
+
if (lower.includes('haiku'))
|
|
16
|
+
return 'haiku';
|
|
17
|
+
// Default to sonnet for any Claude model
|
|
18
|
+
if (lower.includes('claude') || lower.includes('sonnet'))
|
|
19
|
+
return 'sonnet';
|
|
20
|
+
return 'inherit';
|
|
21
|
+
}
|
|
22
|
+
// Convert agent name to valid Claude Code name (lowercase + hyphens)
|
|
23
|
+
function normalizeAgentName(name) {
|
|
24
|
+
return name
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
27
|
+
.replace(/-+/g, '-')
|
|
28
|
+
.replace(/^-|-$/g, '');
|
|
29
|
+
}
|
|
30
|
+
exports.claudeCodeAdapter = {
|
|
31
|
+
id: 'claude-code',
|
|
32
|
+
name: 'Claude Code Sub-Agent',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
formatVersion: '2026-01',
|
|
35
|
+
supportsAgentTypes: ['prompt', 'skill'],
|
|
36
|
+
installPaths: [
|
|
37
|
+
{
|
|
38
|
+
scope: 'user',
|
|
39
|
+
path: '~/.claude/agents/',
|
|
40
|
+
description: 'User-level (available in all projects)',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
scope: 'project',
|
|
44
|
+
path: '.claude/agents/',
|
|
45
|
+
description: 'Project-level (current directory)',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
canConvert(agent) {
|
|
49
|
+
const warnings = [];
|
|
50
|
+
const errors = [];
|
|
51
|
+
// Code agents cannot be converted
|
|
52
|
+
if (agent.type === 'code') {
|
|
53
|
+
errors.push('Code agents cannot be converted to Claude Code sub-agents (they require execution)');
|
|
54
|
+
return { canConvert: false, warnings, errors };
|
|
55
|
+
}
|
|
56
|
+
// Check for prompt
|
|
57
|
+
if (!agent.prompt) {
|
|
58
|
+
errors.push('Agent has no prompt content');
|
|
59
|
+
return { canConvert: false, warnings, errors };
|
|
60
|
+
}
|
|
61
|
+
// Warnings for fields that can't be fully mapped
|
|
62
|
+
if (agent.input_schema) {
|
|
63
|
+
warnings.push('input_schema will be described in the prompt body, not enforced');
|
|
64
|
+
}
|
|
65
|
+
if (agent.output_schema) {
|
|
66
|
+
warnings.push('output_schema will be described in the prompt body, not enforced');
|
|
67
|
+
}
|
|
68
|
+
return { canConvert: true, warnings, errors };
|
|
69
|
+
},
|
|
70
|
+
convert(agent) {
|
|
71
|
+
const normalizedName = normalizeAgentName(agent.name);
|
|
72
|
+
// Build frontmatter
|
|
73
|
+
const frontmatter = {
|
|
74
|
+
name: normalizedName,
|
|
75
|
+
description: agent.description || `Delegatable agent: ${agent.name}`,
|
|
76
|
+
tools: 'Read, Glob, Grep', // Safe defaults - read-only
|
|
77
|
+
};
|
|
78
|
+
// Map model if specified
|
|
79
|
+
if (agent.default_models?.anthropic) {
|
|
80
|
+
const modelAlias = mapModelToAlias(agent.default_models.anthropic);
|
|
81
|
+
if (modelAlias !== 'inherit') {
|
|
82
|
+
frontmatter.model = modelAlias;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Build body
|
|
86
|
+
let body = agent.prompt || '';
|
|
87
|
+
// Add schema descriptions if present
|
|
88
|
+
if (agent.input_schema) {
|
|
89
|
+
body += `\n\n## Input Schema\n\nThis agent expects input matching:\n\`\`\`json\n${JSON.stringify(agent.input_schema, null, 2)}\n\`\`\``;
|
|
90
|
+
}
|
|
91
|
+
if (agent.output_schema) {
|
|
92
|
+
body += `\n\n## Output Schema\n\nThis agent should return output matching:\n\`\`\`json\n${JSON.stringify(agent.output_schema, null, 2)}\n\`\`\``;
|
|
93
|
+
}
|
|
94
|
+
// Combine frontmatter + body
|
|
95
|
+
const content = `---\n${yaml_1.default.stringify(frontmatter).trim()}\n---\n\n${body.trim()}\n`;
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
filename: `${normalizedName}.md`,
|
|
99
|
+
content,
|
|
100
|
+
installPath: '.claude/agents/',
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cursorAdapter = void 0;
|
|
4
|
+
// Convert agent name to valid filename (lowercase + hyphens)
|
|
5
|
+
function normalizeAgentName(name) {
|
|
6
|
+
return name
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
9
|
+
.replace(/-+/g, '-')
|
|
10
|
+
.replace(/^-|-$/g, '');
|
|
11
|
+
}
|
|
12
|
+
exports.cursorAdapter = {
|
|
13
|
+
id: 'cursor',
|
|
14
|
+
name: 'Cursor Rules',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
formatVersion: '2026-01',
|
|
17
|
+
supportsAgentTypes: ['prompt', 'skill'],
|
|
18
|
+
installPaths: [
|
|
19
|
+
{
|
|
20
|
+
scope: 'project',
|
|
21
|
+
path: '.cursor/rules/',
|
|
22
|
+
description: 'Project-level Cursor rules',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
canConvert(agent) {
|
|
26
|
+
const warnings = [];
|
|
27
|
+
const errors = [];
|
|
28
|
+
if (agent.type === 'code') {
|
|
29
|
+
errors.push('Code agents cannot be converted to Cursor rules');
|
|
30
|
+
return { canConvert: false, warnings, errors };
|
|
31
|
+
}
|
|
32
|
+
if (!agent.prompt) {
|
|
33
|
+
errors.push('Agent has no prompt content');
|
|
34
|
+
return { canConvert: false, warnings, errors };
|
|
35
|
+
}
|
|
36
|
+
// Cursor doesn't support input/output schemas
|
|
37
|
+
if (agent.input_schema) {
|
|
38
|
+
warnings.push('input_schema is not supported in Cursor rules');
|
|
39
|
+
}
|
|
40
|
+
if (agent.output_schema) {
|
|
41
|
+
warnings.push('output_schema is not supported in Cursor rules');
|
|
42
|
+
}
|
|
43
|
+
return { canConvert: true, warnings, errors };
|
|
44
|
+
},
|
|
45
|
+
convert(agent) {
|
|
46
|
+
const normalizedName = normalizeAgentName(agent.name);
|
|
47
|
+
const description = agent.description || `Rules from ${agent.name}`;
|
|
48
|
+
// Cursor .mdc format
|
|
49
|
+
const content = `---
|
|
50
|
+
description: ${description}
|
|
51
|
+
globs:
|
|
52
|
+
alwaysApply: false
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
# ${agent.name}
|
|
56
|
+
|
|
57
|
+
${agent.prompt || ''}
|
|
58
|
+
`;
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
filename: `${normalizedName}.mdc`,
|
|
62
|
+
content,
|
|
63
|
+
installPath: '.cursor/rules/',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.agentsMdAdapter = exports.cursorAdapter = exports.claudeCodeAdapter = exports.adapterRegistry = exports.AdapterRegistry = void 0;
|
|
18
|
+
// Export types
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
// Export registry
|
|
21
|
+
const registry_1 = require("./registry");
|
|
22
|
+
var registry_2 = require("./registry");
|
|
23
|
+
Object.defineProperty(exports, "AdapterRegistry", { enumerable: true, get: function () { return registry_2.AdapterRegistry; } });
|
|
24
|
+
Object.defineProperty(exports, "adapterRegistry", { enumerable: true, get: function () { return registry_2.adapterRegistry; } });
|
|
25
|
+
// Import and register adapters
|
|
26
|
+
const claude_code_1 = require("./claude-code");
|
|
27
|
+
var claude_code_2 = require("./claude-code");
|
|
28
|
+
Object.defineProperty(exports, "claudeCodeAdapter", { enumerable: true, get: function () { return claude_code_2.claudeCodeAdapter; } });
|
|
29
|
+
const cursor_1 = require("./cursor");
|
|
30
|
+
var cursor_2 = require("./cursor");
|
|
31
|
+
Object.defineProperty(exports, "cursorAdapter", { enumerable: true, get: function () { return cursor_2.cursorAdapter; } });
|
|
32
|
+
const agents_md_1 = require("./agents-md");
|
|
33
|
+
var agents_md_2 = require("./agents-md");
|
|
34
|
+
Object.defineProperty(exports, "agentsMdAdapter", { enumerable: true, get: function () { return agents_md_2.agentsMdAdapter; } });
|
|
35
|
+
// Register built-in adapters
|
|
36
|
+
registry_1.adapterRegistry.register(claude_code_1.claudeCodeAdapter);
|
|
37
|
+
registry_1.adapterRegistry.register(cursor_1.cursorAdapter);
|
|
38
|
+
registry_1.adapterRegistry.register(agents_md_1.agentsMdAdapter);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.adapterRegistry = exports.AdapterRegistry = void 0;
|
|
4
|
+
class AdapterRegistry {
|
|
5
|
+
adapters = new Map();
|
|
6
|
+
/**
|
|
7
|
+
* Register an adapter
|
|
8
|
+
*/
|
|
9
|
+
register(adapter) {
|
|
10
|
+
if (this.adapters.has(adapter.id)) {
|
|
11
|
+
throw new Error(`Adapter '${adapter.id}' is already registered`);
|
|
12
|
+
}
|
|
13
|
+
this.adapters.set(adapter.id, adapter);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get adapter by ID
|
|
17
|
+
*/
|
|
18
|
+
get(id) {
|
|
19
|
+
return this.adapters.get(id);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* List all registered adapters
|
|
23
|
+
*/
|
|
24
|
+
list() {
|
|
25
|
+
return Array.from(this.adapters.values());
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Find adapters compatible with an agent
|
|
29
|
+
*/
|
|
30
|
+
findCompatible(agent) {
|
|
31
|
+
return this.list().filter(adapter => {
|
|
32
|
+
const result = adapter.canConvert(agent);
|
|
33
|
+
return result.canConvert;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if an adapter ID is valid
|
|
38
|
+
*/
|
|
39
|
+
has(id) {
|
|
40
|
+
return this.adapters.has(id);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get all adapter IDs
|
|
44
|
+
*/
|
|
45
|
+
getIds() {
|
|
46
|
+
return Array.from(this.adapters.keys());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.AdapterRegistry = AdapterRegistry;
|
|
50
|
+
// Global registry instance
|
|
51
|
+
exports.adapterRegistry = new AdapterRegistry();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerConfigCommand = registerConfigCommand;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const errors_1 = require("../lib/errors");
|
|
6
|
+
const SUPPORTED_KEYS = ['default-format'];
|
|
7
|
+
function isValidKey(key) {
|
|
8
|
+
return SUPPORTED_KEYS.includes(key);
|
|
9
|
+
}
|
|
10
|
+
async function setConfigValue(key, value) {
|
|
11
|
+
if (!isValidKey(key)) {
|
|
12
|
+
throw new errors_1.CliError(`Unknown config key: ${key}. Supported keys: ${SUPPORTED_KEYS.join(', ')}`);
|
|
13
|
+
}
|
|
14
|
+
if (key === 'default-format') {
|
|
15
|
+
const formats = value.split(',').map((f) => f.trim()).filter(Boolean);
|
|
16
|
+
// Validate all format IDs
|
|
17
|
+
const invalidFormats = formats.filter((f) => !config_1.VALID_FORMAT_IDS.includes(f));
|
|
18
|
+
if (invalidFormats.length > 0) {
|
|
19
|
+
throw new errors_1.CliError(`Invalid format ID(s): ${invalidFormats.join(', ')}. Valid formats: ${config_1.VALID_FORMAT_IDS.join(', ')}`);
|
|
20
|
+
}
|
|
21
|
+
await (0, config_1.setDefaultFormats)(formats);
|
|
22
|
+
process.stdout.write(`Set default-format to: ${formats.join(',')}\n`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function getConfigValue(key) {
|
|
26
|
+
if (!isValidKey(key)) {
|
|
27
|
+
throw new errors_1.CliError(`Unknown config key: ${key}. Supported keys: ${SUPPORTED_KEYS.join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
if (key === 'default-format') {
|
|
30
|
+
const formats = await (0, config_1.getDefaultFormats)();
|
|
31
|
+
if (formats.length === 0) {
|
|
32
|
+
process.stdout.write('(not set)\n');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
process.stdout.write(`${formats.join(',')}\n`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function listConfigValues() {
|
|
40
|
+
const config = await (0, config_1.loadConfig)();
|
|
41
|
+
process.stdout.write('CLI Configuration:\n\n');
|
|
42
|
+
// default-format
|
|
43
|
+
const formats = config.default_formats ?? [];
|
|
44
|
+
if (formats.length > 0) {
|
|
45
|
+
process.stdout.write(` default-format: ${formats.join(',')}\n`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
process.stdout.write(' default-format: (not set)\n');
|
|
49
|
+
}
|
|
50
|
+
process.stdout.write('\n');
|
|
51
|
+
}
|
|
52
|
+
function registerConfigCommand(program) {
|
|
53
|
+
const config = program
|
|
54
|
+
.command('config')
|
|
55
|
+
.description('Manage CLI configuration');
|
|
56
|
+
config
|
|
57
|
+
.command('set <key> <value>')
|
|
58
|
+
.description('Set a configuration value')
|
|
59
|
+
.action(async (key, value) => {
|
|
60
|
+
await setConfigValue(key, value);
|
|
61
|
+
});
|
|
62
|
+
config
|
|
63
|
+
.command('get <key>')
|
|
64
|
+
.description('Get a configuration value')
|
|
65
|
+
.action(async (key) => {
|
|
66
|
+
await getConfigValue(key);
|
|
67
|
+
});
|
|
68
|
+
config
|
|
69
|
+
.command('list')
|
|
70
|
+
.description('List all configuration values')
|
|
71
|
+
.action(async () => {
|
|
72
|
+
await listConfigValues();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerFormatsCommand = registerFormatsCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const adapters_1 = require("../adapters");
|
|
9
|
+
function registerFormatsCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('formats')
|
|
12
|
+
.description('List available export formats for agents')
|
|
13
|
+
.option('--json', 'Output raw JSON')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
const adapters = adapters_1.adapterRegistry.list();
|
|
16
|
+
if (options.json) {
|
|
17
|
+
const data = adapters.map(a => ({
|
|
18
|
+
id: a.id,
|
|
19
|
+
name: a.name,
|
|
20
|
+
version: a.version,
|
|
21
|
+
formatVersion: a.formatVersion,
|
|
22
|
+
supportsAgentTypes: a.supportsAgentTypes,
|
|
23
|
+
installPaths: a.installPaths,
|
|
24
|
+
}));
|
|
25
|
+
process.stdout.write(JSON.stringify(data, null, 2) + '\n');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
process.stdout.write('\nAvailable export formats:\n\n');
|
|
29
|
+
for (const adapter of adapters) {
|
|
30
|
+
process.stdout.write(` ${chalk_1.default.cyan(adapter.id)} ${adapter.name}\n`);
|
|
31
|
+
process.stdout.write(` Supports: ${adapter.supportsAgentTypes.join(', ')} agents\n`);
|
|
32
|
+
process.stdout.write(` Format version: ${adapter.formatVersion}\n`);
|
|
33
|
+
for (const installPath of adapter.installPaths) {
|
|
34
|
+
const pathDisplay = installPath.scope === 'user'
|
|
35
|
+
? installPath.path
|
|
36
|
+
: installPath.path;
|
|
37
|
+
process.stdout.write(` ${installPath.scope}: ${pathDisplay}\n`);
|
|
38
|
+
}
|
|
39
|
+
process.stdout.write('\n');
|
|
40
|
+
}
|
|
41
|
+
process.stdout.write('Use with: orch install <agent> --format <id>\n');
|
|
42
|
+
process.stdout.write('Set default: orch config set default-format <ids>\n\n');
|
|
43
|
+
});
|
|
44
|
+
}
|
package/dist/commands/index.js
CHANGED
|
@@ -21,6 +21,10 @@ const status_1 = require("./status");
|
|
|
21
21
|
const workspace_1 = require("./workspace");
|
|
22
22
|
const tree_1 = require("./tree");
|
|
23
23
|
const docs_1 = require("./docs");
|
|
24
|
+
const config_1 = require("./config");
|
|
25
|
+
const install_1 = require("./install");
|
|
26
|
+
const formats_1 = require("./formats");
|
|
27
|
+
const update_1 = require("./update");
|
|
24
28
|
function registerCommands(program) {
|
|
25
29
|
(0, login_1.registerLoginCommand)(program);
|
|
26
30
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -42,4 +46,8 @@ function registerCommands(program) {
|
|
|
42
46
|
(0, workspace_1.registerWorkspaceCommand)(program);
|
|
43
47
|
(0, tree_1.registerTreeCommand)(program);
|
|
44
48
|
(0, docs_1.registerDocsCommand)(program);
|
|
49
|
+
(0, config_1.registerConfigCommand)(program);
|
|
50
|
+
(0, install_1.registerInstallCommand)(program);
|
|
51
|
+
(0, formats_1.registerFormatsCommand)(program);
|
|
52
|
+
(0, update_1.registerUpdateCommand)(program);
|
|
45
53
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerInstallCommand = registerInstallCommand;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const config_1 = require("../lib/config");
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
const errors_1 = require("../lib/errors");
|
|
13
|
+
const analytics_1 = require("../lib/analytics");
|
|
14
|
+
const adapters_1 = require("../adapters");
|
|
15
|
+
const installed_1 = require("../lib/installed");
|
|
16
|
+
const DEFAULT_VERSION = 'v1';
|
|
17
|
+
function parseAgentRef(value) {
|
|
18
|
+
const [ref, versionPart] = value.split('@');
|
|
19
|
+
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
20
|
+
const segments = ref.split('/');
|
|
21
|
+
if (segments.length === 1) {
|
|
22
|
+
return { name: segments[0], version };
|
|
23
|
+
}
|
|
24
|
+
if (segments.length === 2) {
|
|
25
|
+
return { org: segments[0], name: segments[1], version };
|
|
26
|
+
}
|
|
27
|
+
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
28
|
+
}
|
|
29
|
+
async function downloadAgentWithFallback(config, org, name, version) {
|
|
30
|
+
// Try public endpoint first
|
|
31
|
+
try {
|
|
32
|
+
const agent = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${name}/${version}`);
|
|
33
|
+
return { ...agent, org_slug: org };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
// Fallback to authenticated endpoint for private agents
|
|
40
|
+
if (!config.apiKey) {
|
|
41
|
+
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
42
|
+
}
|
|
43
|
+
const userOrg = await (0, api_1.getOrg)(config);
|
|
44
|
+
if (userOrg.slug !== org) {
|
|
45
|
+
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
46
|
+
}
|
|
47
|
+
const agents = await (0, api_1.listMyAgents)(config);
|
|
48
|
+
const matching = agents.filter(a => a.name === name);
|
|
49
|
+
if (matching.length === 0) {
|
|
50
|
+
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
51
|
+
}
|
|
52
|
+
let targetAgent;
|
|
53
|
+
if (version === 'latest') {
|
|
54
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const found = matching.find(a => a.version === version);
|
|
58
|
+
if (!found) {
|
|
59
|
+
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
60
|
+
}
|
|
61
|
+
targetAgent = found;
|
|
62
|
+
}
|
|
63
|
+
return { ...targetAgent, org_slug: org };
|
|
64
|
+
}
|
|
65
|
+
function registerInstallCommand(program) {
|
|
66
|
+
program
|
|
67
|
+
.command('install <agent>')
|
|
68
|
+
.description('Install agent as sub-agent (Claude Code, Cursor, etc.)')
|
|
69
|
+
.option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
|
|
70
|
+
.option('--scope <scope>', 'Install scope: user (home dir) or project (current dir)', 'user')
|
|
71
|
+
.option('--dry-run', 'Show what would be installed without making changes')
|
|
72
|
+
.action(async (agentArg, options) => {
|
|
73
|
+
const resolved = await (0, config_1.getResolvedConfig)();
|
|
74
|
+
const parsed = parseAgentRef(agentArg);
|
|
75
|
+
const org = parsed.org ?? resolved.defaultOrg;
|
|
76
|
+
if (!org) {
|
|
77
|
+
throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
|
|
78
|
+
}
|
|
79
|
+
// Determine target formats
|
|
80
|
+
let targetFormats = [];
|
|
81
|
+
if (options.format) {
|
|
82
|
+
targetFormats = options.format.split(',').map(f => f.trim());
|
|
83
|
+
const invalid = targetFormats.filter(f => !adapters_1.adapterRegistry.has(f));
|
|
84
|
+
if (invalid.length > 0) {
|
|
85
|
+
throw new errors_1.CliError(`Unknown format(s): ${invalid.join(', ')}. Available: ${adapters_1.adapterRegistry.getIds().join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const defaults = await (0, config_1.getDefaultFormats)();
|
|
90
|
+
if (defaults.length > 0) {
|
|
91
|
+
targetFormats = defaults;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// No default configured - use claude-code as sensible default
|
|
95
|
+
targetFormats = ['claude-code'];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Validate scope
|
|
99
|
+
const scope = options.scope;
|
|
100
|
+
if (scope !== 'user' && scope !== 'project') {
|
|
101
|
+
throw new errors_1.CliError('Scope must be "user" or "project"');
|
|
102
|
+
}
|
|
103
|
+
// Download agent
|
|
104
|
+
process.stdout.write(`Fetching ${org}/${parsed.name}@${parsed.version}...\n`);
|
|
105
|
+
const agent = await downloadAgentWithFallback(resolved, org, parsed.name, parsed.version);
|
|
106
|
+
// Install for each format
|
|
107
|
+
for (const formatId of targetFormats) {
|
|
108
|
+
const adapter = adapters_1.adapterRegistry.get(formatId);
|
|
109
|
+
if (!adapter) {
|
|
110
|
+
process.stderr.write(`Warning: Unknown format '${formatId}', skipping\n`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
// Check if can convert
|
|
114
|
+
const checkResult = adapter.canConvert(agent);
|
|
115
|
+
if (!checkResult.canConvert) {
|
|
116
|
+
process.stderr.write(`Cannot convert to ${adapter.name}:\n`);
|
|
117
|
+
for (const err of checkResult.errors) {
|
|
118
|
+
process.stderr.write(` - ${err}\n`);
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Show warnings
|
|
123
|
+
for (const warn of checkResult.warnings) {
|
|
124
|
+
process.stdout.write(`Warning (${formatId}): ${warn}\n`);
|
|
125
|
+
}
|
|
126
|
+
// Convert
|
|
127
|
+
const files = adapter.convert(agent);
|
|
128
|
+
// Determine base directory
|
|
129
|
+
const baseDir = scope === 'user' ? os_1.default.homedir() : process.cwd();
|
|
130
|
+
// Install each file
|
|
131
|
+
for (const file of files) {
|
|
132
|
+
const fullDir = path_1.default.join(baseDir, file.installPath);
|
|
133
|
+
const fullPath = path_1.default.join(fullDir, file.filename);
|
|
134
|
+
if (options.dryRun) {
|
|
135
|
+
process.stdout.write(`Would install: ${fullPath}\n`);
|
|
136
|
+
process.stdout.write(`Content preview:\n${file.content.slice(0, 500)}...\n\n`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
// Create directory and write file
|
|
140
|
+
await promises_1.default.mkdir(fullDir, { recursive: true });
|
|
141
|
+
await promises_1.default.writeFile(fullPath, file.content);
|
|
142
|
+
// Track installation
|
|
143
|
+
const installedAgent = {
|
|
144
|
+
agent: `${org}/${parsed.name}`,
|
|
145
|
+
version: parsed.version,
|
|
146
|
+
format: formatId,
|
|
147
|
+
scope,
|
|
148
|
+
path: fullPath,
|
|
149
|
+
installedAt: new Date().toISOString(),
|
|
150
|
+
adapterVersion: adapter.version,
|
|
151
|
+
contentHash: (0, installed_1.computeHash)(file.content),
|
|
152
|
+
};
|
|
153
|
+
await (0, installed_1.trackInstall)(installedAgent);
|
|
154
|
+
process.stdout.write(`Installed: ${fullPath}\n`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!options.dryRun) {
|
|
158
|
+
await (0, analytics_1.track)('cli_agent_install', {
|
|
159
|
+
agent: `${org}/${parsed.name}`,
|
|
160
|
+
formats: targetFormats,
|
|
161
|
+
scope,
|
|
162
|
+
});
|
|
163
|
+
process.stdout.write(`\nAgent installed successfully!\n`);
|
|
164
|
+
if (scope === 'user') {
|
|
165
|
+
process.stdout.write(`Available in all your projects.\n`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
process.stdout.write(`Available in this project only.\n`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
package/dist/commands/run.js
CHANGED
|
@@ -234,6 +234,58 @@ async function downloadDependenciesRecursively(config, depStatuses, visited = ne
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Detect all available LLM providers from environment and server.
|
|
239
|
+
* Returns array of provider configs for fallback support.
|
|
240
|
+
*/
|
|
241
|
+
async function detectAllLlmKeys(supportedProviders, config) {
|
|
242
|
+
const providers = [];
|
|
243
|
+
const seen = new Set();
|
|
244
|
+
// Check environment variables for all providers
|
|
245
|
+
for (const provider of supportedProviders) {
|
|
246
|
+
if (provider === 'any') {
|
|
247
|
+
// Check all known providers
|
|
248
|
+
for (const [p, envVar] of Object.entries(llm_1.PROVIDER_ENV_VARS)) {
|
|
249
|
+
const key = process.env[envVar];
|
|
250
|
+
if (key && !seen.has(p)) {
|
|
251
|
+
seen.add(p);
|
|
252
|
+
providers.push({ provider: p, apiKey: key, model: (0, llm_1.getDefaultModel)(p) });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const envVar = llm_1.PROVIDER_ENV_VARS[provider];
|
|
258
|
+
if (envVar) {
|
|
259
|
+
const key = process.env[envVar];
|
|
260
|
+
if (key && !seen.has(provider)) {
|
|
261
|
+
seen.add(provider);
|
|
262
|
+
providers.push({ provider, apiKey: key, model: (0, llm_1.getDefaultModel)(provider) });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Also check server keys if available
|
|
268
|
+
if (config?.apiKey) {
|
|
269
|
+
try {
|
|
270
|
+
const { fetchLlmKeys } = await Promise.resolve().then(() => __importStar(require('../lib/api')));
|
|
271
|
+
const serverKeys = await fetchLlmKeys(config);
|
|
272
|
+
for (const serverKey of serverKeys) {
|
|
273
|
+
if (!seen.has(serverKey.provider)) {
|
|
274
|
+
seen.add(serverKey.provider);
|
|
275
|
+
providers.push({
|
|
276
|
+
provider: serverKey.provider,
|
|
277
|
+
apiKey: serverKey.api_key,
|
|
278
|
+
model: serverKey.model || (0, llm_1.getDefaultModel)(serverKey.provider),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Server fetch failed, continue with what we have
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return providers;
|
|
288
|
+
}
|
|
237
289
|
async function executePromptLocally(agentData, inputData, skillPrompts = [], config, providerOverride) {
|
|
238
290
|
// If provider override specified, validate and use only that provider
|
|
239
291
|
if (providerOverride) {
|
|
@@ -243,6 +295,44 @@ async function executePromptLocally(agentData, inputData, skillPrompts = [], con
|
|
|
243
295
|
const providersToCheck = providerOverride
|
|
244
296
|
? [providerOverride]
|
|
245
297
|
: agentData.supported_providers;
|
|
298
|
+
// Combine skill prompts with agent prompt (skills first, then agent)
|
|
299
|
+
let basePrompt = agentData.prompt || '';
|
|
300
|
+
if (skillPrompts.length > 0) {
|
|
301
|
+
basePrompt = [...skillPrompts, basePrompt].join('\n\n---\n\n');
|
|
302
|
+
}
|
|
303
|
+
// Build the prompt with input data (matches server behavior)
|
|
304
|
+
const prompt = (0, llm_1.buildPrompt)(basePrompt, inputData);
|
|
305
|
+
// When no provider override, detect all available providers for fallback support
|
|
306
|
+
if (!providerOverride) {
|
|
307
|
+
const allProviders = await detectAllLlmKeys(providersToCheck, config);
|
|
308
|
+
if (allProviders.length === 0) {
|
|
309
|
+
const providers = providersToCheck.join(', ');
|
|
310
|
+
throw new errors_1.CliError(`No LLM key found for: ${providers}\n` +
|
|
311
|
+
`Set an environment variable (e.g., OPENAI_API_KEY) or configure in web dashboard`);
|
|
312
|
+
}
|
|
313
|
+
// Apply agent default models to each provider config
|
|
314
|
+
const providersWithModels = allProviders.map((p) => ({
|
|
315
|
+
...p,
|
|
316
|
+
model: agentData.default_models?.[p.provider] || p.model,
|
|
317
|
+
}));
|
|
318
|
+
// Show which provider is being used (primary)
|
|
319
|
+
const primary = providersWithModels[0];
|
|
320
|
+
if (providersWithModels.length > 1) {
|
|
321
|
+
process.stderr.write(`Running with ${primary.provider} (${primary.model}), ` +
|
|
322
|
+
`${providersWithModels.length - 1} fallback(s) available...\n`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
process.stderr.write(`Running with ${primary.provider} (${primary.model})...\n`);
|
|
326
|
+
}
|
|
327
|
+
// Use fallback if multiple providers, otherwise single call
|
|
328
|
+
if (providersWithModels.length > 1) {
|
|
329
|
+
return await (0, llm_1.callLlmWithFallback)(providersWithModels, prompt, agentData.output_schema);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
return await (0, llm_1.callLlm)(primary.provider, primary.apiKey, primary.model, prompt, agentData.output_schema);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Provider override: use single provider (existing behavior)
|
|
246
336
|
const detected = await (0, llm_1.detectLlmKey)(providersToCheck, config);
|
|
247
337
|
if (!detected) {
|
|
248
338
|
const providers = providersToCheck.join(', ');
|
|
@@ -254,13 +344,6 @@ async function executePromptLocally(agentData, inputData, skillPrompts = [], con
|
|
|
254
344
|
const model = serverModel || agentData.default_models?.[provider] || (0, llm_1.getDefaultModel)(provider);
|
|
255
345
|
// Show which provider is being used (helpful for debugging rate limits)
|
|
256
346
|
process.stderr.write(`Running with ${provider} (${model})...\n`);
|
|
257
|
-
// Combine skill prompts with agent prompt (skills first, then agent)
|
|
258
|
-
let basePrompt = agentData.prompt || '';
|
|
259
|
-
if (skillPrompts.length > 0) {
|
|
260
|
-
basePrompt = [...skillPrompts, basePrompt].join('\n\n---\n\n');
|
|
261
|
-
}
|
|
262
|
-
// Build the prompt with input data (matches server behavior)
|
|
263
|
-
const prompt = (0, llm_1.buildPrompt)(basePrompt, inputData);
|
|
264
347
|
// Call the LLM directly
|
|
265
348
|
const response = await (0, llm_1.callLlm)(provider, key, model, prompt, agentData.output_schema);
|
|
266
349
|
return response;
|
package/dist/commands/skill.js
CHANGED
|
@@ -162,8 +162,29 @@ Instructions and guidance for AI agents...
|
|
|
162
162
|
.description('Install skill to local AI tool directories (Claude Code, Cursor, etc.)')
|
|
163
163
|
.option('--global', 'Install to home directory (default: current directory)')
|
|
164
164
|
.option('--dry-run', 'Show what would be installed without making changes')
|
|
165
|
+
.option('--format <formats>', 'Comma-separated format IDs (e.g., claude-code,cursor)')
|
|
165
166
|
.action(async (skillRef, options) => {
|
|
166
167
|
const resolved = await (0, config_1.getResolvedConfig)();
|
|
168
|
+
// Determine target formats
|
|
169
|
+
let targetFormats = [];
|
|
170
|
+
if (options.format) {
|
|
171
|
+
targetFormats = options.format.split(',').map((f) => f.trim());
|
|
172
|
+
// Validate format IDs
|
|
173
|
+
const invalid = targetFormats.filter(f => !config_1.VALID_FORMAT_IDS.includes(f));
|
|
174
|
+
if (invalid.length > 0) {
|
|
175
|
+
throw new errors_1.CliError(`Invalid format ID(s): ${invalid.join(', ')}. Valid: ${config_1.VALID_FORMAT_IDS.join(', ')}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const defaults = await (0, config_1.getDefaultFormats)();
|
|
180
|
+
if (defaults.length > 0) {
|
|
181
|
+
targetFormats = defaults;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Determine target directories
|
|
185
|
+
const toolDirs = targetFormats.length > 0
|
|
186
|
+
? targetFormats.map(f => config_1.FORMAT_SKILL_DIRS[f])
|
|
187
|
+
: AI_TOOL_SKILL_DIRS; // fallback to all
|
|
167
188
|
const parsed = parseSkillRef(skillRef);
|
|
168
189
|
const org = parsed.org ?? resolved.defaultOrg;
|
|
169
190
|
if (!org) {
|
|
@@ -191,7 +212,7 @@ ${skillData.prompt}
|
|
|
191
212
|
if (options.dryRun) {
|
|
192
213
|
process.stdout.write(`Would install ${org}/${parsed.skill}@${parsed.version}\n\n`);
|
|
193
214
|
process.stdout.write(`Target directories:\n`);
|
|
194
|
-
for (const tool of
|
|
215
|
+
for (const tool of toolDirs) {
|
|
195
216
|
const skillDir = path_1.default.join(baseDir, tool.path);
|
|
196
217
|
const skillFile = path_1.default.join(skillDir, `${parsed.skill}.md`);
|
|
197
218
|
process.stdout.write(` - ${tool.name}: ${skillFile}\n`);
|
|
@@ -199,9 +220,9 @@ ${skillData.prompt}
|
|
|
199
220
|
process.stdout.write(`\nNo changes made (dry run)\n`);
|
|
200
221
|
return;
|
|
201
222
|
}
|
|
202
|
-
// Install to
|
|
223
|
+
// Install to target AI tool directories
|
|
203
224
|
const installed = [];
|
|
204
|
-
for (const tool of
|
|
225
|
+
for (const tool of toolDirs) {
|
|
205
226
|
const skillDir = path_1.default.join(baseDir, tool.path);
|
|
206
227
|
const skillFile = path_1.default.join(skillDir, `${parsed.skill}.md`);
|
|
207
228
|
try {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerUpdateCommand = registerUpdateCommand;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const config_1 = require("../lib/config");
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
const analytics_1 = require("../lib/analytics");
|
|
13
|
+
const adapters_1 = require("../adapters");
|
|
14
|
+
const installed_1 = require("../lib/installed");
|
|
15
|
+
async function fetchLatestAgent(config, agentRef) {
|
|
16
|
+
const [org, name] = agentRef.split('/');
|
|
17
|
+
if (!org || !name)
|
|
18
|
+
return null;
|
|
19
|
+
try {
|
|
20
|
+
// Try to get latest version
|
|
21
|
+
const agent = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${name}/latest`);
|
|
22
|
+
return {
|
|
23
|
+
agent: { ...agent, org_slug: org },
|
|
24
|
+
latestVersion: agent.version
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err instanceof api_1.ApiError && err.status === 404) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function registerUpdateCommand(program) {
|
|
35
|
+
program
|
|
36
|
+
.command('update [agent]')
|
|
37
|
+
.description('Update installed agents to latest versions')
|
|
38
|
+
.option('--check', 'Check for updates without installing')
|
|
39
|
+
.option('--force', 'Force update even if file was modified locally')
|
|
40
|
+
.action(async (agentRef, options) => {
|
|
41
|
+
const resolved = await (0, config_1.getResolvedConfig)();
|
|
42
|
+
const installed = await (0, installed_1.getInstalled)();
|
|
43
|
+
if (installed.length === 0) {
|
|
44
|
+
process.stdout.write('No agents installed. Use "orch install <agent>" to install agents.\n');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Filter to specific agent if provided
|
|
48
|
+
const toCheck = agentRef
|
|
49
|
+
? installed.filter(i => i.agent === agentRef)
|
|
50
|
+
: installed;
|
|
51
|
+
if (toCheck.length === 0) {
|
|
52
|
+
process.stdout.write(`Agent "${agentRef}" is not installed.\n`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
process.stdout.write(`Checking ${toCheck.length} installed agent(s) for updates...\n\n`);
|
|
56
|
+
let updatesAvailable = 0;
|
|
57
|
+
let updatesApplied = 0;
|
|
58
|
+
let skippedModified = 0;
|
|
59
|
+
for (const item of toCheck) {
|
|
60
|
+
// Check if file was modified locally
|
|
61
|
+
const wasModified = await (0, installed_1.checkModified)(item);
|
|
62
|
+
// Fetch latest version
|
|
63
|
+
const latest = await fetchLatestAgent(resolved, item.agent);
|
|
64
|
+
if (!latest) {
|
|
65
|
+
process.stdout.write(` ${chalk_1.default.yellow('?')} ${item.agent} - could not fetch latest\n`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const hasUpdate = latest.latestVersion !== item.version;
|
|
69
|
+
if (!hasUpdate && !wasModified) {
|
|
70
|
+
process.stdout.write(` ${chalk_1.default.green('✓')} ${item.agent}@${item.version} - up to date\n`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (wasModified && !options.force) {
|
|
74
|
+
process.stdout.write(` ${chalk_1.default.yellow('!')} ${item.agent} - local modifications (use --force to overwrite)\n`);
|
|
75
|
+
skippedModified++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (hasUpdate) {
|
|
79
|
+
updatesAvailable++;
|
|
80
|
+
process.stdout.write(` ${chalk_1.default.blue('↑')} ${item.agent}@${item.version} → ${latest.latestVersion}`);
|
|
81
|
+
if (wasModified) {
|
|
82
|
+
process.stdout.write(` ${chalk_1.default.yellow('(modified)')}`);
|
|
83
|
+
}
|
|
84
|
+
process.stdout.write('\n');
|
|
85
|
+
if (options.check) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
// Apply update
|
|
89
|
+
const adapter = adapters_1.adapterRegistry.get(item.format);
|
|
90
|
+
if (!adapter) {
|
|
91
|
+
process.stderr.write(` Skipped: Unknown format "${item.format}"\n`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const checkResult = adapter.canConvert(latest.agent);
|
|
95
|
+
if (!checkResult.canConvert) {
|
|
96
|
+
process.stderr.write(` Skipped: ${checkResult.errors.join(', ')}\n`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const files = adapter.convert(latest.agent);
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
// Use the original path from tracking
|
|
102
|
+
const fullPath = item.path;
|
|
103
|
+
try {
|
|
104
|
+
const dir = path_1.default.dirname(fullPath);
|
|
105
|
+
await promises_1.default.mkdir(dir, { recursive: true });
|
|
106
|
+
await promises_1.default.writeFile(fullPath, file.content);
|
|
107
|
+
// Update tracking
|
|
108
|
+
const updatedItem = {
|
|
109
|
+
...item,
|
|
110
|
+
version: latest.latestVersion,
|
|
111
|
+
installedAt: new Date().toISOString(),
|
|
112
|
+
adapterVersion: adapter.version,
|
|
113
|
+
contentHash: (0, installed_1.computeHash)(file.content),
|
|
114
|
+
};
|
|
115
|
+
await (0, installed_1.trackInstall)(updatedItem);
|
|
116
|
+
process.stdout.write(` Updated: ${fullPath}\n`);
|
|
117
|
+
updatesApplied++;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
process.stderr.write(` Error: ${err.message}\n`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
process.stdout.write('\n');
|
|
126
|
+
if (options.check) {
|
|
127
|
+
process.stdout.write(`Found ${updatesAvailable} update(s) available.\n`);
|
|
128
|
+
if (skippedModified > 0) {
|
|
129
|
+
process.stdout.write(`${skippedModified} agent(s) have local modifications.\n`);
|
|
130
|
+
}
|
|
131
|
+
process.stdout.write('Run "orch update" without --check to apply updates.\n');
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
process.stdout.write(`Applied ${updatesApplied} update(s).\n`);
|
|
135
|
+
if (skippedModified > 0) {
|
|
136
|
+
process.stdout.write(`Skipped ${skippedModified} modified agent(s). Use --force to overwrite.\n`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
await (0, analytics_1.track)('cli_agent_update', {
|
|
140
|
+
checked: toCheck.length,
|
|
141
|
+
updatesAvailable,
|
|
142
|
+
updatesApplied,
|
|
143
|
+
skippedModified,
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
package/dist/lib/api.js
CHANGED
|
@@ -59,6 +59,8 @@ exports.previewAgentVersion = previewAgentVersion;
|
|
|
59
59
|
exports.reportInstall = reportInstall;
|
|
60
60
|
const errors_1 = require("./errors");
|
|
61
61
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
62
|
+
const MAX_RETRIES = 3;
|
|
63
|
+
const BASE_DELAY_MS = 1000;
|
|
62
64
|
async function safeFetch(url, options) {
|
|
63
65
|
try {
|
|
64
66
|
return await fetch(url, {
|
|
@@ -74,6 +76,39 @@ async function safeFetch(url, options) {
|
|
|
74
76
|
throw new errors_1.NetworkError(url, err instanceof Error ? err : undefined);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
79
|
+
async function safeFetchWithRetry(url, options) {
|
|
80
|
+
let lastError;
|
|
81
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
const response = await safeFetch(url, options);
|
|
84
|
+
// Don't retry client errors (except 429)
|
|
85
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
// Retry on 5xx or 429
|
|
89
|
+
if (response.status >= 500 || response.status === 429) {
|
|
90
|
+
if (attempt < MAX_RETRIES) {
|
|
91
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1);
|
|
92
|
+
const jitter = Math.random() * 500;
|
|
93
|
+
process.stderr.write(`Request failed (${response.status}), retrying in ${Math.round((delay + jitter) / 1000)}s...\n`);
|
|
94
|
+
await new Promise(r => setTimeout(r, delay + jitter));
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return response;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
lastError = error;
|
|
102
|
+
if (attempt < MAX_RETRIES) {
|
|
103
|
+
const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1);
|
|
104
|
+
const jitter = Math.random() * 500;
|
|
105
|
+
process.stderr.write(`Network error, retrying in ${Math.round((delay + jitter) / 1000)}s...\n`);
|
|
106
|
+
await new Promise(r => setTimeout(r, delay + jitter));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
throw lastError ?? new errors_1.NetworkError(url);
|
|
111
|
+
}
|
|
77
112
|
class ApiError extends Error {
|
|
78
113
|
status;
|
|
79
114
|
payload;
|
|
@@ -108,7 +143,7 @@ async function request(config, method, path, options = {}) {
|
|
|
108
143
|
if (!config.apiKey) {
|
|
109
144
|
throw new ApiError('Missing API key. Run `orchagent login` first.', 401);
|
|
110
145
|
}
|
|
111
|
-
const response = await
|
|
146
|
+
const response = await safeFetchWithRetry(buildUrl(config.apiUrl, path), {
|
|
112
147
|
method,
|
|
113
148
|
headers: {
|
|
114
149
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -122,7 +157,7 @@ async function request(config, method, path, options = {}) {
|
|
|
122
157
|
return (await response.json());
|
|
123
158
|
}
|
|
124
159
|
async function publicRequest(config, path) {
|
|
125
|
-
const response = await
|
|
160
|
+
const response = await safeFetchWithRetry(buildUrl(config.apiUrl, path));
|
|
126
161
|
if (!response.ok) {
|
|
127
162
|
throw await parseError(response);
|
|
128
163
|
}
|
package/dist/lib/config.js
CHANGED
|
@@ -3,16 +3,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FORMAT_SKILL_DIRS = exports.VALID_FORMAT_IDS = void 0;
|
|
6
7
|
exports.loadConfig = loadConfig;
|
|
7
8
|
exports.saveConfig = saveConfig;
|
|
8
9
|
exports.getResolvedConfig = getResolvedConfig;
|
|
9
10
|
exports.getConfigPath = getConfigPath;
|
|
11
|
+
exports.getDefaultFormats = getDefaultFormats;
|
|
12
|
+
exports.setDefaultFormats = setDefaultFormats;
|
|
10
13
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
14
|
const path_1 = __importDefault(require("path"));
|
|
12
15
|
const os_1 = __importDefault(require("os"));
|
|
13
16
|
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
|
|
14
17
|
const CONFIG_PATH = path_1.default.join(CONFIG_DIR, 'config.json');
|
|
15
18
|
const DEFAULT_API_URL = 'https://api.orchagent.io';
|
|
19
|
+
// Valid format IDs for multi-format agent export
|
|
20
|
+
exports.VALID_FORMAT_IDS = ['claude-code', 'cursor', 'codex', 'amp', 'opencode', 'antigravity'];
|
|
21
|
+
// Map format IDs to skill directories (used by skill install and agent install)
|
|
22
|
+
exports.FORMAT_SKILL_DIRS = {
|
|
23
|
+
'claude-code': { name: 'Claude Code', path: '.claude/skills' },
|
|
24
|
+
'cursor': { name: 'Cursor', path: '.cursor/skills' },
|
|
25
|
+
'codex': { name: 'Codex', path: '.codex/skills' },
|
|
26
|
+
'amp': { name: 'Amp', path: '.agents/skills' },
|
|
27
|
+
'opencode': { name: 'OpenCode', path: '.opencode/skill' },
|
|
28
|
+
'antigravity': { name: 'Antigravity', path: '.agent/skills' },
|
|
29
|
+
};
|
|
16
30
|
async function loadConfig() {
|
|
17
31
|
try {
|
|
18
32
|
const raw = await promises_1.default.readFile(CONFIG_PATH, 'utf-8');
|
|
@@ -56,3 +70,12 @@ async function getResolvedConfig(overrides = {}, profile) {
|
|
|
56
70
|
function getConfigPath() {
|
|
57
71
|
return CONFIG_PATH;
|
|
58
72
|
}
|
|
73
|
+
async function getDefaultFormats() {
|
|
74
|
+
const config = await loadConfig();
|
|
75
|
+
return config.default_formats ?? [];
|
|
76
|
+
}
|
|
77
|
+
async function setDefaultFormats(formats) {
|
|
78
|
+
const config = await loadConfig();
|
|
79
|
+
config.default_formats = formats;
|
|
80
|
+
await saveConfig(config);
|
|
81
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.computeHash = computeHash;
|
|
7
|
+
exports.loadInstalled = loadInstalled;
|
|
8
|
+
exports.saveInstalled = saveInstalled;
|
|
9
|
+
exports.trackInstall = trackInstall;
|
|
10
|
+
exports.getInstalled = getInstalled;
|
|
11
|
+
exports.getInstalledByFormat = getInstalledByFormat;
|
|
12
|
+
exports.checkModified = checkModified;
|
|
13
|
+
exports.untrackInstall = untrackInstall;
|
|
14
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const os_1 = __importDefault(require("os"));
|
|
17
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
18
|
+
const ORCHAGENT_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
|
|
19
|
+
const INSTALLED_PATH = path_1.default.join(ORCHAGENT_DIR, 'installed.json');
|
|
20
|
+
/**
|
|
21
|
+
* Compute SHA-256 hash of content
|
|
22
|
+
*/
|
|
23
|
+
function computeHash(content) {
|
|
24
|
+
return crypto_1.default.createHash('sha256').update(content).digest('hex');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Load installed agents from tracking file
|
|
28
|
+
*/
|
|
29
|
+
async function loadInstalled() {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await promises_1.default.readFile(INSTALLED_PATH, 'utf-8');
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err.code === 'ENOENT') {
|
|
36
|
+
return { version: 1, installed: [] };
|
|
37
|
+
}
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Save installed agents to tracking file
|
|
43
|
+
*/
|
|
44
|
+
async function saveInstalled(data) {
|
|
45
|
+
await promises_1.default.mkdir(ORCHAGENT_DIR, { recursive: true });
|
|
46
|
+
await promises_1.default.writeFile(INSTALLED_PATH, JSON.stringify(data, null, 2) + '\n');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Track a newly installed agent
|
|
50
|
+
*/
|
|
51
|
+
async function trackInstall(agent) {
|
|
52
|
+
const data = await loadInstalled();
|
|
53
|
+
// Remove any existing entry for same agent/format/scope/path
|
|
54
|
+
data.installed = data.installed.filter(i => !(i.agent === agent.agent && i.format === agent.format && i.path === agent.path));
|
|
55
|
+
// Add new entry
|
|
56
|
+
data.installed.push(agent);
|
|
57
|
+
await saveInstalled(data);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get all installed agents
|
|
61
|
+
*/
|
|
62
|
+
async function getInstalled() {
|
|
63
|
+
const data = await loadInstalled();
|
|
64
|
+
return data.installed;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get installed agents filtered by format
|
|
68
|
+
*/
|
|
69
|
+
async function getInstalledByFormat(format) {
|
|
70
|
+
const data = await loadInstalled();
|
|
71
|
+
return data.installed.filter(i => i.format === format);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a file has been modified since installation
|
|
75
|
+
*/
|
|
76
|
+
async function checkModified(installed) {
|
|
77
|
+
try {
|
|
78
|
+
const content = await promises_1.default.readFile(installed.path, 'utf-8');
|
|
79
|
+
const currentHash = computeHash(content);
|
|
80
|
+
return currentHash !== installed.contentHash;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// File doesn't exist or can't be read
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Remove an installed agent from tracking
|
|
89
|
+
*/
|
|
90
|
+
async function untrackInstall(agentRef, format, filePath) {
|
|
91
|
+
const data = await loadInstalled();
|
|
92
|
+
data.installed = data.installed.filter(i => !(i.agent === agentRef && i.format === format && i.path === filePath));
|
|
93
|
+
await saveInstalled(data);
|
|
94
|
+
}
|
package/dist/lib/llm.js
CHANGED
|
@@ -39,15 +39,28 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.DEFAULT_MODELS = exports.PROVIDER_ENV_VARS = void 0;
|
|
42
|
+
exports.DEFAULT_MODELS = exports.PROVIDER_ENV_VARS = exports.LlmError = void 0;
|
|
43
|
+
exports.isRateLimitError = isRateLimitError;
|
|
43
44
|
exports.detectLlmKeyFromEnv = detectLlmKeyFromEnv;
|
|
44
45
|
exports.detectLlmKey = detectLlmKey;
|
|
45
46
|
exports.getDefaultModel = getDefaultModel;
|
|
46
47
|
exports.buildPrompt = buildPrompt;
|
|
47
48
|
exports.callLlm = callLlm;
|
|
49
|
+
exports.callLlmWithFallback = callLlmWithFallback;
|
|
48
50
|
exports.validateProvider = validateProvider;
|
|
49
51
|
const errors_1 = require("./errors");
|
|
50
52
|
const llm_errors_1 = require("./llm-errors");
|
|
53
|
+
class LlmError extends errors_1.CliError {
|
|
54
|
+
statusCode;
|
|
55
|
+
constructor(message, statusCode) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.statusCode = statusCode;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.LlmError = LlmError;
|
|
61
|
+
function isRateLimitError(error) {
|
|
62
|
+
return error instanceof LlmError && error.statusCode === 429;
|
|
63
|
+
}
|
|
51
64
|
// Environment variable names for each provider
|
|
52
65
|
exports.PROVIDER_ENV_VARS = {
|
|
53
66
|
openai: 'OPENAI_API_KEY',
|
|
@@ -169,6 +182,27 @@ async function callLlm(provider, apiKey, model, prompt, outputSchema) {
|
|
|
169
182
|
}
|
|
170
183
|
throw new errors_1.CliError(`Unsupported provider: ${provider}`);
|
|
171
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Call LLM with automatic fallback to next provider on rate limit.
|
|
187
|
+
* Tries each provider in order until one succeeds.
|
|
188
|
+
*/
|
|
189
|
+
async function callLlmWithFallback(providers, prompt, outputSchema) {
|
|
190
|
+
let lastError;
|
|
191
|
+
for (const { provider, apiKey, model } of providers) {
|
|
192
|
+
try {
|
|
193
|
+
return await callLlm(provider, apiKey, model, prompt, outputSchema);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
lastError = error;
|
|
197
|
+
if (isRateLimitError(error)) {
|
|
198
|
+
process.stderr.write(`${provider} rate-limited, trying next provider...\n`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
throw error; // Don't retry non-rate-limit errors
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
throw lastError ?? new errors_1.CliError('All LLM providers failed');
|
|
205
|
+
}
|
|
172
206
|
async function callOpenAI(apiKey, model, prompt, outputSchema) {
|
|
173
207
|
const body = {
|
|
174
208
|
model,
|
|
@@ -187,7 +221,8 @@ async function callOpenAI(apiKey, model, prompt, outputSchema) {
|
|
|
187
221
|
});
|
|
188
222
|
if (!response.ok) {
|
|
189
223
|
const text = await response.text();
|
|
190
|
-
|
|
224
|
+
const parsed = (0, llm_errors_1.parseLlmError)('openai', text, response.status);
|
|
225
|
+
throw new LlmError(parsed.message, response.status);
|
|
191
226
|
}
|
|
192
227
|
const data = (await response.json());
|
|
193
228
|
const content = data.choices?.[0]?.message?.content || '';
|
|
@@ -214,7 +249,8 @@ async function callAnthropic(apiKey, model, prompt, _outputSchema) {
|
|
|
214
249
|
});
|
|
215
250
|
if (!response.ok) {
|
|
216
251
|
const text = await response.text();
|
|
217
|
-
|
|
252
|
+
const parsed = (0, llm_errors_1.parseLlmError)('anthropic', text, response.status);
|
|
253
|
+
throw new LlmError(parsed.message, response.status);
|
|
218
254
|
}
|
|
219
255
|
const data = (await response.json());
|
|
220
256
|
const content = data.content?.[0]?.text || '';
|
|
@@ -236,7 +272,8 @@ async function callGemini(apiKey, model, prompt, _outputSchema) {
|
|
|
236
272
|
});
|
|
237
273
|
if (!response.ok) {
|
|
238
274
|
const text = await response.text();
|
|
239
|
-
|
|
275
|
+
const parsed = (0, llm_errors_1.parseLlmError)('gemini', text, response.status);
|
|
276
|
+
throw new LlmError(parsed.message, response.status);
|
|
240
277
|
}
|
|
241
278
|
const data = (await response.json());
|
|
242
279
|
const content = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
@@ -264,10 +301,11 @@ async function callOllama(endpoint, model, prompt, _outputSchema) {
|
|
|
264
301
|
});
|
|
265
302
|
if (!response.ok) {
|
|
266
303
|
if (response.status === 404) {
|
|
267
|
-
throw new
|
|
304
|
+
throw new LlmError(`Model '${model}' not found. Run: ollama pull ${model}`, 404);
|
|
268
305
|
}
|
|
269
306
|
const text = await response.text();
|
|
270
|
-
|
|
307
|
+
const parsed = (0, llm_errors_1.parseLlmError)('ollama', text, response.status);
|
|
308
|
+
throw new LlmError(parsed.message, response.status);
|
|
271
309
|
}
|
|
272
310
|
const data = await response.json();
|
|
273
311
|
const content = data.choices?.[0]?.message?.content || '';
|