@orchagent/cli 0.3.55 → 0.3.56

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.
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.55",
3
+ "version": "0.3.56",
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>",