@orchagent/cli 0.3.57 → 0.3.59
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/github.js +116 -285
- package/dist/commands/index.js +2 -0
- package/dist/commands/pull.js +419 -0
- package/package.json +1 -1
package/dist/commands/github.js
CHANGED
|
@@ -37,7 +37,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.registerGitHubCommand = registerGitHubCommand;
|
|
40
|
-
const http_1 = __importDefault(require("http"));
|
|
41
40
|
const open_1 = __importDefault(require("open"));
|
|
42
41
|
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
43
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -47,195 +46,10 @@ const api_1 = require("../lib/api");
|
|
|
47
46
|
const errors_1 = require("../lib/errors");
|
|
48
47
|
const analytics_1 = require("../lib/analytics");
|
|
49
48
|
const output_1 = require("../lib/output");
|
|
50
|
-
const
|
|
51
|
-
const
|
|
49
|
+
const POLL_INTERVAL_MS = 1500;
|
|
50
|
+
const POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
51
|
+
const SETTINGS_REDIRECT_BASE = 'https://orchagent.io/settings';
|
|
52
52
|
// Helper functions
|
|
53
|
-
function successHtml() {
|
|
54
|
-
return `<!DOCTYPE html>
|
|
55
|
-
<html>
|
|
56
|
-
<head>
|
|
57
|
-
<meta charset="utf-8">
|
|
58
|
-
<title>orchagent CLI - GitHub Connected</title>
|
|
59
|
-
<style>
|
|
60
|
-
body {
|
|
61
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
62
|
-
display: flex;
|
|
63
|
-
align-items: center;
|
|
64
|
-
justify-content: center;
|
|
65
|
-
min-height: 100vh;
|
|
66
|
-
margin: 0;
|
|
67
|
-
background: #0a0a0a;
|
|
68
|
-
color: #fafafa;
|
|
69
|
-
}
|
|
70
|
-
.container {
|
|
71
|
-
text-align: center;
|
|
72
|
-
padding: 2rem;
|
|
73
|
-
}
|
|
74
|
-
.icon {
|
|
75
|
-
width: 64px;
|
|
76
|
-
height: 64px;
|
|
77
|
-
background: rgba(34, 197, 94, 0.1);
|
|
78
|
-
border-radius: 50%;
|
|
79
|
-
display: flex;
|
|
80
|
-
align-items: center;
|
|
81
|
-
justify-content: center;
|
|
82
|
-
margin: 0 auto 1.5rem;
|
|
83
|
-
}
|
|
84
|
-
.icon svg {
|
|
85
|
-
width: 32px;
|
|
86
|
-
height: 32px;
|
|
87
|
-
color: #22c55e;
|
|
88
|
-
}
|
|
89
|
-
h1 {
|
|
90
|
-
font-size: 1.5rem;
|
|
91
|
-
margin: 0 0 0.5rem;
|
|
92
|
-
}
|
|
93
|
-
p {
|
|
94
|
-
color: #a1a1aa;
|
|
95
|
-
margin: 0;
|
|
96
|
-
}
|
|
97
|
-
</style>
|
|
98
|
-
</head>
|
|
99
|
-
<body>
|
|
100
|
-
<div class="container">
|
|
101
|
-
<div class="icon">
|
|
102
|
-
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
103
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
104
|
-
</svg>
|
|
105
|
-
</div>
|
|
106
|
-
<h1>GitHub Connected</h1>
|
|
107
|
-
<p>You can close this tab and return to your terminal.</p>
|
|
108
|
-
</div>
|
|
109
|
-
</body>
|
|
110
|
-
</html>`;
|
|
111
|
-
}
|
|
112
|
-
function errorHtml(message) {
|
|
113
|
-
return `<!DOCTYPE html>
|
|
114
|
-
<html>
|
|
115
|
-
<head>
|
|
116
|
-
<meta charset="utf-8">
|
|
117
|
-
<title>orchagent CLI - GitHub Connection Error</title>
|
|
118
|
-
<style>
|
|
119
|
-
body {
|
|
120
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
121
|
-
display: flex;
|
|
122
|
-
align-items: center;
|
|
123
|
-
justify-content: center;
|
|
124
|
-
min-height: 100vh;
|
|
125
|
-
margin: 0;
|
|
126
|
-
background: #0a0a0a;
|
|
127
|
-
color: #fafafa;
|
|
128
|
-
}
|
|
129
|
-
.container {
|
|
130
|
-
text-align: center;
|
|
131
|
-
padding: 2rem;
|
|
132
|
-
}
|
|
133
|
-
.icon {
|
|
134
|
-
width: 64px;
|
|
135
|
-
height: 64px;
|
|
136
|
-
background: rgba(239, 68, 68, 0.1);
|
|
137
|
-
border-radius: 50%;
|
|
138
|
-
display: flex;
|
|
139
|
-
align-items: center;
|
|
140
|
-
justify-content: center;
|
|
141
|
-
margin: 0 auto 1.5rem;
|
|
142
|
-
}
|
|
143
|
-
.icon svg {
|
|
144
|
-
width: 32px;
|
|
145
|
-
height: 32px;
|
|
146
|
-
color: #ef4444;
|
|
147
|
-
}
|
|
148
|
-
h1 {
|
|
149
|
-
font-size: 1.5rem;
|
|
150
|
-
margin: 0 0 0.5rem;
|
|
151
|
-
}
|
|
152
|
-
p {
|
|
153
|
-
color: #a1a1aa;
|
|
154
|
-
margin: 0;
|
|
155
|
-
}
|
|
156
|
-
</style>
|
|
157
|
-
</head>
|
|
158
|
-
<body>
|
|
159
|
-
<div class="container">
|
|
160
|
-
<div class="icon">
|
|
161
|
-
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
162
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
163
|
-
</svg>
|
|
164
|
-
</div>
|
|
165
|
-
<h1>Connection Error</h1>
|
|
166
|
-
<p>${message}</p>
|
|
167
|
-
</div>
|
|
168
|
-
</body>
|
|
169
|
-
</html>`;
|
|
170
|
-
}
|
|
171
|
-
async function waitForGitHubCallback(port, timeoutMs) {
|
|
172
|
-
return new Promise((resolve, reject) => {
|
|
173
|
-
let resolved = false;
|
|
174
|
-
let server = null;
|
|
175
|
-
const cleanup = () => {
|
|
176
|
-
if (server) {
|
|
177
|
-
server.close();
|
|
178
|
-
server = null;
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
const timeout = setTimeout(() => {
|
|
182
|
-
if (!resolved) {
|
|
183
|
-
resolved = true;
|
|
184
|
-
cleanup();
|
|
185
|
-
reject(new errors_1.CliError('GitHub authentication timed out. Please try again.'));
|
|
186
|
-
}
|
|
187
|
-
}, timeoutMs);
|
|
188
|
-
server = http_1.default.createServer((req, res) => {
|
|
189
|
-
const url = new URL(req.url || '/', `http://127.0.0.1:${port}`);
|
|
190
|
-
if (url.pathname !== '/callback') {
|
|
191
|
-
res.writeHead(404);
|
|
192
|
-
res.end('Not Found');
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
const code = url.searchParams.get('code');
|
|
196
|
-
const state = url.searchParams.get('state');
|
|
197
|
-
const error = url.searchParams.get('error');
|
|
198
|
-
if (error) {
|
|
199
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
200
|
-
res.end(errorHtml(url.searchParams.get('error_description') || error));
|
|
201
|
-
if (!resolved) {
|
|
202
|
-
resolved = true;
|
|
203
|
-
clearTimeout(timeout);
|
|
204
|
-
cleanup();
|
|
205
|
-
reject(new errors_1.CliError(`GitHub authorization failed: ${error}`));
|
|
206
|
-
}
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
if (!code || !state) {
|
|
210
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
211
|
-
res.end(errorHtml('Missing code or state parameter'));
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
215
|
-
res.end(successHtml());
|
|
216
|
-
if (!resolved) {
|
|
217
|
-
resolved = true;
|
|
218
|
-
clearTimeout(timeout);
|
|
219
|
-
cleanup();
|
|
220
|
-
resolve({ code, state });
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
server.on('error', (err) => {
|
|
224
|
-
if (!resolved) {
|
|
225
|
-
resolved = true;
|
|
226
|
-
clearTimeout(timeout);
|
|
227
|
-
cleanup();
|
|
228
|
-
if (err.code === 'EADDRINUSE') {
|
|
229
|
-
reject(new errors_1.CliError(`Port ${port} is already in use. Try a different port with --port.`));
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
reject(new errors_1.CliError(`Failed to start auth server: ${err.message}`));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
server.listen(port, '127.0.0.1');
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
53
|
async function promptForSelection(items) {
|
|
240
54
|
if (!process.stdin.isTTY) {
|
|
241
55
|
return null;
|
|
@@ -271,38 +85,54 @@ async function promptForSelection(items) {
|
|
|
271
85
|
});
|
|
272
86
|
});
|
|
273
87
|
}
|
|
88
|
+
function sleep(ms) {
|
|
89
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
90
|
+
}
|
|
274
91
|
// Command implementations
|
|
275
|
-
async function connectGitHub(config
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
body: JSON.stringify({ redirect_uri: redirectUri }),
|
|
92
|
+
async function connectGitHub(config) {
|
|
93
|
+
// Step 1: Initialize the GitHub App install flow
|
|
94
|
+
const initResponse = await (0, api_1.request)(config, 'POST', '/github/install/init', {
|
|
95
|
+
body: JSON.stringify({ redirect_uri: SETTINGS_REDIRECT_BASE }),
|
|
280
96
|
headers: { 'Content-Type': 'application/json' },
|
|
281
97
|
});
|
|
282
|
-
// Step 2:
|
|
283
|
-
|
|
284
|
-
// Step 3: Open browser
|
|
285
|
-
process.stdout.write('Opening browser for GitHub authentication...\n');
|
|
98
|
+
// Step 2: Open browser for GitHub App installation
|
|
99
|
+
process.stdout.write('Opening browser to install the GitHub App...\n');
|
|
286
100
|
try {
|
|
287
|
-
await (0, open_1.default)(initResponse.
|
|
101
|
+
await (0, open_1.default)(initResponse.install_url);
|
|
288
102
|
}
|
|
289
103
|
catch {
|
|
290
|
-
|
|
104
|
+
// Headless or browser unavailable - print URL for manual copy/paste
|
|
105
|
+
process.stdout.write(`\nCould not open browser automatically.\n`);
|
|
106
|
+
process.stdout.write(`Please open this URL in your browser:\n\n`);
|
|
107
|
+
process.stdout.write(` ${initResponse.install_url}\n\n`);
|
|
291
108
|
}
|
|
292
|
-
// Step
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
109
|
+
// Step 3: Poll for completion
|
|
110
|
+
process.stdout.write('Waiting for GitHub App installation...\n');
|
|
111
|
+
const startTime = Date.now();
|
|
112
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
113
|
+
await sleep(POLL_INTERVAL_MS);
|
|
114
|
+
const status = await (0, api_1.request)(config, 'GET', `/github/install/status?state=${encodeURIComponent(initResponse.state)}`);
|
|
115
|
+
if (status.status === 'completed') {
|
|
116
|
+
await (0, analytics_1.track)('cli_github_connect', { success: true });
|
|
117
|
+
process.stdout.write('\n');
|
|
118
|
+
process.stdout.write(`Connected to GitHub as ${chalk_1.default.bold(status.github_account_login)}\n`);
|
|
119
|
+
if (status.github_account_type) {
|
|
120
|
+
process.stdout.write(` Type: ${status.github_account_type}\n`);
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (status.status === 'failed') {
|
|
125
|
+
await (0, analytics_1.track)('cli_github_connect', { success: false });
|
|
126
|
+
throw new errors_1.CliError(`GitHub App installation failed: ${status.error_message || 'Unknown error'}`);
|
|
127
|
+
}
|
|
128
|
+
// Still pending - continue polling
|
|
129
|
+
}
|
|
130
|
+
throw new errors_1.CliError('GitHub App installation timed out after 5 minutes. Please try again.');
|
|
301
131
|
}
|
|
302
132
|
async function disconnectGitHub(config) {
|
|
303
|
-
await (0, api_1.request)(config, 'DELETE', '/github/
|
|
133
|
+
await (0, api_1.request)(config, 'DELETE', '/github/uninstall');
|
|
304
134
|
await (0, analytics_1.track)('cli_github_disconnect');
|
|
305
|
-
process.stdout.write('
|
|
135
|
+
process.stdout.write('GitHub App uninstalled.\n');
|
|
306
136
|
}
|
|
307
137
|
async function getGitHubStatus(config, json) {
|
|
308
138
|
const connection = await (0, api_1.request)(config, 'GET', '/github/connection');
|
|
@@ -317,52 +147,19 @@ async function getGitHubStatus(config, json) {
|
|
|
317
147
|
}
|
|
318
148
|
process.stdout.write(`GitHub Status:\n\n`);
|
|
319
149
|
process.stdout.write(` Connected: ${chalk_1.default.green('Yes')}\n`);
|
|
320
|
-
process.stdout.write(`
|
|
321
|
-
if (connection.
|
|
322
|
-
|
|
150
|
+
process.stdout.write(` Account: ${chalk_1.default.bold(connection.github_account_login)}\n`);
|
|
151
|
+
if (connection.github_account_type) {
|
|
152
|
+
process.stdout.write(` Type: ${connection.github_account_type === 'User' ? 'User' : 'Organization'}\n`);
|
|
153
|
+
}
|
|
154
|
+
if (connection.installed_at) {
|
|
155
|
+
const date = new Date(connection.installed_at).toLocaleDateString();
|
|
323
156
|
process.stdout.write(` Since: ${date}\n`);
|
|
324
157
|
}
|
|
325
|
-
if (connection.
|
|
326
|
-
process.stdout.write(`
|
|
158
|
+
if (connection.suspended_at) {
|
|
159
|
+
process.stdout.write(` Status: ${chalk_1.default.yellow('Suspended')} (since ${new Date(connection.suspended_at).toLocaleDateString()})\n`);
|
|
327
160
|
}
|
|
328
161
|
process.stdout.write('\n');
|
|
329
162
|
}
|
|
330
|
-
async function listGitHubRepos(config, search, json) {
|
|
331
|
-
const params = new URLSearchParams();
|
|
332
|
-
if (search) {
|
|
333
|
-
params.append('search', search);
|
|
334
|
-
}
|
|
335
|
-
const queryStr = params.toString();
|
|
336
|
-
const repos = await (0, api_1.request)(config, 'GET', `/github/repos${queryStr ? `?${queryStr}` : ''}`);
|
|
337
|
-
await (0, analytics_1.track)('cli_github_list', { search: !!search, count: repos.length });
|
|
338
|
-
if (json) {
|
|
339
|
-
(0, output_1.printJson)(repos);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
if (repos.length === 0) {
|
|
343
|
-
process.stdout.write('No repositories found.\n');
|
|
344
|
-
if (search) {
|
|
345
|
-
process.stdout.write(`\nTry a different search term or run without --search to see all repos.\n`);
|
|
346
|
-
}
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const table = new cli_table3_1.default({
|
|
350
|
-
head: [
|
|
351
|
-
chalk_1.default.bold('Repository'),
|
|
352
|
-
chalk_1.default.bold('Private'),
|
|
353
|
-
chalk_1.default.bold('Last Pushed'),
|
|
354
|
-
],
|
|
355
|
-
});
|
|
356
|
-
repos.forEach((repo) => {
|
|
357
|
-
const visibility = repo.private ? chalk_1.default.yellow('Yes') : chalk_1.default.green('No');
|
|
358
|
-
const pushed = repo.pushed_at
|
|
359
|
-
? new Date(repo.pushed_at).toLocaleDateString()
|
|
360
|
-
: '-';
|
|
361
|
-
table.push([repo.full_name, visibility, pushed]);
|
|
362
|
-
});
|
|
363
|
-
process.stdout.write(`${table.toString()}\n`);
|
|
364
|
-
process.stdout.write(`\nFound ${repos.length} repositor${repos.length === 1 ? 'y' : 'ies'}.\n`);
|
|
365
|
-
}
|
|
366
163
|
async function scanGitHubRepo(config, repo, json) {
|
|
367
164
|
// Parse owner/repo format
|
|
368
165
|
const parts = repo.split('/');
|
|
@@ -371,7 +168,8 @@ async function scanGitHubRepo(config, repo, json) {
|
|
|
371
168
|
`Use owner/repo format, e.g.: orchagent github scan myorg/myrepo`);
|
|
372
169
|
}
|
|
373
170
|
const [owner, repoName] = parts;
|
|
374
|
-
const
|
|
171
|
+
const response = await (0, api_1.request)(config, 'GET', `/github/repos/${owner}/${repoName}/scan`);
|
|
172
|
+
const results = response.items;
|
|
375
173
|
await (0, analytics_1.track)('cli_github_scan', { repo, found: results.length });
|
|
376
174
|
if (json) {
|
|
377
175
|
(0, output_1.printJson)(results);
|
|
@@ -392,7 +190,7 @@ async function scanGitHubRepo(config, repo, json) {
|
|
|
392
190
|
],
|
|
393
191
|
});
|
|
394
192
|
results.forEach((item) => {
|
|
395
|
-
const typeLabel = item.type === 'skill' ? chalk_1.default.cyan('skill') : chalk_1.default.magenta(
|
|
193
|
+
const typeLabel = item.type === 'skill' ? chalk_1.default.cyan('skill') : chalk_1.default.magenta(item.type);
|
|
396
194
|
table.push([typeLabel, item.path, item.name || '-']);
|
|
397
195
|
});
|
|
398
196
|
process.stdout.write(`${table.toString()}\n`);
|
|
@@ -401,13 +199,12 @@ async function scanGitHubRepo(config, repo, json) {
|
|
|
401
199
|
return results;
|
|
402
200
|
}
|
|
403
201
|
async function importFromGitHub(config, repo, options) {
|
|
404
|
-
//
|
|
202
|
+
// Validate owner/repo format
|
|
405
203
|
const parts = repo.split('/');
|
|
406
204
|
if (parts.length !== 2) {
|
|
407
205
|
throw new errors_1.CliError(`Invalid repository format: ${repo}\n\n` +
|
|
408
206
|
`Use owner/repo format, e.g.: orchagent github import myorg/myrepo`);
|
|
409
207
|
}
|
|
410
|
-
const [owner, repoName] = parts;
|
|
411
208
|
let selectedPath = options.path;
|
|
412
209
|
// If no path specified, scan first and let user choose
|
|
413
210
|
if (!selectedPath) {
|
|
@@ -433,8 +230,7 @@ async function importFromGitHub(config, repo, options) {
|
|
|
433
230
|
const isPublic = options.private ? false : true;
|
|
434
231
|
const importResult = await (0, api_1.request)(config, 'POST', '/github/import', {
|
|
435
232
|
body: JSON.stringify({
|
|
436
|
-
|
|
437
|
-
repo: repoName,
|
|
233
|
+
repo,
|
|
438
234
|
path: selectedPath,
|
|
439
235
|
is_public: isPublic,
|
|
440
236
|
name: options.name,
|
|
@@ -445,21 +241,48 @@ async function importFromGitHub(config, repo, options) {
|
|
|
445
241
|
repo,
|
|
446
242
|
path: selectedPath,
|
|
447
243
|
is_public: isPublic,
|
|
448
|
-
type: importResult.type,
|
|
244
|
+
type: importResult.agent.type,
|
|
449
245
|
});
|
|
450
246
|
if (options.json) {
|
|
451
247
|
(0, output_1.printJson)(importResult);
|
|
452
248
|
return;
|
|
453
249
|
}
|
|
454
250
|
process.stdout.write('\n');
|
|
455
|
-
process.stdout.write(`Imported ${chalk_1.default.bold(importResult.name)} from ${repo}\n`);
|
|
251
|
+
process.stdout.write(`Imported ${chalk_1.default.bold(importResult.agent.name)} from ${repo}\n`);
|
|
456
252
|
process.stdout.write('\n');
|
|
457
|
-
process.stdout.write(` Agent: ${importResult.
|
|
458
|
-
process.stdout.write(` Version: ${importResult.version}\n`);
|
|
459
|
-
process.stdout.write(` Type: ${importResult.type}\n`);
|
|
253
|
+
process.stdout.write(` Agent: ${importResult.agent.name}\n`);
|
|
254
|
+
process.stdout.write(` Version: ${importResult.agent.version}\n`);
|
|
255
|
+
process.stdout.write(` Type: ${importResult.agent.type}\n`);
|
|
460
256
|
process.stdout.write(` Public: ${isPublic ? chalk_1.default.green('Yes') : chalk_1.default.yellow('No')}\n`);
|
|
461
257
|
process.stdout.write('\n');
|
|
462
|
-
|
|
258
|
+
}
|
|
259
|
+
async function getSyncConfig(config, agentId, options) {
|
|
260
|
+
// If --set-auto-publish is specified, update config first
|
|
261
|
+
if (options.setAutoPublish !== undefined) {
|
|
262
|
+
const autoPublish = options.setAutoPublish === 'true';
|
|
263
|
+
await (0, api_1.request)(config, 'PATCH', `/github/agents/${agentId}/sync-config`, {
|
|
264
|
+
body: JSON.stringify({ auto_publish: autoPublish }),
|
|
265
|
+
headers: { 'Content-Type': 'application/json' },
|
|
266
|
+
});
|
|
267
|
+
process.stdout.write(`Updated auto_publish to ${chalk_1.default.bold(String(autoPublish))} for agent ${agentId}\n`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const syncConfig = await (0, api_1.request)(config, 'GET', `/github/agents/${agentId}/sync-config`);
|
|
271
|
+
if (options.json) {
|
|
272
|
+
(0, output_1.printJson)(syncConfig);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
process.stdout.write(`Sync Config for ${chalk_1.default.bold(agentId)}:\n\n`);
|
|
276
|
+
process.stdout.write(` Auto-publish: ${syncConfig.auto_publish ? chalk_1.default.green('enabled') : chalk_1.default.yellow('disabled')}\n`);
|
|
277
|
+
if (syncConfig.sync_status) {
|
|
278
|
+
process.stdout.write(` Sync status: ${syncConfig.sync_status}\n`);
|
|
279
|
+
}
|
|
280
|
+
process.stdout.write('\n');
|
|
281
|
+
}
|
|
282
|
+
async function approveSync(config, agentId) {
|
|
283
|
+
await (0, api_1.request)(config, 'POST', `/github/agents/${agentId}/approve`);
|
|
284
|
+
await (0, analytics_1.track)('cli_github_approve', { agent_id: agentId });
|
|
285
|
+
process.stdout.write(`Approved pending sync for agent ${chalk_1.default.bold(agentId)}.\n`);
|
|
463
286
|
}
|
|
464
287
|
// Command registration
|
|
465
288
|
function registerGitHubCommand(program) {
|
|
@@ -468,22 +291,17 @@ function registerGitHubCommand(program) {
|
|
|
468
291
|
.description('Connect to GitHub and import agents');
|
|
469
292
|
github
|
|
470
293
|
.command('connect')
|
|
471
|
-
.description('
|
|
472
|
-
.
|
|
473
|
-
.action(async (options) => {
|
|
294
|
+
.description('Install the GitHub App to connect your account')
|
|
295
|
+
.action(async () => {
|
|
474
296
|
const config = await (0, config_1.getResolvedConfig)();
|
|
475
297
|
if (!config.apiKey) {
|
|
476
298
|
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
477
299
|
}
|
|
478
|
-
|
|
479
|
-
if (isNaN(port) || port < 1024 || port > 65535) {
|
|
480
|
-
throw new errors_1.CliError('Port must be a number between 1024 and 65535');
|
|
481
|
-
}
|
|
482
|
-
await connectGitHub(config, port);
|
|
300
|
+
await connectGitHub(config);
|
|
483
301
|
});
|
|
484
302
|
github
|
|
485
303
|
.command('disconnect')
|
|
486
|
-
.description('Remove GitHub
|
|
304
|
+
.description('Remove GitHub App installation')
|
|
487
305
|
.action(async () => {
|
|
488
306
|
const config = await (0, config_1.getResolvedConfig)();
|
|
489
307
|
if (!config.apiKey) {
|
|
@@ -502,18 +320,6 @@ function registerGitHubCommand(program) {
|
|
|
502
320
|
}
|
|
503
321
|
await getGitHubStatus(config, options.json || false);
|
|
504
322
|
});
|
|
505
|
-
github
|
|
506
|
-
.command('list')
|
|
507
|
-
.description('List accessible GitHub repositories')
|
|
508
|
-
.option('--search <query>', 'Filter repositories by name')
|
|
509
|
-
.option('--json', 'Output raw JSON')
|
|
510
|
-
.action(async (options) => {
|
|
511
|
-
const config = await (0, config_1.getResolvedConfig)();
|
|
512
|
-
if (!config.apiKey) {
|
|
513
|
-
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
514
|
-
}
|
|
515
|
-
await listGitHubRepos(config, options.search, options.json || false);
|
|
516
|
-
});
|
|
517
323
|
github
|
|
518
324
|
.command('scan <repo>')
|
|
519
325
|
.description('Scan a repository for agents and skills')
|
|
@@ -540,4 +346,29 @@ function registerGitHubCommand(program) {
|
|
|
540
346
|
}
|
|
541
347
|
await importFromGitHub(config, repo, options);
|
|
542
348
|
});
|
|
349
|
+
github
|
|
350
|
+
.command('sync-config <agent>')
|
|
351
|
+
.description('View or update sync configuration for a GitHub-linked agent')
|
|
352
|
+
.option('--set-auto-publish <value>', 'Set auto_publish (true or false)')
|
|
353
|
+
.option('--json', 'Output raw JSON')
|
|
354
|
+
.action(async (agent, options) => {
|
|
355
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
356
|
+
if (!config.apiKey) {
|
|
357
|
+
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
358
|
+
}
|
|
359
|
+
if (options.setAutoPublish !== undefined && options.setAutoPublish !== 'true' && options.setAutoPublish !== 'false') {
|
|
360
|
+
throw new errors_1.CliError('--set-auto-publish must be "true" or "false"');
|
|
361
|
+
}
|
|
362
|
+
await getSyncConfig(config, agent, options);
|
|
363
|
+
});
|
|
364
|
+
github
|
|
365
|
+
.command('approve <agent_id>')
|
|
366
|
+
.description('Approve a pending GitHub sync for an agent')
|
|
367
|
+
.action(async (agentId) => {
|
|
368
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
369
|
+
if (!config.apiKey) {
|
|
370
|
+
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
371
|
+
}
|
|
372
|
+
await approveSync(config, agentId);
|
|
373
|
+
});
|
|
543
374
|
}
|
package/dist/commands/index.js
CHANGED
|
@@ -32,6 +32,7 @@ const agent_keys_1 = require("./agent-keys");
|
|
|
32
32
|
const schedule_1 = require("./schedule");
|
|
33
33
|
const service_1 = require("./service");
|
|
34
34
|
const transfer_1 = require("./transfer");
|
|
35
|
+
const pull_1 = require("./pull");
|
|
35
36
|
function registerCommands(program) {
|
|
36
37
|
(0, login_1.registerLoginCommand)(program);
|
|
37
38
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -64,4 +65,5 @@ function registerCommands(program) {
|
|
|
64
65
|
(0, schedule_1.registerScheduleCommand)(program);
|
|
65
66
|
(0, service_1.registerServiceCommand)(program);
|
|
66
67
|
(0, transfer_1.registerTransferCommand)(program);
|
|
68
|
+
(0, pull_1.registerPullCommand)(program);
|
|
67
69
|
}
|
|
@@ -0,0 +1,419 @@
|
|
|
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.registerPullCommand = registerPullCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const config_1 = require("../lib/config");
|
|
13
|
+
const api_1 = require("../lib/api");
|
|
14
|
+
const errors_1 = require("../lib/errors");
|
|
15
|
+
const analytics_1 = require("../lib/analytics");
|
|
16
|
+
const output_1 = require("../lib/output");
|
|
17
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
18
|
+
function parsePullRef(value) {
|
|
19
|
+
const [ref, versionPart] = value.split('@');
|
|
20
|
+
const version = versionPart?.trim() || 'latest';
|
|
21
|
+
const segments = ref.split('/');
|
|
22
|
+
if (segments.length === 1) {
|
|
23
|
+
return { agent: segments[0], version };
|
|
24
|
+
}
|
|
25
|
+
if (segments.length === 2) {
|
|
26
|
+
return { org: segments[0], agent: segments[1], version };
|
|
27
|
+
}
|
|
28
|
+
throw new errors_1.CliError('Invalid agent reference. Use org/agent[@version] or agent[@version] format.');
|
|
29
|
+
}
|
|
30
|
+
function canonicalType(typeValue) {
|
|
31
|
+
const normalized = (typeValue || 'agent').toLowerCase();
|
|
32
|
+
return normalized === 'skill' ? 'skill' : 'agent';
|
|
33
|
+
}
|
|
34
|
+
function resolveEngine(data) {
|
|
35
|
+
const ee = data.execution_engine;
|
|
36
|
+
if (ee === 'direct_llm' || ee === 'managed_loop' || ee === 'code_runtime') {
|
|
37
|
+
return ee;
|
|
38
|
+
}
|
|
39
|
+
const normalized = (data.type || '').toLowerCase();
|
|
40
|
+
if (normalized === 'tool' || normalized === 'code')
|
|
41
|
+
return 'code_runtime';
|
|
42
|
+
if (normalized === 'agentic')
|
|
43
|
+
return 'managed_loop';
|
|
44
|
+
return 'direct_llm';
|
|
45
|
+
}
|
|
46
|
+
// ─── Agent Resolution ───────────────────────────────────────────────────────
|
|
47
|
+
async function resolveAgent(config, org, agent, version) {
|
|
48
|
+
// 1. Try public download endpoint
|
|
49
|
+
try {
|
|
50
|
+
const data = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
51
|
+
return {
|
|
52
|
+
name: data.name,
|
|
53
|
+
version: data.version,
|
|
54
|
+
description: data.description,
|
|
55
|
+
type: data.type || 'agent',
|
|
56
|
+
run_mode: data.run_mode,
|
|
57
|
+
execution_engine: data.execution_engine,
|
|
58
|
+
callable: data.callable,
|
|
59
|
+
prompt: data.prompt,
|
|
60
|
+
input_schema: data.input_schema,
|
|
61
|
+
output_schema: data.output_schema,
|
|
62
|
+
supported_providers: data.supported_providers,
|
|
63
|
+
default_models: data.default_models,
|
|
64
|
+
default_skills: data.default_skills,
|
|
65
|
+
skills_locked: data.skills_locked,
|
|
66
|
+
source_url: data.source_url,
|
|
67
|
+
pip_package: data.pip_package,
|
|
68
|
+
run_command: data.run_command,
|
|
69
|
+
entrypoint: data.entrypoint,
|
|
70
|
+
has_bundle: data.has_bundle,
|
|
71
|
+
source: 'public_download',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
// 2. Handle 403 (server-only / download-disabled)
|
|
76
|
+
if (err instanceof api_1.ApiError && err.status === 403) {
|
|
77
|
+
const payload = err.payload;
|
|
78
|
+
const errorCode = payload?.error?.code;
|
|
79
|
+
if (errorCode === 'PAID_AGENT_SERVER_ONLY' || errorCode === 'DOWNLOAD_DISABLED') {
|
|
80
|
+
// Try authenticated owner path
|
|
81
|
+
if (config.apiKey) {
|
|
82
|
+
const ownerData = await tryOwnerFallback(config, org, agent, version);
|
|
83
|
+
if (ownerData)
|
|
84
|
+
return { ...ownerData, source: 'owner_authenticated' };
|
|
85
|
+
}
|
|
86
|
+
// Not owner - block with message
|
|
87
|
+
if (errorCode === 'PAID_AGENT_SERVER_ONLY') {
|
|
88
|
+
throw new errors_1.CliError(`This agent is paid and runs on server only.\n\n` +
|
|
89
|
+
`Use cloud execution: orch run ${org}/${agent}@${version} --data '{...}'`);
|
|
90
|
+
}
|
|
91
|
+
throw new errors_1.CliError(`This agent is server-only and cannot be downloaded.\n\n` +
|
|
92
|
+
`Use cloud execution: orch run ${org}/${agent}@${version} --data '{...}'`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 3. Handle 404 - try private authenticated fallback
|
|
96
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
// 4. Private agent fallback (authenticated)
|
|
100
|
+
if (!config.apiKey) {
|
|
101
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
102
|
+
}
|
|
103
|
+
const userOrg = await (0, api_1.getOrg)(config);
|
|
104
|
+
if (userOrg.slug !== org) {
|
|
105
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
106
|
+
}
|
|
107
|
+
const data = await resolveFromMyAgents(config, agent, version, org);
|
|
108
|
+
if (!data) {
|
|
109
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
110
|
+
}
|
|
111
|
+
return { ...data, source: 'private_authenticated' };
|
|
112
|
+
}
|
|
113
|
+
async function tryOwnerFallback(config, org, agent, version) {
|
|
114
|
+
try {
|
|
115
|
+
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
116
|
+
let match;
|
|
117
|
+
if (version === 'latest') {
|
|
118
|
+
match = myAgents
|
|
119
|
+
.filter(a => a.name === agent && a.org_slug === org)
|
|
120
|
+
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
match = myAgents.find(a => a.name === agent && a.version === version && a.org_slug === org);
|
|
124
|
+
}
|
|
125
|
+
if (!match)
|
|
126
|
+
return null;
|
|
127
|
+
const agentData = await (0, api_1.request)(config, 'GET', `/agents/${match.id}`);
|
|
128
|
+
return mapAgentToPullData(agentData);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function resolveFromMyAgents(config, agent, version, org) {
|
|
135
|
+
const agents = await (0, api_1.listMyAgents)(config);
|
|
136
|
+
const matching = agents.filter(a => a.name === agent);
|
|
137
|
+
if (matching.length === 0)
|
|
138
|
+
return null;
|
|
139
|
+
let target;
|
|
140
|
+
if (version === 'latest') {
|
|
141
|
+
target = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const found = matching.find(a => a.version === version);
|
|
145
|
+
if (!found)
|
|
146
|
+
return null;
|
|
147
|
+
target = found;
|
|
148
|
+
}
|
|
149
|
+
const agentData = await (0, api_1.request)(config, 'GET', `/agents/${target.id}`);
|
|
150
|
+
return mapAgentToPullData(agentData);
|
|
151
|
+
}
|
|
152
|
+
function mapAgentToPullData(agent) {
|
|
153
|
+
return {
|
|
154
|
+
name: agent.name,
|
|
155
|
+
version: agent.version,
|
|
156
|
+
description: agent.description,
|
|
157
|
+
type: agent.type,
|
|
158
|
+
run_mode: agent.run_mode ?? null,
|
|
159
|
+
execution_engine: agent.execution_engine ?? null,
|
|
160
|
+
callable: agent.callable,
|
|
161
|
+
prompt: agent.prompt,
|
|
162
|
+
input_schema: agent.input_schema,
|
|
163
|
+
output_schema: agent.output_schema,
|
|
164
|
+
supported_providers: agent.supported_providers,
|
|
165
|
+
default_models: agent.default_models,
|
|
166
|
+
tags: agent.tags,
|
|
167
|
+
default_skills: agent.default_skills,
|
|
168
|
+
skills_locked: agent.skills_locked,
|
|
169
|
+
source_url: agent.source_url,
|
|
170
|
+
pip_package: agent.pip_package,
|
|
171
|
+
run_command: agent.run_command,
|
|
172
|
+
entrypoint: agent.entrypoint,
|
|
173
|
+
has_bundle: !!agent.code_bundle_url,
|
|
174
|
+
manifest: agent.manifest,
|
|
175
|
+
agentId: agent.id,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// ─── Manifest Reconstruction ────────────────────────────────────────────────
|
|
179
|
+
function buildManifest(data) {
|
|
180
|
+
const manifest = {
|
|
181
|
+
name: data.name,
|
|
182
|
+
description: data.description || '',
|
|
183
|
+
type: canonicalType(data.type) === 'skill' ? 'skill' : 'agent',
|
|
184
|
+
};
|
|
185
|
+
if (data.run_mode)
|
|
186
|
+
manifest.run_mode = data.run_mode;
|
|
187
|
+
if (data.callable !== undefined)
|
|
188
|
+
manifest.callable = data.callable;
|
|
189
|
+
if (data.tags && data.tags.length > 0)
|
|
190
|
+
manifest.tags = data.tags;
|
|
191
|
+
if (data.supported_providers && data.supported_providers.length > 0) {
|
|
192
|
+
// Don't include if it's just ['any'] (the default)
|
|
193
|
+
if (!(data.supported_providers.length === 1 && data.supported_providers[0] === 'any')) {
|
|
194
|
+
manifest.supported_providers = data.supported_providers;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (data.default_models && Object.keys(data.default_models).length > 0) {
|
|
198
|
+
manifest.default_models = data.default_models;
|
|
199
|
+
}
|
|
200
|
+
// Skills
|
|
201
|
+
if (data.default_skills && data.default_skills.length > 0) {
|
|
202
|
+
manifest.default_skills = data.default_skills;
|
|
203
|
+
}
|
|
204
|
+
if (data.skills_locked !== undefined && data.skills_locked) {
|
|
205
|
+
manifest.skills_locked = true;
|
|
206
|
+
}
|
|
207
|
+
// Engine-specific fields
|
|
208
|
+
const engine = resolveEngine(data);
|
|
209
|
+
if (engine === 'code_runtime') {
|
|
210
|
+
if (data.entrypoint && data.entrypoint !== 'sandbox_main.py') {
|
|
211
|
+
manifest.entrypoint = data.entrypoint;
|
|
212
|
+
}
|
|
213
|
+
if (data.source_url)
|
|
214
|
+
manifest.source_url = data.source_url;
|
|
215
|
+
if (data.pip_package)
|
|
216
|
+
manifest.pip_package = data.pip_package;
|
|
217
|
+
if (data.run_command)
|
|
218
|
+
manifest.run_command = data.run_command;
|
|
219
|
+
}
|
|
220
|
+
// Include orchestration manifest if present (for dependencies, etc.)
|
|
221
|
+
if (data.manifest && typeof data.manifest === 'object') {
|
|
222
|
+
const m = { ...data.manifest };
|
|
223
|
+
// Clean up fields that are already top-level
|
|
224
|
+
delete m.runtime;
|
|
225
|
+
delete m.loop;
|
|
226
|
+
if (Object.keys(m).length > 0) {
|
|
227
|
+
manifest.manifest = m;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return manifest;
|
|
231
|
+
}
|
|
232
|
+
// ─── Bundle Download + Extraction ───────────────────────────────────────────
|
|
233
|
+
async function downloadBundle(config, org, agent, version, agentId) {
|
|
234
|
+
try {
|
|
235
|
+
return await (0, api_1.downloadCodeBundle)(config, org, agent, version);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
if (config.apiKey && agentId) {
|
|
242
|
+
try {
|
|
243
|
+
return await (0, api_1.downloadCodeBundleAuthenticated)(config, agentId);
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
async function unzipBundle(zipPath, destDir) {
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
const proc = (0, child_process_1.spawn)('unzip', ['-q', zipPath, '-d', destDir], {
|
|
255
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
256
|
+
});
|
|
257
|
+
let stderr = '';
|
|
258
|
+
proc.stderr?.on('data', (chunk) => {
|
|
259
|
+
stderr += chunk.toString();
|
|
260
|
+
});
|
|
261
|
+
proc.on('close', (code) => {
|
|
262
|
+
if (code !== 0) {
|
|
263
|
+
const detail = stderr.trim() || `exit code ${code}`;
|
|
264
|
+
reject(new errors_1.CliError(`Failed to extract bundle: ${detail}`));
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
resolve();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
proc.on('error', (err) => {
|
|
271
|
+
reject(new errors_1.CliError(`Failed to run unzip: ${err.message}. Make sure unzip is installed.`));
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
// ─── Command ────────────────────────────────────────────────────────────────
|
|
276
|
+
function registerPullCommand(program) {
|
|
277
|
+
program
|
|
278
|
+
.command('pull <agent>')
|
|
279
|
+
.description('Pull a published agent into a local project directory')
|
|
280
|
+
.option('-o, --output <path>', 'Output directory (default: ./<agent-name>/)')
|
|
281
|
+
.option('--overwrite', 'Replace existing output directory contents')
|
|
282
|
+
.option('--json', 'Print machine-readable result summary')
|
|
283
|
+
.addHelpText('after', `
|
|
284
|
+
Examples:
|
|
285
|
+
orch pull acme/my-agent
|
|
286
|
+
orch pull acme/my-agent@v2
|
|
287
|
+
orch pull my-agent --output ./custom-dir
|
|
288
|
+
orch pull acme/my-agent --overwrite
|
|
289
|
+
orch pull acme/my-agent --json
|
|
290
|
+
`)
|
|
291
|
+
.action(async (agentRef, options) => {
|
|
292
|
+
const write = (message) => {
|
|
293
|
+
if (!options.json)
|
|
294
|
+
process.stdout.write(message);
|
|
295
|
+
};
|
|
296
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
297
|
+
const parsed = parsePullRef(agentRef);
|
|
298
|
+
// Resolve org from workspace / defaultOrg fallback
|
|
299
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
300
|
+
const org = parsed.org ?? configFile.workspace ?? config.defaultOrg;
|
|
301
|
+
if (!org) {
|
|
302
|
+
throw new errors_1.CliError('Missing org. Use org/agent[@version] format, or set a default org with:\n' +
|
|
303
|
+
' orch config set default-org <org>');
|
|
304
|
+
}
|
|
305
|
+
write(`Resolving ${org}/${parsed.agent}@${parsed.version}...\n`);
|
|
306
|
+
// Resolve agent data
|
|
307
|
+
const data = await resolveAgent(config, org, parsed.agent, parsed.version);
|
|
308
|
+
// Reject skills
|
|
309
|
+
if (canonicalType(data.type) === 'skill') {
|
|
310
|
+
throw new errors_1.CliError("This is a skill. Use 'orch skill install <ref>' instead.");
|
|
311
|
+
}
|
|
312
|
+
// Resolve output path
|
|
313
|
+
const outputDir = path_1.default.resolve(options.output || `./${data.name}`);
|
|
314
|
+
// Check if output path already exists
|
|
315
|
+
try {
|
|
316
|
+
const stat = await promises_1.default.stat(outputDir);
|
|
317
|
+
if (stat.isFile()) {
|
|
318
|
+
throw new errors_1.CliError(`Output path '${outputDir}' is a file. Please specify a directory.`);
|
|
319
|
+
}
|
|
320
|
+
if (!options.overwrite) {
|
|
321
|
+
throw new errors_1.CliError(`Output directory '${outputDir}' already exists.\n` +
|
|
322
|
+
`Use --overwrite to replace its contents.`);
|
|
323
|
+
}
|
|
324
|
+
// Overwrite: clear and recreate
|
|
325
|
+
await promises_1.default.rm(outputDir, { recursive: true });
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
if (err.code !== 'ENOENT') {
|
|
329
|
+
if (err instanceof errors_1.CliError)
|
|
330
|
+
throw err;
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
await promises_1.default.mkdir(outputDir, { recursive: true });
|
|
335
|
+
const engine = resolveEngine(data);
|
|
336
|
+
const filesWritten = [];
|
|
337
|
+
const warnings = [];
|
|
338
|
+
let bundleExtracted = false;
|
|
339
|
+
// Write orchagent.json
|
|
340
|
+
const manifest = buildManifest(data);
|
|
341
|
+
await promises_1.default.writeFile(path_1.default.join(outputDir, 'orchagent.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
342
|
+
filesWritten.push('orchagent.json');
|
|
343
|
+
// Write prompt.md (for prompt-driven engines)
|
|
344
|
+
if (data.prompt && (engine === 'direct_llm' || engine === 'managed_loop')) {
|
|
345
|
+
await promises_1.default.writeFile(path_1.default.join(outputDir, 'prompt.md'), data.prompt);
|
|
346
|
+
filesWritten.push('prompt.md');
|
|
347
|
+
}
|
|
348
|
+
// Write schema.json (if schemas exist)
|
|
349
|
+
if (data.input_schema || data.output_schema) {
|
|
350
|
+
const schema = {};
|
|
351
|
+
if (data.input_schema)
|
|
352
|
+
schema.input = data.input_schema;
|
|
353
|
+
if (data.output_schema)
|
|
354
|
+
schema.output = data.output_schema;
|
|
355
|
+
await promises_1.default.writeFile(path_1.default.join(outputDir, 'schema.json'), JSON.stringify(schema, null, 2) + '\n');
|
|
356
|
+
filesWritten.push('schema.json');
|
|
357
|
+
}
|
|
358
|
+
// Bundle download for code_runtime agents
|
|
359
|
+
if (engine === 'code_runtime' && data.has_bundle) {
|
|
360
|
+
write('Downloading code bundle...\n');
|
|
361
|
+
const bundle = await downloadBundle(config, org, data.name, data.version, data.agentId);
|
|
362
|
+
if (bundle) {
|
|
363
|
+
const tempDir = path_1.default.join(os_1.default.tmpdir(), `orchagent-pull-${Date.now()}`);
|
|
364
|
+
const zipPath = path_1.default.join(tempDir, 'bundle.zip');
|
|
365
|
+
try {
|
|
366
|
+
await promises_1.default.mkdir(tempDir, { recursive: true });
|
|
367
|
+
await promises_1.default.writeFile(zipPath, bundle);
|
|
368
|
+
await unzipBundle(zipPath, outputDir);
|
|
369
|
+
bundleExtracted = true;
|
|
370
|
+
write('Bundle extracted.\n');
|
|
371
|
+
}
|
|
372
|
+
finally {
|
|
373
|
+
await promises_1.default.rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
warnings.push('No downloadable bundle available for this version.');
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else if (engine === 'code_runtime' && !data.has_bundle) {
|
|
381
|
+
warnings.push('No downloadable bundle available for this version.');
|
|
382
|
+
}
|
|
383
|
+
// Track analytics
|
|
384
|
+
await (0, analytics_1.track)('cli_pull', {
|
|
385
|
+
org,
|
|
386
|
+
agent: parsed.agent,
|
|
387
|
+
version: data.version,
|
|
388
|
+
engine,
|
|
389
|
+
source: data.source,
|
|
390
|
+
});
|
|
391
|
+
// Output
|
|
392
|
+
const resolvedRef = `${org}/${data.name}@${data.version}`;
|
|
393
|
+
if (options.json) {
|
|
394
|
+
const result = {
|
|
395
|
+
success: true,
|
|
396
|
+
requested_ref: `${org}/${parsed.agent}@${parsed.version}`,
|
|
397
|
+
resolved_ref: resolvedRef,
|
|
398
|
+
output_dir: outputDir,
|
|
399
|
+
engine,
|
|
400
|
+
source: data.source,
|
|
401
|
+
files_written: filesWritten,
|
|
402
|
+
bundle_extracted: bundleExtracted,
|
|
403
|
+
warnings,
|
|
404
|
+
};
|
|
405
|
+
(0, output_1.printJson)(result);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
write(`\n${chalk_1.default.green('\u2713')} Pulled ${resolvedRef}\n`);
|
|
409
|
+
write(` Output: ${outputDir}\n`);
|
|
410
|
+
write(` Engine: ${engine}\n`);
|
|
411
|
+
write(` Files: ${filesWritten.join(', ')}\n`);
|
|
412
|
+
if (bundleExtracted) {
|
|
413
|
+
write(` Bundle: extracted\n`);
|
|
414
|
+
}
|
|
415
|
+
for (const w of warnings) {
|
|
416
|
+
write(` ${chalk_1.default.yellow('Warning:')} ${w}\n`);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
package/package.json
CHANGED