@orchagent/cli 0.1.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/README.md +18 -0
- package/dist/commands/agents.js +26 -0
- package/dist/commands/call.js +264 -0
- package/dist/commands/fork.js +42 -0
- package/dist/commands/index.js +33 -0
- package/dist/commands/info.js +88 -0
- package/dist/commands/init.js +101 -0
- package/dist/commands/keys.js +172 -0
- package/dist/commands/llm-config.js +40 -0
- package/dist/commands/login.js +94 -0
- package/dist/commands/publish.js +192 -0
- package/dist/commands/publish.test.js +475 -0
- package/dist/commands/run.js +421 -0
- package/dist/commands/run.test.js +330 -0
- package/dist/commands/search.js +46 -0
- package/dist/commands/skill.js +141 -0
- package/dist/commands/star.js +41 -0
- package/dist/commands/whoami.js +17 -0
- package/dist/index.js +55 -0
- package/dist/lib/analytics.js +27 -0
- package/dist/lib/api.js +179 -0
- package/dist/lib/api.test.js +230 -0
- package/dist/lib/browser-auth.js +278 -0
- package/dist/lib/bundle.js +213 -0
- package/dist/lib/config.js +54 -0
- package/dist/lib/config.test.js +144 -0
- package/dist/lib/errors.js +75 -0
- package/dist/lib/llm.js +252 -0
- package/dist/lib/output.js +50 -0
- package/dist/types.js +2 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# OrchAgent CLI
|
|
2
|
+
|
|
3
|
+
Minimal CLI for interacting with the OrchAgent platform.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `orchagent login` - store an API key locally
|
|
8
|
+
- `orchagent call <agent> [file]` - call an agent endpoint
|
|
9
|
+
- `orchagent agents` - list public agents
|
|
10
|
+
|
|
11
|
+
## Development
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd cli
|
|
15
|
+
bun install
|
|
16
|
+
bun run build
|
|
17
|
+
node dist/index.js --help
|
|
18
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAgentsCommand = registerAgentsCommand;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const api_1 = require("../lib/api");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
function registerAgentsCommand(program) {
|
|
8
|
+
program
|
|
9
|
+
.command('agents')
|
|
10
|
+
.description('List public agents')
|
|
11
|
+
.option('--filter <text>', 'Filter by name (case-insensitive)')
|
|
12
|
+
.option('--json', 'Output raw JSON')
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
15
|
+
const agents = await (0, api_1.listPublicAgents)(config);
|
|
16
|
+
const filter = options.filter?.toLowerCase();
|
|
17
|
+
const filtered = filter
|
|
18
|
+
? agents.filter((agent) => `${agent.org_slug}/${agent.name}`.toLowerCase().includes(filter))
|
|
19
|
+
: agents;
|
|
20
|
+
if (options.json) {
|
|
21
|
+
(0, output_1.printJson)(filtered);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
(0, output_1.printAgentsTable)(filtered);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
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.registerCallCommand = registerCallCommand;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const config_1 = require("../lib/config");
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
const errors_1 = require("../lib/errors");
|
|
12
|
+
const output_1 = require("../lib/output");
|
|
13
|
+
const llm_1 = require("../lib/llm");
|
|
14
|
+
const analytics_1 = require("../lib/analytics");
|
|
15
|
+
const DEFAULT_VERSION = 'v1';
|
|
16
|
+
function parseAgentRef(value) {
|
|
17
|
+
const [ref, versionPart] = value.split('@');
|
|
18
|
+
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
19
|
+
const segments = ref.split('/');
|
|
20
|
+
if (segments.length === 1) {
|
|
21
|
+
return { agent: segments[0], version };
|
|
22
|
+
}
|
|
23
|
+
if (segments.length === 2) {
|
|
24
|
+
return { org: segments[0], agent: segments[1], version };
|
|
25
|
+
}
|
|
26
|
+
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
27
|
+
}
|
|
28
|
+
async function readStdin() {
|
|
29
|
+
if (process.stdin.isTTY)
|
|
30
|
+
return null;
|
|
31
|
+
const chunks = [];
|
|
32
|
+
for await (const chunk of process.stdin) {
|
|
33
|
+
chunks.push(Buffer.from(chunk));
|
|
34
|
+
}
|
|
35
|
+
if (!chunks.length)
|
|
36
|
+
return null;
|
|
37
|
+
return Buffer.concat(chunks);
|
|
38
|
+
}
|
|
39
|
+
async function buildMultipartBody(filePaths, metadata) {
|
|
40
|
+
if (!filePaths || filePaths.length === 0) {
|
|
41
|
+
const stdinData = await readStdin();
|
|
42
|
+
if (stdinData) {
|
|
43
|
+
const form = new FormData();
|
|
44
|
+
form.append('files[]', new Blob([new Uint8Array(stdinData)]), 'stdin');
|
|
45
|
+
if (metadata) {
|
|
46
|
+
form.append('metadata', metadata);
|
|
47
|
+
}
|
|
48
|
+
return { body: form, sourceLabel: 'stdin' };
|
|
49
|
+
}
|
|
50
|
+
if (metadata) {
|
|
51
|
+
const form = new FormData();
|
|
52
|
+
form.append('metadata', metadata);
|
|
53
|
+
return { body: form, sourceLabel: 'metadata' };
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
const form = new FormData();
|
|
58
|
+
for (const filePath of filePaths) {
|
|
59
|
+
const buffer = await promises_1.default.readFile(filePath);
|
|
60
|
+
const filename = path_1.default.basename(filePath);
|
|
61
|
+
form.append('files[]', new Blob([new Uint8Array(buffer)]), filename);
|
|
62
|
+
}
|
|
63
|
+
if (metadata) {
|
|
64
|
+
form.append('metadata', metadata);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
body: form,
|
|
68
|
+
sourceLabel: filePaths.length === 1 ? filePaths[0] : `${filePaths.length} files`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async function resolveJsonBody(input) {
|
|
72
|
+
let raw = input;
|
|
73
|
+
if (input.startsWith('@')) {
|
|
74
|
+
const source = input.slice(1);
|
|
75
|
+
if (!source) {
|
|
76
|
+
throw new errors_1.CliError('Invalid JSON input. Use a JSON string or @file.');
|
|
77
|
+
}
|
|
78
|
+
if (source === '-') {
|
|
79
|
+
const stdinData = await readStdin();
|
|
80
|
+
if (!stdinData) {
|
|
81
|
+
throw new errors_1.CliError('No stdin provided for JSON input.');
|
|
82
|
+
}
|
|
83
|
+
raw = stdinData.toString('utf8');
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
raw = await promises_1.default.readFile(source, 'utf8');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return JSON.stringify(JSON.parse(raw));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
throw new errors_1.CliError('Invalid JSON input. Provide a valid JSON string or @file.');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function registerCallCommand(program) {
|
|
97
|
+
program
|
|
98
|
+
.command('call <agent> [file]')
|
|
99
|
+
.description('Call an agent endpoint')
|
|
100
|
+
.option('--endpoint <endpoint>', 'Override agent endpoint')
|
|
101
|
+
.option('--tenant <tenant>', 'Tenant identifier for multi-tenant callers')
|
|
102
|
+
.option('--data <json>', 'JSON payload (string or @file, @- for stdin)')
|
|
103
|
+
.option('--key <key>', 'LLM API key (overrides env vars)')
|
|
104
|
+
.option('--provider <provider>', 'LLM provider (openai, anthropic, gemini)')
|
|
105
|
+
.option('--json', 'Output raw JSON')
|
|
106
|
+
.option('--output <file>', 'Save response body to a file')
|
|
107
|
+
.option('--skills <skills>', 'Add skills (comma-separated)')
|
|
108
|
+
.option('--skills-only <skills>', 'Use only these skills')
|
|
109
|
+
.option('--no-skills', 'Ignore default skills')
|
|
110
|
+
.option('--file <path...>', 'File(s) to upload (can specify multiple)')
|
|
111
|
+
.option('--metadata <json>', 'JSON metadata to send with files')
|
|
112
|
+
.action(async (agentRef, file, options) => {
|
|
113
|
+
const resolved = await (0, config_1.getResolvedConfig)();
|
|
114
|
+
if (!resolved.apiKey) {
|
|
115
|
+
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
116
|
+
}
|
|
117
|
+
const parsed = parseAgentRef(agentRef);
|
|
118
|
+
const org = parsed.org ?? resolved.defaultOrg;
|
|
119
|
+
if (!org) {
|
|
120
|
+
throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
|
|
121
|
+
}
|
|
122
|
+
const agentMeta = await (0, api_1.getPublicAgent)(resolved, org, parsed.agent, parsed.version);
|
|
123
|
+
const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
|
|
124
|
+
const headers = {
|
|
125
|
+
Authorization: `Bearer ${resolved.apiKey}`,
|
|
126
|
+
};
|
|
127
|
+
if (options.tenant) {
|
|
128
|
+
headers['X-OrchAgent-Tenant'] = options.tenant;
|
|
129
|
+
}
|
|
130
|
+
const supportedProviders = agentMeta.supported_providers || ['any'];
|
|
131
|
+
let llmKey;
|
|
132
|
+
let llmProvider;
|
|
133
|
+
if (options.key) {
|
|
134
|
+
// Explicit key provided - require provider
|
|
135
|
+
if (!options.provider) {
|
|
136
|
+
throw new errors_1.CliError('When using --key, you must also specify --provider (openai, anthropic, or gemini)');
|
|
137
|
+
}
|
|
138
|
+
(0, llm_1.validateProvider)(options.provider);
|
|
139
|
+
llmKey = options.key;
|
|
140
|
+
llmProvider = options.provider;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Try to detect from environment or server
|
|
144
|
+
const detected = await (0, llm_1.detectLlmKey)(supportedProviders, resolved);
|
|
145
|
+
if (detected) {
|
|
146
|
+
llmKey = detected.key;
|
|
147
|
+
llmProvider = detected.provider;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Add LLM key to headers
|
|
151
|
+
if (llmKey) {
|
|
152
|
+
headers['X-LLM-API-Key'] = llmKey;
|
|
153
|
+
if (llmProvider) {
|
|
154
|
+
headers['X-LLM-Provider'] = llmProvider;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (agentMeta.type === 'prompt') {
|
|
158
|
+
// Warn if no key found for prompt-based agent
|
|
159
|
+
const providerList = supportedProviders.join(', ');
|
|
160
|
+
process.stderr.write(`Warning: No LLM key found for providers: ${providerList}\n` +
|
|
161
|
+
`Set an env var (e.g., OPENAI_API_KEY), use --key, or configure in web dashboard\n\n`);
|
|
162
|
+
}
|
|
163
|
+
// Add skill headers
|
|
164
|
+
if (options.skills) {
|
|
165
|
+
headers['X-OrchAgent-Skills'] = options.skills;
|
|
166
|
+
}
|
|
167
|
+
if (options.skillsOnly) {
|
|
168
|
+
headers['X-OrchAgent-Skills-Only'] = options.skillsOnly;
|
|
169
|
+
}
|
|
170
|
+
if (options.noSkills) {
|
|
171
|
+
headers['X-OrchAgent-No-Skills'] = 'true';
|
|
172
|
+
}
|
|
173
|
+
let body;
|
|
174
|
+
let sourceLabel;
|
|
175
|
+
const filePaths = [
|
|
176
|
+
...(options.file ?? []),
|
|
177
|
+
...(file ? [file] : []),
|
|
178
|
+
];
|
|
179
|
+
if (options.data) {
|
|
180
|
+
if (filePaths.length > 0 || options.metadata) {
|
|
181
|
+
throw new errors_1.CliError('Cannot use --data with file uploads or --metadata.');
|
|
182
|
+
}
|
|
183
|
+
body = await resolveJsonBody(options.data);
|
|
184
|
+
headers['Content-Type'] = 'application/json';
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const multipart = await buildMultipartBody(filePaths.length ? filePaths : undefined, options.metadata);
|
|
188
|
+
body = multipart.body;
|
|
189
|
+
sourceLabel = multipart.sourceLabel;
|
|
190
|
+
}
|
|
191
|
+
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${parsed.agent}/${parsed.version}/${endpoint}`;
|
|
192
|
+
const response = await fetch(url, {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers,
|
|
195
|
+
body,
|
|
196
|
+
});
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
const text = await response.text();
|
|
199
|
+
let payload;
|
|
200
|
+
try {
|
|
201
|
+
payload = JSON.parse(text);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
payload = text;
|
|
205
|
+
}
|
|
206
|
+
// Handle specific error codes with helpful messages
|
|
207
|
+
const errorCode = typeof payload === 'object' && payload
|
|
208
|
+
? payload.error?.code
|
|
209
|
+
: undefined;
|
|
210
|
+
if (errorCode === 'LLM_KEY_REQUIRED') {
|
|
211
|
+
throw new errors_1.CliError('This public agent requires you to provide an LLM key.\n' +
|
|
212
|
+
'Use --key <key> --provider <provider> or set OPENAI_API_KEY/ANTHROPIC_API_KEY env var.');
|
|
213
|
+
}
|
|
214
|
+
const message = typeof payload === 'object' && payload
|
|
215
|
+
? payload.error
|
|
216
|
+
?.message ||
|
|
217
|
+
payload.message ||
|
|
218
|
+
response.statusText
|
|
219
|
+
: response.statusText;
|
|
220
|
+
throw new errors_1.CliError(message);
|
|
221
|
+
}
|
|
222
|
+
// Track successful call
|
|
223
|
+
const inputType = filePaths.length > 0
|
|
224
|
+
? 'file'
|
|
225
|
+
: options.data
|
|
226
|
+
? 'json'
|
|
227
|
+
: sourceLabel === 'stdin'
|
|
228
|
+
? 'stdin'
|
|
229
|
+
: sourceLabel === 'metadata'
|
|
230
|
+
? 'metadata'
|
|
231
|
+
: 'empty';
|
|
232
|
+
await (0, analytics_1.track)('cli_call', {
|
|
233
|
+
agent: `${org}/${parsed.agent}@${parsed.version}`,
|
|
234
|
+
input_type: inputType,
|
|
235
|
+
});
|
|
236
|
+
if (options.output) {
|
|
237
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
238
|
+
await promises_1.default.writeFile(options.output, buffer);
|
|
239
|
+
process.stdout.write(`Saved response to ${options.output}\n`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const text = await response.text();
|
|
243
|
+
let payload;
|
|
244
|
+
try {
|
|
245
|
+
payload = JSON.parse(text);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
payload = text;
|
|
249
|
+
}
|
|
250
|
+
if (options.json) {
|
|
251
|
+
if (typeof payload === 'string') {
|
|
252
|
+
process.stdout.write(`${payload}\n`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
(0, output_1.printJson)(payload);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (typeof payload === 'string') {
|
|
259
|
+
process.stdout.write(`${payload}\n`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
(0, output_1.printJson)(payload);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerForkCommand = registerForkCommand;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const api_1 = require("../lib/api");
|
|
6
|
+
const errors_1 = require("../lib/errors");
|
|
7
|
+
const analytics_1 = require("../lib/analytics");
|
|
8
|
+
function parseAgentRef(ref) {
|
|
9
|
+
// Format: org/agent/version or org/agent (defaults to v1)
|
|
10
|
+
const parts = ref.split('/');
|
|
11
|
+
if (parts.length < 2 || parts.length > 3) {
|
|
12
|
+
throw new errors_1.CliError('Invalid agent reference. Use: org/agent or org/agent/version');
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
org: parts[0],
|
|
16
|
+
name: parts[1],
|
|
17
|
+
version: parts[2] || 'v1',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function registerForkCommand(program) {
|
|
21
|
+
program
|
|
22
|
+
.command('fork')
|
|
23
|
+
.description('Fork an agent to your account')
|
|
24
|
+
.argument('<agent>', 'Agent reference (org/name or org/name/version)')
|
|
25
|
+
.action(async (agent) => {
|
|
26
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
27
|
+
const { org, name, version } = parseAgentRef(agent);
|
|
28
|
+
// Get the agent to get its ID
|
|
29
|
+
const agentInfo = await (0, api_1.getPublicAgent)(config, org, name, version);
|
|
30
|
+
// Fork it
|
|
31
|
+
const result = await (0, api_1.forkAgent)(config, agentInfo.id);
|
|
32
|
+
// Get our org info
|
|
33
|
+
const myOrg = await (0, api_1.getOrg)(config);
|
|
34
|
+
await (0, analytics_1.track)('cli_fork', { agent: `${org}/${name}/${version}` });
|
|
35
|
+
process.stdout.write(`Forked ${org}/${name}/${version} to your account\n`);
|
|
36
|
+
process.stdout.write(`\nYour forked agent: ${myOrg.slug}/${name}/v1\n`);
|
|
37
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
38
|
+
process.stdout.write(` 1. Run: orchagent init ${name}\n`);
|
|
39
|
+
process.stdout.write(` 2. Edit the prompt and schemas locally\n`);
|
|
40
|
+
process.stdout.write(` 3. Run: orchagent publish\n`);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerCommands = registerCommands;
|
|
4
|
+
const login_1 = require("./login");
|
|
5
|
+
const call_1 = require("./call");
|
|
6
|
+
const agents_1 = require("./agents");
|
|
7
|
+
const init_1 = require("./init");
|
|
8
|
+
const publish_1 = require("./publish");
|
|
9
|
+
const whoami_1 = require("./whoami");
|
|
10
|
+
const star_1 = require("./star");
|
|
11
|
+
const fork_1 = require("./fork");
|
|
12
|
+
const search_1 = require("./search");
|
|
13
|
+
const llm_config_1 = require("./llm-config");
|
|
14
|
+
const keys_1 = require("./keys");
|
|
15
|
+
const run_1 = require("./run");
|
|
16
|
+
const info_1 = require("./info");
|
|
17
|
+
const skill_1 = require("./skill");
|
|
18
|
+
function registerCommands(program) {
|
|
19
|
+
(0, login_1.registerLoginCommand)(program);
|
|
20
|
+
(0, whoami_1.registerWhoamiCommand)(program);
|
|
21
|
+
(0, init_1.registerInitCommand)(program);
|
|
22
|
+
(0, publish_1.registerPublishCommand)(program);
|
|
23
|
+
(0, call_1.registerCallCommand)(program);
|
|
24
|
+
(0, run_1.registerRunCommand)(program);
|
|
25
|
+
(0, info_1.registerInfoCommand)(program);
|
|
26
|
+
(0, agents_1.registerAgentsCommand)(program);
|
|
27
|
+
(0, search_1.registerSearchCommand)(program);
|
|
28
|
+
(0, star_1.registerStarCommand)(program);
|
|
29
|
+
(0, fork_1.registerForkCommand)(program);
|
|
30
|
+
(0, llm_config_1.registerLlmConfigCommand)(program);
|
|
31
|
+
(0, keys_1.registerKeysCommand)(program);
|
|
32
|
+
(0, skill_1.registerSkillCommand)(program);
|
|
33
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerInfoCommand = registerInfoCommand;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const api_1 = require("../lib/api");
|
|
6
|
+
const errors_1 = require("../lib/errors");
|
|
7
|
+
const DEFAULT_VERSION = 'latest';
|
|
8
|
+
function parseAgentRef(value) {
|
|
9
|
+
const [ref, versionPart] = value.split('@');
|
|
10
|
+
const version = versionPart?.trim() || DEFAULT_VERSION;
|
|
11
|
+
const segments = ref.split('/');
|
|
12
|
+
if (segments.length === 2) {
|
|
13
|
+
return { org: segments[0], agent: segments[1], version };
|
|
14
|
+
}
|
|
15
|
+
throw new errors_1.CliError('Invalid agent reference. Use org/agent format (e.g., joe/leak-finder)');
|
|
16
|
+
}
|
|
17
|
+
function deriveReadmeUrl(sourceUrl) {
|
|
18
|
+
// Parse GitHub URLs like:
|
|
19
|
+
// git+https://github.com/user/repo.git#subdirectory=path
|
|
20
|
+
// https://github.com/user/repo
|
|
21
|
+
const githubMatch = sourceUrl.match(/github\.com[/:]([^/]+)\/([^/.#]+)(?:\.git)?(?:#subdirectory=(.+))?/);
|
|
22
|
+
if (!githubMatch)
|
|
23
|
+
return null;
|
|
24
|
+
const [, user, repo, subdirectory] = githubMatch;
|
|
25
|
+
const path = subdirectory ? `${subdirectory}/README.md` : 'README.md';
|
|
26
|
+
return `https://raw.githubusercontent.com/${user}/${repo}/main/${path}`;
|
|
27
|
+
}
|
|
28
|
+
async function fetchReadme(url) {
|
|
29
|
+
try {
|
|
30
|
+
const response = await fetch(url);
|
|
31
|
+
if (!response.ok)
|
|
32
|
+
return null;
|
|
33
|
+
return await response.text();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function registerInfoCommand(program) {
|
|
40
|
+
program
|
|
41
|
+
.command('info <agent>')
|
|
42
|
+
.description('Show agent information and README')
|
|
43
|
+
.option('--json', 'Output as JSON')
|
|
44
|
+
.action(async (agentArg, options) => {
|
|
45
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
46
|
+
const { org, agent, version } = parseAgentRef(agentArg);
|
|
47
|
+
// Fetch agent metadata
|
|
48
|
+
const agentData = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
49
|
+
if (options.json) {
|
|
50
|
+
process.stdout.write(JSON.stringify(agentData, null, 2) + '\n');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Display agent info
|
|
54
|
+
process.stdout.write('\n');
|
|
55
|
+
process.stdout.write(`${org}/${agent}@${version}\n`);
|
|
56
|
+
process.stdout.write('='.repeat(40) + '\n\n');
|
|
57
|
+
if (agentData.description) {
|
|
58
|
+
process.stdout.write(`${agentData.description}\n\n`);
|
|
59
|
+
}
|
|
60
|
+
process.stdout.write(`Type: ${agentData.type}\n`);
|
|
61
|
+
process.stdout.write(`Providers: ${agentData.supported_providers.join(', ')}\n`);
|
|
62
|
+
if (agentData.type === 'code') {
|
|
63
|
+
if (agentData.url) {
|
|
64
|
+
process.stdout.write(`Server: ${agentData.url}\n`);
|
|
65
|
+
}
|
|
66
|
+
if (agentData.source_url) {
|
|
67
|
+
process.stdout.write(`Source: ${agentData.source_url}\n`);
|
|
68
|
+
}
|
|
69
|
+
if (agentData.run_command) {
|
|
70
|
+
process.stdout.write(`Run: ${agentData.run_command}\n`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Fetch and display README if available
|
|
74
|
+
if (agentData.source_url) {
|
|
75
|
+
const readmeUrl = deriveReadmeUrl(agentData.source_url);
|
|
76
|
+
if (readmeUrl) {
|
|
77
|
+
const readme = await fetchReadme(readmeUrl);
|
|
78
|
+
if (readme) {
|
|
79
|
+
process.stdout.write('\n' + '-'.repeat(40) + '\n');
|
|
80
|
+
process.stdout.write('README\n');
|
|
81
|
+
process.stdout.write('-'.repeat(40) + '\n\n');
|
|
82
|
+
process.stdout.write(readme);
|
|
83
|
+
process.stdout.write('\n');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
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.registerInitCommand = registerInitCommand;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const errors_1 = require("../lib/errors");
|
|
10
|
+
const MANIFEST_TEMPLATE = `{
|
|
11
|
+
"name": "my-agent",
|
|
12
|
+
"version": "v1",
|
|
13
|
+
"description": "A simple AI agent",
|
|
14
|
+
"type": "prompt",
|
|
15
|
+
"tags": []
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
const PROMPT_TEMPLATE = `You are a helpful AI assistant.
|
|
19
|
+
|
|
20
|
+
Given the following input, provide a clear and concise response.
|
|
21
|
+
|
|
22
|
+
Input: {{input}}
|
|
23
|
+
|
|
24
|
+
Respond in a helpful and professional manner.
|
|
25
|
+
`;
|
|
26
|
+
const SCHEMA_TEMPLATE = `{
|
|
27
|
+
"input": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"properties": {
|
|
30
|
+
"input": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "The user's input or question"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": ["input"]
|
|
36
|
+
},
|
|
37
|
+
"output": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"result": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "The agent's response"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"required": ["result"]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
function registerInitCommand(program) {
|
|
50
|
+
program
|
|
51
|
+
.command('init')
|
|
52
|
+
.description('Initialize a new agent project')
|
|
53
|
+
.argument('[name]', 'Agent name (default: current directory name)')
|
|
54
|
+
.option('--type <type>', 'Agent type: prompt or code (default: prompt)', 'prompt')
|
|
55
|
+
.action(async (name, options) => {
|
|
56
|
+
const cwd = process.cwd();
|
|
57
|
+
const agentName = name || path_1.default.basename(cwd);
|
|
58
|
+
const manifestPath = path_1.default.join(cwd, 'orchagent.json');
|
|
59
|
+
const promptPath = path_1.default.join(cwd, 'prompt.md');
|
|
60
|
+
const schemaPath = path_1.default.join(cwd, 'schema.json');
|
|
61
|
+
// Check if already initialized
|
|
62
|
+
try {
|
|
63
|
+
await promises_1.default.access(manifestPath);
|
|
64
|
+
throw new errors_1.CliError(`Already initialized (orchagent.json exists)`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err.code !== 'ENOENT') {
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Create manifest
|
|
72
|
+
const manifest = JSON.parse(MANIFEST_TEMPLATE);
|
|
73
|
+
manifest.name = agentName;
|
|
74
|
+
manifest.type = options.type === 'code' ? 'code' : 'prompt';
|
|
75
|
+
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
76
|
+
// Create prompt template (for prompt-based agents)
|
|
77
|
+
if (options.type !== 'code') {
|
|
78
|
+
await promises_1.default.writeFile(promptPath, PROMPT_TEMPLATE);
|
|
79
|
+
}
|
|
80
|
+
// Create schema template
|
|
81
|
+
await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
|
|
82
|
+
process.stdout.write(`Initialized agent "${agentName}" in ${cwd}\n`);
|
|
83
|
+
process.stdout.write(`\nFiles created:\n`);
|
|
84
|
+
process.stdout.write(` orchagent.json - Agent configuration\n`);
|
|
85
|
+
if (options.type !== 'code') {
|
|
86
|
+
process.stdout.write(` prompt.md - Prompt template\n`);
|
|
87
|
+
}
|
|
88
|
+
process.stdout.write(` schema.json - Input/output schemas\n`);
|
|
89
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
90
|
+
if (options.type !== 'code') {
|
|
91
|
+
process.stdout.write(` 1. Edit prompt.md with your prompt template\n`);
|
|
92
|
+
process.stdout.write(` 2. Edit schema.json with your input/output schemas\n`);
|
|
93
|
+
process.stdout.write(` 3. Run: orchagent publish\n`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
process.stdout.write(` 1. Edit schema.json with your input/output schemas\n`);
|
|
97
|
+
process.stdout.write(` 2. Deploy your code and get the URL\n`);
|
|
98
|
+
process.stdout.write(` 3. Run: orchagent publish --url <your-agent-url>\n`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|