@orchagent/cli 0.2.15 → 0.2.17

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.
@@ -18,6 +18,7 @@ const delete_1 = require("./delete");
18
18
  const github_1 = require("./github");
19
19
  const doctor_1 = require("./doctor");
20
20
  const status_1 = require("./status");
21
+ const workspace_1 = require("./workspace");
21
22
  function registerCommands(program) {
22
23
  (0, login_1.registerLoginCommand)(program);
23
24
  (0, whoami_1.registerWhoamiCommand)(program);
@@ -36,4 +37,5 @@ function registerCommands(program) {
36
37
  (0, github_1.registerGitHubCommand)(program);
37
38
  (0, doctor_1.registerDoctorCommand)(program);
38
39
  (0, status_1.registerStatusCommand)(program);
40
+ (0, workspace_1.registerWorkspaceCommand)(program);
39
41
  }
@@ -65,11 +65,14 @@ function registerPublishCommand(program) {
65
65
  .command('publish')
66
66
  .description('Publish agent or skill from local files')
67
67
  .option('--url <url>', 'Agent URL (for code-based agents)')
68
- .option('--public', 'Make agent public (default: true)', true)
69
- .option('--private', 'Make agent private')
68
+ .option('--public', 'Make agent public')
69
+ .option('--private', 'Make agent private (deprecated: now the default)')
70
70
  .option('--profile <name>', 'Use API key from named profile')
71
71
  .option('--dry-run', 'Show what would be published without making changes')
72
72
  .action(async (options) => {
73
+ if (options.private) {
74
+ process.stderr.write('Warning: --private is deprecated (private is now the default). You can safely remove it.\n');
75
+ }
73
76
  const config = await (0, config_1.getResolvedConfig)({}, options.profile);
74
77
  const cwd = process.cwd();
75
78
  // Check for SKILL.md first (skills take precedence)
@@ -94,7 +97,7 @@ function registerPublishCommand(program) {
94
97
  process.stderr.write(` Name: ${skillData.frontmatter.name}\n`);
95
98
  process.stderr.write(` Type: skill\n`);
96
99
  process.stderr.write(` Version: ${versionInfo}\n`);
97
- process.stderr.write(` Visibility: ${options.private ? 'private' : 'public'}\n`);
100
+ process.stderr.write(` Visibility: ${options.public ? 'public' : 'private'}\n`);
98
101
  process.stderr.write(` Providers: any\n`);
99
102
  process.stderr.write(`\nWould publish: ${preview.org_slug}/${skillData.frontmatter.name}@${preview.next_version}\n`);
100
103
  process.stderr.write(`API endpoint: POST ${config.apiUrl}/${preview.org_slug}/${skillData.frontmatter.name}/${preview.next_version}/run\n\n`);
@@ -106,13 +109,13 @@ function registerPublishCommand(program) {
106
109
  type: 'skill',
107
110
  description: skillData.frontmatter.description,
108
111
  prompt: skillData.body,
109
- is_public: options.private ? false : true,
112
+ is_public: options.public ? true : false,
110
113
  supported_providers: ['any'],
111
114
  });
112
115
  const skillVersion = skillResult.agent?.version || 'v1';
113
116
  await (0, analytics_1.track)('cli_publish', { agent_type: 'skill' });
114
117
  process.stdout.write(`\nPublished skill: ${org.slug}/${skillData.frontmatter.name}@${skillVersion}\n`);
115
- process.stdout.write(`Public: ${options.private ? 'no' : 'yes'}\n`);
118
+ process.stdout.write(`Public: ${options.public ? 'yes' : 'no'}\n`);
116
119
  return;
117
120
  }
118
121
  // Read manifest
@@ -237,7 +240,7 @@ function registerPublishCommand(program) {
237
240
  process.stderr.write(` Name: ${manifest.name}\n`);
238
241
  process.stderr.write(` Type: ${manifest.type}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
239
242
  process.stderr.write(` Version: ${versionInfo}\n`);
240
- process.stderr.write(` Visibility: ${options.private ? 'private' : 'public'}\n`);
243
+ process.stderr.write(` Visibility: ${options.public ? 'public' : 'private'}\n`);
241
244
  process.stderr.write(` Providers: ${supportedProviders.join(', ')}\n`);
242
245
  process.stderr.write(`\nWould publish: ${preview.org_slug}/${manifest.name}@${preview.next_version}\n`);
243
246
  if (shouldUploadBundle) {
@@ -262,7 +265,7 @@ function registerPublishCommand(program) {
262
265
  input_schema: inputSchema,
263
266
  output_schema: outputSchema,
264
267
  tags: manifest.tags,
265
- is_public: options.private ? false : true,
268
+ is_public: options.public ? true : false,
266
269
  supported_providers: supportedProviders,
267
270
  default_models: manifest.default_models,
268
271
  // Local run fields for code agents
@@ -307,7 +310,7 @@ function registerPublishCommand(program) {
307
310
  process.stdout.write(`\nPublished agent: ${org.slug}/${manifest.name}@${assignedVersion}\n`);
308
311
  process.stdout.write(`Type: ${manifest.type}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
309
312
  process.stdout.write(`Providers: ${supportedProviders.join(', ')}\n`);
310
- process.stdout.write(`Public: ${options.private ? 'no' : 'yes'}\n`);
313
+ process.stdout.write(`Public: ${options.public ? 'yes' : 'no'}\n`);
311
314
  if (result.service_key) {
312
315
  process.stdout.write(`\nService key (save this - shown only once):\n`);
313
316
  process.stdout.write(` ${result.service_key}\n`);
@@ -1,133 +1,254 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.registerWorkspaceCommand = registerWorkspaceCommand;
7
+ const cli_table3_1 = __importDefault(require("cli-table3"));
8
+ const chalk_1 = __importDefault(require("chalk"));
4
9
  const config_1 = require("../lib/config");
5
10
  const api_1 = require("../lib/api");
11
+ const errors_1 = require("../lib/errors");
12
+ const analytics_1 = require("../lib/analytics");
13
+ function deriveSlug(name) {
14
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
15
+ }
16
+ async function resolveWorkspaceId(config, slug) {
17
+ const configFile = await (0, config_1.loadConfig)();
18
+ const targetSlug = slug ?? configFile.workspace;
19
+ if (!targetSlug) {
20
+ throw new errors_1.CliError('No workspace specified. Use --workspace <slug> or run `orchagent workspace use <slug>` first.');
21
+ }
22
+ const response = await (0, api_1.request)(config, 'GET', '/workspaces');
23
+ const workspace = response.workspaces.find((w) => w.slug === targetSlug);
24
+ if (!workspace) {
25
+ throw new errors_1.CliError(`Workspace '${targetSlug}' not found.`);
26
+ }
27
+ return workspace.id;
28
+ }
29
+ async function listWorkspaces(config, options) {
30
+ const response = await (0, api_1.request)(config, 'GET', '/workspaces');
31
+ const workspaces = response.workspaces;
32
+ const configFile = await (0, config_1.loadConfig)();
33
+ const currentSlug = configFile.workspace;
34
+ await (0, analytics_1.track)('cli_workspace_list');
35
+ if (options.json) {
36
+ process.stdout.write(`${JSON.stringify(workspaces, null, 2)}\n`);
37
+ return;
38
+ }
39
+ if (workspaces.length === 0) {
40
+ process.stdout.write('No workspaces found.\n');
41
+ process.stdout.write('\nCreate one with: orchagent workspace create <name>\n');
42
+ return;
43
+ }
44
+ const table = new cli_table3_1.default({
45
+ head: [
46
+ '',
47
+ chalk_1.default.bold('Name'),
48
+ chalk_1.default.bold('Slug'),
49
+ chalk_1.default.bold('Type'),
50
+ chalk_1.default.bold('Role'),
51
+ chalk_1.default.bold('Members'),
52
+ ],
53
+ });
54
+ workspaces.forEach((workspace) => {
55
+ const isCurrent = workspace.slug === currentSlug;
56
+ const marker = isCurrent ? chalk_1.default.green('\u2192') : '';
57
+ table.push([
58
+ marker,
59
+ workspace.name,
60
+ workspace.slug,
61
+ workspace.type,
62
+ workspace.role,
63
+ workspace.member_count.toString(),
64
+ ]);
65
+ });
66
+ process.stdout.write(`${table.toString()}\n`);
67
+ }
68
+ async function createWorkspace(config, name, options) {
69
+ const slug = options.slug ?? deriveSlug(name);
70
+ const response = await (0, api_1.request)(config, 'POST', '/workspaces', {
71
+ body: JSON.stringify({ name, slug }),
72
+ headers: { 'Content-Type': 'application/json' },
73
+ });
74
+ await (0, analytics_1.track)('cli_workspace_create', { slug });
75
+ process.stdout.write(chalk_1.default.green('\u2713') + ` Created workspace: ${response.workspace.name} (${response.workspace.slug})\n`);
76
+ }
77
+ async function useWorkspace(slug) {
78
+ const config = await (0, config_1.getResolvedConfig)();
79
+ // Verify workspace exists
80
+ const response = await (0, api_1.request)(config, 'GET', '/workspaces');
81
+ const workspace = response.workspaces.find((w) => w.slug === slug);
82
+ if (!workspace) {
83
+ throw new errors_1.CliError(`Workspace '${slug}' not found.`);
84
+ }
85
+ // Save to config
86
+ const configFile = await (0, config_1.loadConfig)();
87
+ configFile.workspace = slug;
88
+ await (0, config_1.saveConfig)(configFile);
89
+ await (0, analytics_1.track)('cli_workspace_use', { slug });
90
+ process.stdout.write(chalk_1.default.green('\u2713') + ` Now using workspace: ${workspace.name} (${workspace.slug})\n`);
91
+ }
92
+ async function listMembers(config, workspaceSlug, options) {
93
+ const workspaceId = await resolveWorkspaceId(config, workspaceSlug);
94
+ const response = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/members`);
95
+ await (0, analytics_1.track)('cli_workspace_members');
96
+ if (options.json) {
97
+ process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
98
+ return;
99
+ }
100
+ // Members table
101
+ if (response.members.length > 0) {
102
+ process.stdout.write('Members:\n');
103
+ const membersTable = new cli_table3_1.default({
104
+ head: [
105
+ chalk_1.default.bold('Name'),
106
+ chalk_1.default.bold('Email'),
107
+ chalk_1.default.bold('Role'),
108
+ chalk_1.default.bold('Joined'),
109
+ ],
110
+ });
111
+ response.members.forEach((member) => {
112
+ membersTable.push([
113
+ member.name ?? '-',
114
+ member.email ?? '-',
115
+ member.role,
116
+ member.accepted_at ? new Date(member.accepted_at).toLocaleDateString() : '-',
117
+ ]);
118
+ });
119
+ process.stdout.write(`${membersTable.toString()}\n`);
120
+ }
121
+ else {
122
+ process.stdout.write('No members found.\n');
123
+ }
124
+ // Pending invites table (the gateway only returns pending invites)
125
+ if (response.invites.length > 0) {
126
+ process.stdout.write('\nPending Invites:\n');
127
+ const invitesTable = new cli_table3_1.default({
128
+ head: [
129
+ chalk_1.default.bold('Email'),
130
+ chalk_1.default.bold('Role'),
131
+ chalk_1.default.bold('Sent'),
132
+ ],
133
+ });
134
+ response.invites.forEach((invite) => {
135
+ invitesTable.push([
136
+ invite.email,
137
+ invite.role,
138
+ new Date(invite.created_at).toLocaleDateString(),
139
+ ]);
140
+ });
141
+ process.stdout.write(`${invitesTable.toString()}\n`);
142
+ }
143
+ }
144
+ async function inviteMember(config, email, options) {
145
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
146
+ const role = options.role ?? 'member';
147
+ const response = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/invites`, {
148
+ body: JSON.stringify({ email, role }),
149
+ headers: { 'Content-Type': 'application/json' },
150
+ });
151
+ await (0, analytics_1.track)('cli_workspace_invite', { role });
152
+ process.stdout.write(chalk_1.default.green('\u2713') + ` Invited ${email} as ${role}\n`);
153
+ if (response.invite.invite_url) {
154
+ process.stdout.write(`\nInvite URL: ${response.invite.invite_url}\n`);
155
+ }
156
+ }
157
+ async function leaveWorkspace(config, workspaceSlug) {
158
+ const workspaceId = await resolveWorkspaceId(config, workspaceSlug);
159
+ // Get current user's email and members list to find our clerk_user_id
160
+ const [userResponse, membersResponse] = await Promise.all([
161
+ (0, api_1.request)(config, 'GET', '/users/me'),
162
+ (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/members`),
163
+ ]);
164
+ const currentUserEmail = userResponse.user.email;
165
+ const currentMember = membersResponse.members.find((m) => m.email === currentUserEmail);
166
+ if (!currentMember) {
167
+ throw new errors_1.CliError('Could not find your membership in this workspace.');
168
+ }
169
+ await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/members/${currentMember.clerk_user_id}`);
170
+ await (0, analytics_1.track)('cli_workspace_leave');
171
+ // Clear workspace from config if it was the current one
172
+ const configFile = await (0, config_1.loadConfig)();
173
+ if (configFile.workspace) {
174
+ const response = await (0, api_1.request)(config, 'GET', '/workspaces');
175
+ const leftWorkspace = response.workspaces.find((w) => w.id === workspaceId);
176
+ if (leftWorkspace && configFile.workspace === leftWorkspace.slug) {
177
+ delete configFile.workspace;
178
+ await (0, config_1.saveConfig)(configFile);
179
+ }
180
+ }
181
+ process.stdout.write(chalk_1.default.green('\u2713') + ' Left workspace\n');
182
+ }
6
183
  function registerWorkspaceCommand(program) {
7
184
  const workspace = program
8
185
  .command('workspace')
9
186
  .description('Manage workspaces');
10
- // List workspaces
187
+ // workspace list
11
188
  workspace
12
189
  .command('list')
13
190
  .description('List all workspaces')
14
- .action(async () => {
191
+ .option('--json', 'Output raw JSON')
192
+ .action(async (options) => {
15
193
  const config = await (0, config_1.getResolvedConfig)();
16
- const fileConfig = await (0, config_1.loadConfig)();
17
- const workspaces = await (0, api_1.listWorkspaces)(config);
18
- if (workspaces.length === 0) {
19
- process.stdout.write('No workspaces found.\n');
20
- return;
194
+ if (!config.apiKey) {
195
+ throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
21
196
  }
22
- const currentId = fileConfig.current_workspace;
23
- for (const ws of workspaces) {
24
- const isCurrent = ws.id === currentId || ws.slug === currentId;
25
- const marker = isCurrent ? '* ' : ' ';
26
- const type = ws.type === 'team' ? '(team)' : '(personal)';
27
- const members = ws.type === 'team' ? ` - ${ws.member_count} members` : '';
28
- process.stdout.write(`${marker}${ws.slug} - ${ws.name} ${type}${members}\n`);
197
+ await listWorkspaces(config, options);
198
+ });
199
+ // workspace create <name>
200
+ workspace
201
+ .command('create <name>')
202
+ .description('Create a new workspace')
203
+ .option('--slug <slug>', 'Workspace slug (default: derived from name)')
204
+ .action(async (name, options) => {
205
+ const config = await (0, config_1.getResolvedConfig)();
206
+ if (!config.apiKey) {
207
+ throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
29
208
  }
209
+ await createWorkspace(config, name, options);
30
210
  });
31
- // Switch workspace
211
+ // workspace use <slug>
32
212
  workspace
33
213
  .command('use <slug>')
34
- .description('Switch to a different workspace')
214
+ .description('Set the current workspace')
35
215
  .action(async (slug) => {
36
- const config = await (0, config_1.getResolvedConfig)();
37
- const workspaces = await (0, api_1.listWorkspaces)(config);
38
- const target = workspaces.find(ws => ws.slug === slug || ws.id === slug);
39
- if (!target) {
40
- process.stderr.write(`Workspace "${slug}" not found.\n`);
41
- process.exit(1);
42
- }
43
- const fileConfig = await (0, config_1.loadConfig)();
44
- fileConfig.current_workspace = target.id;
45
- await (0, config_1.saveConfig)(fileConfig);
46
- process.stdout.write(`Switched to workspace: ${target.name} (${target.slug})\n`);
216
+ await useWorkspace(slug);
47
217
  });
48
- // Create workspace
218
+ // workspace members [workspace]
49
219
  workspace
50
- .command('create <name>')
51
- .description('Create a new team workspace')
52
- .option('--slug <slug>', 'Workspace URL slug')
53
- .action(async (name, options) => {
220
+ .command('members [workspace]')
221
+ .description('List workspace members and pending invites')
222
+ .option('--json', 'Output raw JSON')
223
+ .action(async (workspaceSlug, options) => {
54
224
  const config = await (0, config_1.getResolvedConfig)();
55
- // Auto-generate slug from name if not provided
56
- const slug = options.slug || name
57
- .toLowerCase()
58
- .replace(/\s+/g, '-')
59
- .replace(/[^a-z0-9-]/g, '')
60
- .replace(/-+/g, '-')
61
- .slice(0, 30);
62
- try {
63
- const workspace = await (0, api_1.createWorkspace)(config, { name, slug });
64
- // Switch to the new workspace
65
- const fileConfig = await (0, config_1.loadConfig)();
66
- fileConfig.current_workspace = workspace.id;
67
- await (0, config_1.saveConfig)(fileConfig);
68
- process.stdout.write(`Created workspace: ${workspace.name} (${workspace.slug})\n`);
69
- process.stdout.write(`Switched to new workspace.\n`);
70
- }
71
- catch (err) {
72
- process.stderr.write(`Failed to create workspace: ${err instanceof Error ? err.message : 'Unknown error'}\n`);
73
- process.exit(1);
225
+ if (!config.apiKey) {
226
+ throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
74
227
  }
228
+ await listMembers(config, workspaceSlug, options);
75
229
  });
76
- // Invite member
230
+ // workspace invite <email>
77
231
  workspace
78
232
  .command('invite <email>')
79
- .description('Invite a member to the current workspace')
80
- .option('--role <role>', 'Role: owner or member', 'member')
233
+ .description('Invite a user to the workspace')
234
+ .option('--role <role>', 'Role for the invited user (default: member)', 'member')
235
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
81
236
  .action(async (email, options) => {
82
237
  const config = await (0, config_1.getResolvedConfig)();
83
- const fileConfig = await (0, config_1.loadConfig)();
84
- if (!fileConfig.current_workspace) {
85
- process.stderr.write('No workspace selected. Run `orch workspace use <slug>` first.\n');
86
- process.exit(1);
87
- }
88
- const role = options.role;
89
- if (role !== 'owner' && role !== 'member') {
90
- process.stderr.write('Role must be "owner" or "member".\n');
91
- process.exit(1);
92
- }
93
- try {
94
- await (0, api_1.inviteToWorkspace)(config, fileConfig.current_workspace, { email, role });
95
- process.stdout.write(`Invited ${email} as ${role}.\n`);
96
- }
97
- catch (err) {
98
- process.stderr.write(`Failed to send invite: ${err instanceof Error ? err.message : 'Unknown error'}\n`);
99
- process.exit(1);
238
+ if (!config.apiKey) {
239
+ throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
100
240
  }
241
+ await inviteMember(config, email, options);
101
242
  });
102
- // List members
243
+ // workspace leave [workspace]
103
244
  workspace
104
- .command('members')
105
- .description('List members of the current workspace')
106
- .action(async () => {
245
+ .command('leave [workspace]')
246
+ .description('Leave a workspace')
247
+ .action(async (workspaceSlug) => {
107
248
  const config = await (0, config_1.getResolvedConfig)();
108
- const fileConfig = await (0, config_1.loadConfig)();
109
- if (!fileConfig.current_workspace) {
110
- process.stderr.write('No workspace selected. Run `orch workspace use <slug>` first.\n');
111
- process.exit(1);
112
- }
113
- try {
114
- const { members, invites } = await (0, api_1.getWorkspaceMembers)(config, fileConfig.current_workspace);
115
- process.stdout.write('Members:\n');
116
- for (const member of members) {
117
- const roleLabel = member.role === 'owner' ? '[owner]' : '[member]';
118
- process.stdout.write(` ${member.clerk_user_id} ${roleLabel}\n`);
119
- }
120
- if (invites.length > 0) {
121
- process.stdout.write('\nPending invites:\n');
122
- for (const invite of invites) {
123
- const expires = new Date(invite.expires_at).toLocaleDateString();
124
- process.stdout.write(` ${invite.email} (${invite.role}) - expires ${expires}\n`);
125
- }
126
- }
127
- }
128
- catch (err) {
129
- process.stderr.write(`Failed to list members: ${err instanceof Error ? err.message : 'Unknown error'}\n`);
130
- process.exit(1);
249
+ if (!config.apiKey) {
250
+ throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
131
251
  }
252
+ await leaveWorkspace(config, workspaceSlug);
132
253
  });
133
254
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Command-line interface for the OrchAgent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "OrchAgent <hello@orchagent.io>",