@orchagent/cli 0.3.56 → 0.3.58

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.
@@ -0,0 +1,108 @@
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.registerForkCommand = registerForkCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const config_1 = require("../lib/config");
9
+ const api_1 = require("../lib/api");
10
+ const agent_ref_1 = require("../lib/agent-ref");
11
+ const errors_1 = require("../lib/errors");
12
+ const analytics_1 = require("../lib/analytics");
13
+ const output_1 = require("../lib/output");
14
+ function getWorkspaceAuthError(err) {
15
+ if (!(err instanceof api_1.ApiError) || (err.status !== 401 && err.status !== 403)) {
16
+ return null;
17
+ }
18
+ const message = err.message.toLowerCase();
19
+ if (message.includes('workspace targeting') ||
20
+ message.includes('specified workspace') ||
21
+ message.includes('user authentication') ||
22
+ message.includes('clerk')) {
23
+ return new errors_1.CliError('Forking into a specific workspace requires a user session key. Run `orch login` (browser sign-in, without `--key`) and retry.');
24
+ }
25
+ return null;
26
+ }
27
+ async function resolveWorkspace(config, workspaceSlug) {
28
+ const response = await (0, api_1.request)(config, 'GET', '/workspaces');
29
+ const workspace = response.workspaces.find((w) => w.slug === workspaceSlug);
30
+ if (!workspace) {
31
+ throw new errors_1.CliError(`Workspace '${workspaceSlug}' not found. Run \`orchagent workspace list\` to see available workspaces.`);
32
+ }
33
+ return workspace;
34
+ }
35
+ function registerForkCommand(program) {
36
+ program
37
+ .command('fork <agent>')
38
+ .description('Fork a public agent into your workspace')
39
+ .option('--name <new-name>', 'Rename the forked agent')
40
+ .option('-w, --workspace <workspace-slug>', 'Target workspace slug')
41
+ .option('--json', 'Output raw JSON')
42
+ .addHelpText('after', `
43
+ Examples:
44
+ orch fork orchagent/my-discord-agent
45
+ orch fork orchagent/my-discord-agent --workspace acme-corp
46
+ orch fork orchagent/my-discord-agent --name customer-support-bot
47
+ orch fork orchagent/my-discord-agent@v2 --json
48
+ `)
49
+ .action(async (agentRef, options) => {
50
+ const write = (message) => {
51
+ if (!options.json)
52
+ process.stdout.write(message);
53
+ };
54
+ const config = await (0, config_1.getResolvedConfig)();
55
+ if (!config.apiKey) {
56
+ throw new errors_1.CliError('Not logged in. Run `orchagent login` first.');
57
+ }
58
+ const { org, agent, version } = (0, agent_ref_1.parseAgentRef)(agentRef);
59
+ write('Resolving source agent...\n');
60
+ const source = await (0, api_1.getPublicAgent)(config, org, agent, version);
61
+ if (!source.id) {
62
+ throw new errors_1.CliError(`Could not resolve source agent ID for '${org}/${agent}@${version}'.`);
63
+ }
64
+ let targetWorkspace = null;
65
+ if (options.workspace) {
66
+ write('Resolving target workspace...\n');
67
+ targetWorkspace = await resolveWorkspace(config, options.workspace);
68
+ }
69
+ write('Forking agent...\n');
70
+ const payload = {};
71
+ if (targetWorkspace)
72
+ payload.workspace_id = targetWorkspace.id;
73
+ const requestedName = options.name?.trim();
74
+ if (requestedName)
75
+ payload.new_name = requestedName;
76
+ let result;
77
+ try {
78
+ result = await (0, api_1.forkAgent)(config, source.id, payload);
79
+ }
80
+ catch (err) {
81
+ const authErr = getWorkspaceAuthError(err);
82
+ if (authErr)
83
+ throw authErr;
84
+ throw err;
85
+ }
86
+ await (0, analytics_1.track)('cli_fork', {
87
+ source_org: org,
88
+ source_agent: agent,
89
+ source_version: version,
90
+ target_workspace: targetWorkspace?.slug ?? null,
91
+ });
92
+ if (options.json) {
93
+ (0, output_1.printJson)(result);
94
+ return;
95
+ }
96
+ const forked = result.agent;
97
+ const targetOrgSlug = forked.org_slug ?? targetWorkspace?.slug ?? 'current-workspace';
98
+ write(`\n${chalk_1.default.green('\u2713')} Forked ${org}/${agent}@${version}\n`);
99
+ write(` New agent: ${targetOrgSlug}/${forked.name}@${forked.version}\n`);
100
+ if (targetWorkspace) {
101
+ write(` Workspace: ${targetWorkspace.name} (${targetWorkspace.slug})\n`);
102
+ }
103
+ if (result.service_key) {
104
+ write(`\nService key (save this - shown only once):\n`);
105
+ write(` ${result.service_key}\n`);
106
+ }
107
+ });
108
+ }
@@ -12,6 +12,7 @@ const run_1 = require("./run");
12
12
  const info_1 = require("./info");
