@orchagent/cli 0.3.55 → 0.3.57

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");
@@ -43,6 +44,7 @@ function registerCommands(program) {
43
44
  (0, keys_1.registerKeysCommand)(program);
44
45
  (0, skill_1.registerSkillCommand)(program);
45
46
  (0, delete_1.registerDeleteCommand)(program);
47
+ (0, fork_1.registerForkCommand)(program);
46
48
  (0, github_1.registerGitHubCommand)(program);
47
49
  (0, doctor_1.registerDoctorCommand)(program);
48
50
  (0, status_1.registerStatusCommand)(program);
@@ -11,6 +11,18 @@ const api_1 = require("../lib/api");
11
11
  const errors_1 = require("../lib/errors");
12
12
  const analytics_1 = require("../lib/analytics");
13
13
  const output_1 = require("../lib/output");
14
+ function getTransferAuthError(err) {
15
+ if (!(err instanceof api_1.ApiError) || err.status !== 403) {
16
+ return null;
17
+ }
18
+ const message = err.message.toLowerCase();
19
+ if (message.includes('personal workspace session key') ||
20
+ message.includes('user authentication') ||
21
+ message.includes('clerk jwt')) {
22
+ return new errors_1.CliError('Transfer requires a user session key. Run `orch login` (browser sign-in, without `--key`) and retry.');
23
+ }
24
+ return null;
25
+ }
14
26
  async function promptText(message) {
15
27
  const rl = promises_1.default.createInterface({
16
28
  input: process.stdin,
@@ -25,31 +37,49 @@ function registerTransferCommand(program) {
25
37
  .command('transfer <agent-name>')
26
38
  .description('Transfer an agent to another workspace')
27
39
  .requiredOption('--to <workspace-slug>', 'Target workspace slug')
40
+ .option('-w, --workspace <workspace-slug>', 'Source workspace slug (defaults to active workspace)')
28
41
  .option('-y, --yes', 'Skip confirmation prompt')
29
42
  .option('--dry-run', 'Show what would be transferred without making changes')
30
43
  .option('--json', 'Output result as JSON')
31
44
  .addHelpText('after', `
32
45
  Examples:
33
46
  orch transfer my-agent --to team-workspace # Transfer agent to another workspace
47
+ orch transfer my-agent --to team-workspace --workspace my-team
34
48
  orch transfer my-agent --to team-workspace --dry-run # Preview transfer
35
49
  orch transfer my-agent --to team-workspace --yes # Skip confirmation
36
50
  `)
37
51
  .action(async (agentName, options) => {
52
+ const write = (message) => {
53
+ if (!options.json)
54
+ process.stdout.write(message);
55
+ };
38
56
  const config = await (0, config_1.getResolvedConfig)();
57
+ const configFile = await (0, config_1.loadConfig)();
39
58
  if (!config.apiKey) {
40
59
  throw new errors_1.CliError('Not logged in. Run `orchagent login` first.');
41
60
  }
42
- process.stdout.write('Finding agent and workspaces...\n');
43
- // Fetch workspaces and agents in parallel
44
- const [workspacesResponse, agents] = await Promise.all([
45
- (0, api_1.request)(config, 'GET', '/workspaces'),
46
- (0, api_1.listMyAgents)(config),
47
- ]);
61
+ write('Finding agent and workspaces...\n');
62
+ // Fetch workspace list first (needed to resolve source/target IDs).
63
+ const workspacesResponse = await (0, api_1.request)(config, 'GET', '/workspaces');
48
64
  // Find the target workspace by slug
49
65
  const targetWorkspace = workspacesResponse.workspaces.find((w) => w.slug === options.to);
50
66
  if (!targetWorkspace) {
51
67
  throw new errors_1.CliError(`Workspace '${options.to}' not found. Run \`orchagent workspace list\` to see available workspaces.`);
52
68
  }
69
+ // Resolve source workspace (optional). If set, list agents from that workspace.
70
+ const sourceWorkspaceSlug = options.workspace ?? configFile.workspace;
71
+ const sourceWorkspace = sourceWorkspaceSlug
72
+ ? workspacesResponse.workspaces.find((w) => w.slug === sourceWorkspaceSlug)
73
+ : null;
74
+ if (sourceWorkspaceSlug && !sourceWorkspace) {
75
+ throw new errors_1.CliError(`Source workspace '${sourceWorkspaceSlug}' not found. Run \`orchagent workspace list\` to see available workspaces.`);
76
+ }
77
+ if (sourceWorkspace && sourceWorkspace.id === targetWorkspace.id) {
78
+ throw new errors_1.CliError('Source and target workspaces must be different.');
79
+ }
80
+ const agents = sourceWorkspace
81
+ ? await (0, api_1.request)(config, 'GET', `/agents?workspace_id=${encodeURIComponent(sourceWorkspace.id)}`)
82
+ : (await (0, api_1.listMyAgents)(config));
53
83
  // Find the agent by name
54
84
  const matching = agents.filter((a) => a.name === agentName);
55
85
  if (matching.length === 0) {
@@ -58,64 +88,118 @@ Examples:
58
88
  // Use the most recent version to get the agent ID
59
89
  const agent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
60
90
  // Check transfer eligibility
61
- const check = await (0, api_1.checkAgentTransfer)(config, agent.id, targetWorkspace.id);
91
+ let check;
92
+ try {
93
+ check = await (0, api_1.checkAgentTransfer)(config, agent.id, targetWorkspace.id);
94
+ }
95
+ catch (err) {
96
+ const authErr = getTransferAuthError(err);
97
+ if (authErr)
98
+ throw authErr;
99
+ throw err;
100
+ }
62
101
  // Show transfer summary
63
- process.stdout.write(`\n${chalk_1.default.bold('Agent:')} ${agent.name}\n`);
64
- process.stdout.write(`${chalk_1.default.bold('Target workspace:')} ${targetWorkspace.name} (${targetWorkspace.slug})\n`);
102
+ write(`\n${chalk_1.default.bold('Agent:')} ${agent.name}\n`);
103
+ write(`${chalk_1.default.bold('Target workspace:')} ${targetWorkspace.name} (${targetWorkspace.slug})\n`);
65
104
  const { details } = check;
66
- process.stdout.write(`${chalk_1.default.bold('Versions:')} ${details.version_count}\n`);
105
+ write(`${chalk_1.default.bold('Versions:')} ${details.version_count}\n`);
67
106
  if (details.grants_count > 0) {
68
- process.stdout.write(`${chalk_1.default.bold('Grants to revoke:')} ${details.grants_count}\n`);
107
+ write(`${chalk_1.default.bold('Grants to revoke:')} ${details.grants_count}\n`);
69
108
  }
70
109
  if (details.keys_count > 0) {
71
- process.stdout.write(`${chalk_1.default.bold('Keys to delete:')} ${details.keys_count}\n`);
110
+ write(`${chalk_1.default.bold('Keys to delete:')} ${details.keys_count}\n`);
72
111
  }
73
112
  if (details.schedules_count > 0) {
74
- process.stdout.write(`${chalk_1.default.bold('Schedules to disable:')} ${details.schedules_count}\n`);
113
+ write(`${chalk_1.default.bold('Schedules to disable:')} ${details.schedules_count}\n`);
75
114
  }
76
- process.stdout.write('\n');
115
+ write('\n');
77
116
  // Show warnings
78
117
  if (check.warnings.length > 0) {
79
118
  for (const warning of check.warnings) {
80
- process.stdout.write(chalk_1.default.yellow(`Warning: ${warning}\n`));
119
+ write(chalk_1.default.yellow(`Warning: ${warning}\n`));
81
120
  }
82
- process.stdout.write('\n');
121
+ write('\n');
83
122
  }
84
123
  // Show blockers and exit if any
85
124
  if (check.blockers.length > 0) {
125
+ if (options.json) {
126
+ (0, output_1.printJson)({
127
+ can_transfer: false,
128
+ blockers: check.blockers,
129
+ warnings: check.warnings,
130
+ details,
131
+ agent_name: agent.name,
132
+ target_workspace: {
133
+ id: targetWorkspace.id,
134
+ slug: targetWorkspace.slug,
135
+ name: targetWorkspace.name,
136
+ },
137
+ });
138
+ process.exit(1);
139
+ }
86
140
  for (const blocker of check.blockers) {
87
- process.stdout.write(chalk_1.default.red(`Blocker: ${blocker}\n`));
141
+ write(chalk_1.default.red(`Blocker: ${blocker}\n`));
88
142
  }
89
- process.stdout.write(chalk_1.default.red('\nTransfer cannot proceed due to blockers above.\n'));
143
+ write(chalk_1.default.red('\nTransfer cannot proceed due to blockers above.\n'));
90
144
  process.exit(1);
91
145
  }
92
146
  // Handle dry-run
93
147
  if (options.dryRun) {
94
- process.stdout.write('DRY RUN - No changes will be made\n\n');
95
- process.stdout.write(`Would transfer: ${agent.name} (${details.version_count} version(s))\n`);
96
- process.stdout.write(`Target: ${targetWorkspace.name} (${targetWorkspace.slug})\n`);
148
+ if (options.json) {
149
+ (0, output_1.printJson)({
150
+ dry_run: true,
151
+ can_transfer: true,
152
+ agent_name: agent.name,
153
+ target_workspace: {
154
+ id: targetWorkspace.id,
155
+ slug: targetWorkspace.slug,
156
+ name: targetWorkspace.name,
157
+ },
158
+ details,
159
+ warnings: check.warnings,
160
+ blockers: check.blockers,
161
+ });
162
+ return;
163
+ }
164
+ write('DRY RUN - No changes will be made\n\n');
165
+ write(`Would transfer: ${agent.name} (${details.version_count} version(s))\n`);
166
+ write(`Target: ${targetWorkspace.name} (${targetWorkspace.slug})\n`);
97
167
  if (details.grants_count > 0 || details.keys_count > 0 || details.schedules_count > 0) {
98
- process.stdout.write(chalk_1.default.yellow(`Cleanup: ${details.grants_count} grant(s) revoked, ${details.keys_count} key(s) deleted, ${details.schedules_count} schedule(s) disabled\n`));
168
+ write(chalk_1.default.yellow(`Cleanup: ${details.grants_count} grant(s) revoked, ${details.keys_count} key(s) deleted, ${details.schedules_count} schedule(s) disabled\n`));
99
169
  }
100
- process.stdout.write('\nNo changes made (dry run)\n');
170
+ write('\nNo changes made (dry run)\n');
101
171
  return;
102
172
  }
103
173
  // Prompt for confirmation
104
174
  if (!options.yes) {
105
- process.stdout.write(chalk_1.default.yellow('This will transfer the agent and all its versions to the target workspace.\n'));
106
- process.stdout.write(chalk_1.default.yellow('Existing grants, keys, and schedules in the source workspace will be cleaned up.\n\n'));
175
+ write(chalk_1.default.yellow('This will transfer the agent and all its versions to the target workspace.\n'));
176
+ write(chalk_1.default.yellow('Existing grants, keys, and schedules in the source workspace will be cleaned up.\n\n'));
107
177
  const confirmName = await promptText(`Type "${agent.name}" to confirm transfer: `);
108
178
  if (confirmName !== agent.name) {
109
- process.stdout.write(chalk_1.default.red('\nTransfer cancelled. Name did not match.\n'));
179
+ if (options.json) {
180
+ (0, output_1.printJson)({ cancelled: true, reason: 'confirmation_mismatch' });
181
+ }
182
+ else {
183
+ write(chalk_1.default.red('\nTransfer cancelled. Name did not match.\n'));
184
+ }
110
185
  process.exit(1);
111
186
  }
112
187
  }
113
188
  // Perform transfer
114
- process.stdout.write('Transferring agent...\n');
115
- const result = await (0, api_1.transferAgent)(config, agent.id, {
116
- target_workspace_id: targetWorkspace.id,
117
- confirmation_name: agent.name,
118
- });
189
+ write('Transferring agent...\n');
190
+ let result;
191
+ try {
192
+ result = await (0, api_1.transferAgent)(config, agent.id, {
193
+ target_workspace_id: targetWorkspace.id,
194
+ confirmation_name: agent.name,
195
+ });
196
+ }
197
+ catch (err) {
198
+ const authErr = getTransferAuthError(err);
199
+ if (authErr)
200
+ throw authErr;
201
+ throw err;
202
+ }
119
203
  await (0, analytics_1.track)('cli_transfer', {
120
204
  agent_name: result.agent_name,
121
205
  versions_transferred: result.versions_transferred,
@@ -125,11 +209,11 @@ Examples:
125
209
  (0, output_1.printJson)(result);
126
210
  return;
127
211
  }
128
- process.stdout.write(`\n${chalk_1.default.green('+')} Transferred ${result.agent_name} (${result.versions_transferred} version(s))\n`);
129
- process.stdout.write(` From: ${result.source_workspace.name} (${result.source_workspace.slug})\n`);
130
- process.stdout.write(` To: ${result.target_workspace.name} (${result.target_workspace.slug})\n`);
212
+ write(`\n${chalk_1.default.green('+')} Transferred ${result.agent_name} (${result.versions_transferred} version(s))\n`);
213
+ write(` From: ${result.source_workspace.name} (${result.source_workspace.slug})\n`);
214
+ write(` To: ${result.target_workspace.name} (${result.target_workspace.slug})\n`);
131
215
  if (result.cleanup.grants_revoked > 0 || result.cleanup.keys_deleted > 0 || result.cleanup.schedules_disabled > 0) {
132
- process.stdout.write(chalk_1.default.gray(`\nCleanup: ${result.cleanup.grants_revoked} grant(s) revoked, ${result.cleanup.keys_deleted} key(s) deleted, ${result.cleanup.schedules_disabled} schedule(s) disabled\n`));
216
+ write(chalk_1.default.gray(`\nCleanup: ${result.cleanup.grants_revoked} grant(s) revoked, ${result.cleanup.keys_deleted} key(s) deleted, ${result.cleanup.schedules_disabled} schedule(s) disabled\n`));
133
217
  }
134
218
  });
135
219
  }
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.55",
3
+ "version": "0.3.57",
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>",