@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.
- package/dist/commands/fork.js +108 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/transfer.js +119 -35
- package/dist/index.js +1 -0
- package/dist/lib/api.js +10 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|
package/dist/commands/index.js
CHANGED
|
@@ -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
|
-
|
|
43
|
-
// Fetch
|
|
44
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
105
|
+
write(`${chalk_1.default.bold('Versions:')} ${details.version_count}\n`);
|
|
67
106
|
if (details.grants_count > 0) {
|
|
68
|
-
|
|
107
|
+
write(`${chalk_1.default.bold('Grants to revoke:')} ${details.grants_count}\n`);
|
|
69
108
|
}
|
|
70
109
|
if (details.keys_count > 0) {
|
|
71
|
-
|
|
110
|
+
write(`${chalk_1.default.bold('Keys to delete:')} ${details.keys_count}\n`);
|
|
72
111
|
}
|
|
73
112
|
if (details.schedules_count > 0) {
|
|
74
|
-
|
|
113
|
+
write(`${chalk_1.default.bold('Schedules to disable:')} ${details.schedules_count}\n`);
|
|
75
114
|
}
|
|
76
|
-
|
|
115
|
+
write('\n');
|
|
77
116
|
// Show warnings
|
|
78
117
|
if (check.warnings.length > 0) {
|
|
79
118
|
for (const warning of check.warnings) {
|
|
80
|
-
|
|
119
|
+
write(chalk_1.default.yellow(`Warning: ${warning}\n`));
|
|
81
120
|
}
|
|
82
|
-
|
|
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
|
-
|
|
141
|
+
write(chalk_1.default.red(`Blocker: ${blocker}\n`));
|
|
88
142
|
}
|
|
89
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
170
|
+
write('\nNo changes made (dry run)\n');
|
|
101
171
|
return;
|
|
102
172
|
}
|
|
103
173
|
// Prompt for confirmation
|
|
104
174
|
if (!options.yes) {
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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