13
13
  const skill_1 = require("./skill");
14
14
  const delete_1 = require("./delete");
15
+ const fork_1 = require("./fork");
15
16
  const github_1 = require("./github");
16
17
  const doctor_1 = require("./doctor");
17
18
  const status_1 = require("./status");
@@ -31,6 +32,7 @@ const agent_keys_1 = require("./agent-keys");
31
32
  const schedule_1 = require("./schedule");
32
33
  const service_1 = require("./service");
33
34
  const transfer_1 = require("./transfer");
35
+ const pull_1 = require("./pull");
34
36
  function registerCommands(program) {
35
37
  (0, login_1.registerLoginCommand)(program);
36
38
  (0, whoami_1.registerWhoamiCommand)(program);
@@ -43,6 +45,7 @@ function registerCommands(program) {
43
45
  (0, keys_1.registerKeysCommand)(program);
44
46
  (0, skill_1.registerSkillCommand)(program);
45
47
  (0, delete_1.registerDeleteCommand)(program);
48
+ (0, fork_1.registerForkCommand)(program);
46
49
  (0, github_1.registerGitHubCommand)(program);
47
50
  (0, doctor_1.registerDoctorCommand)(program);
48
51
  (0, status_1.registerStatusCommand)(program);
@@ -62,4 +65,5 @@ function registerCommands(program) {
62
65
  (0, schedule_1.registerScheduleCommand)(program);
63
66
  (0, service_1.registerServiceCommand)(program);
64
67
  (0, transfer_1.registerTransferCommand)(program);
68
+ (0, pull_1.registerPullCommand)(program);
65
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/dist/index.js CHANGED
@@ -61,6 +61,7 @@ program
61
61
  Quick Reference:
62
62
  run Run an agent (cloud by default, --local for local execution)
63
63
  info Show agent details and input/output schemas
64
+ fork Fork a public template into your workspace
64
65
 
65
66
  Installation:
66
67
  npm install -g @orchagent/cli Install globally (then use: orch)
package/dist/lib/api.js CHANGED
@@ -54,6 +54,7 @@ exports.getAgentWithFallback = getAgentWithFallback;
54
54
  exports.downloadCodeBundleAuthenticated = downloadCodeBundleAuthenticated;
55
55
  exports.checkAgentDelete = checkAgentDelete;
56
56
  exports.deleteAgent = deleteAgent;
57
+ exports.forkAgent = forkAgent;
57
58
  exports.checkAgentTransfer = checkAgentTransfer;
58
59
  exports.transferAgent = transferAgent;
59
60
  exports.previewAgentVersion = previewAgentVersion;
@@ -368,6 +369,15 @@ async function deleteAgent(config, agentId, confirmationName) {
368
369
  const params = confirmationName ? `?confirmation_name=${encodeURIComponent(confirmationName)}` : '';
369
370
  return request(config, 'DELETE', `/agents/${agentId}${params}`);
370
371
  }
372
+ /**
373
+ * Fork a public agent into the caller's workspace (or an explicit workspace_id).
374
+ */
375
+ async function forkAgent(config, sourceAgentId, data = {}) {
376
+ return request(config, 'POST', `/agents/${sourceAgentId}/fork`, {
377
+ body: JSON.stringify(data),
378
+ headers: { 'Content-Type': 'application/json' },
379
+ });
380
+ }
371
381
  /**
372
382
  * Check if an agent can be transferred to another workspace.
373
383
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.56",
3
+ "version": "0.3.58",
4
4
  "description": "Command-line interface for orchagent — deploy and run AI agents for your team",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",