genbox 1.0.190 → 1.0.191

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,409 @@
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.orgCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const api_1 = require("../api");
11
+ exports.orgCommand = new commander_1.Command('org')
12
+ .description('Manage organizations')
13
+ .action(async () => {
14
+ // Default action: show current context and list organizations
15
+ try {
16
+ // Get current context
17
+ const context = await (0, api_1.fetchApi)('/users/me/context');
18
+ console.log(chalk_1.default.bold('\nCurrent Context'));
19
+ if (context.type === 'organization' && context.organizationUsername) {
20
+ console.log(` Working in: ${chalk_1.default.cyan(context.organizationUsername)} ${chalk_1.default.dim('(organization)')}`);
21
+ }
22
+ else {
23
+ console.log(` Working in: ${chalk_1.default.cyan('personal')} ${chalk_1.default.dim('(your account)')}`);
24
+ }
25
+ // List organizations
26
+ const orgs = await (0, api_1.fetchApi)('/organizations');
27
+ console.log(chalk_1.default.bold('\nYour Organizations'));
28
+ if (orgs.length === 0) {
29
+ console.log(chalk_1.default.dim(' No organizations yet.'));
30
+ console.log(chalk_1.default.dim('\n Create one with: gb org create <name>'));
31
+ }
32
+ else {
33
+ for (const org of orgs) {
34
+ const roleColor = org.role === 'owner'
35
+ ? chalk_1.default.yellow
36
+ : org.role === 'admin'
37
+ ? chalk_1.default.blue
38
+ : chalk_1.default.dim;
39
+ console.log(` ${chalk_1.default.cyan(org.username)} - ${org.displayName} ${roleColor(`[${org.role}]`)}`);
40
+ console.log(chalk_1.default.dim(` ${org.memberCount} members`));
41
+ }
42
+ console.log(chalk_1.default.dim('\n Switch context: gb org switch <username>'));
43
+ }
44
+ }
45
+ catch (error) {
46
+ if (error instanceof api_1.AuthenticationError) {
47
+ (0, api_1.handleApiError)(error);
48
+ return;
49
+ }
50
+ console.error(chalk_1.default.red('Error:'), error.message);
51
+ }
52
+ });
53
+ // Sub-command: list organizations
54
+ exports.orgCommand
55
+ .command('list')
56
+ .alias('ls')
57
+ .description('List organizations you are a member of')
58
+ .action(async () => {
59
+ try {
60
+ const orgs = await (0, api_1.fetchApi)('/organizations');
61
+ if (orgs.length === 0) {
62
+ console.log(chalk_1.default.dim('No organizations found.'));
63
+ console.log(chalk_1.default.dim('\nCreate one with: gb org create <name>'));
64
+ return;
65
+ }
66
+ console.log(chalk_1.default.bold('\nOrganizations'));
67
+ for (const org of orgs) {
68
+ const roleColor = org.role === 'owner'
69
+ ? chalk_1.default.yellow
70
+ : org.role === 'admin'
71
+ ? chalk_1.default.blue
72
+ : chalk_1.default.dim;
73
+ console.log(`\n ${chalk_1.default.cyan(org.username)}`);
74
+ console.log(` Display name: ${org.displayName}`);
75
+ console.log(` Role: ${roleColor(org.role)}`);
76
+ console.log(` Members: ${org.memberCount}`);
77
+ }
78
+ }
79
+ catch (error) {
80
+ if (error instanceof api_1.AuthenticationError) {
81
+ (0, api_1.handleApiError)(error);
82
+ return;
83
+ }
84
+ console.error(chalk_1.default.red('Error:'), error.message);
85
+ }
86
+ });
87
+ // Sub-command: create organization
88
+ exports.orgCommand
89
+ .command('create <username>')
90
+ .description('Create a new organization')
91
+ .option('-n, --name <displayName>', 'Display name for the organization')
92
+ .option('-d, --description <desc>', 'Organization description')
93
+ .action(async (username, options) => {
94
+ try {
95
+ const normalized = username.toLowerCase().trim();
96
+ // Validate format
97
+ if (normalized.length < 3) {
98
+ console.error(chalk_1.default.red('Organization username must be at least 3 characters'));
99
+ return;
100
+ }
101
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(normalized)) {
102
+ console.error(chalk_1.default.red('Username must be lowercase, alphanumeric with hyphens (no start/end hyphen)'));
103
+ return;
104
+ }
105
+ // Check availability
106
+ const check = await (0, api_1.fetchApi)(`/usernames/${normalized}/check`);
107
+ if (!check.available) {
108
+ console.error(chalk_1.default.red(`Username '${normalized}' is not available: ${check.reason}`));
109
+ return;
110
+ }
111
+ // Prompt for display name if not provided
112
+ let displayName = options.name;
113
+ if (!displayName) {
114
+ const answers = await inquirer_1.default.prompt([
115
+ {
116
+ type: 'input',
117
+ name: 'displayName',
118
+ message: 'Display name:',
119
+ default: normalized,
120
+ validate: (input) => input.trim().length > 0 || 'Display name is required',
121
+ },
122
+ ]);
123
+ displayName = answers.displayName;
124
+ }
125
+ // Create organization
126
+ const result = await (0, api_1.fetchApi)('/organizations', {
127
+ method: 'POST',
128
+ body: JSON.stringify({
129
+ username: normalized,
130
+ displayName,
131
+ description: options.description,
132
+ }),
133
+ });
134
+ console.log(chalk_1.default.green(`\n✓ Organization '${result.username}' created!`));
135
+ console.log(chalk_1.default.dim(` Display name: ${result.displayName}`));
136
+ console.log(chalk_1.default.dim(`\n Switch to it: gb org switch ${result.username}`));
137
+ console.log(chalk_1.default.dim(` Invite members: gb org invite ${result.username} <email>`));
138
+ }
139
+ catch (error) {
140
+ if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
141
+ console.log(chalk_1.default.dim('Cancelled.'));
142
+ return;
143
+ }
144
+ if (error instanceof api_1.AuthenticationError) {
145
+ (0, api_1.handleApiError)(error);
146
+ return;
147
+ }
148
+ console.error(chalk_1.default.red('Error:'), error.message);
149
+ }
150
+ });
151
+ // Sub-command: show organization info
152
+ exports.orgCommand
153
+ .command('info [username]')
154
+ .description('Show organization details')
155
+ .action(async (username) => {
156
+ try {
157
+ // If no username provided, use current context
158
+ if (!username) {
159
+ const context = await (0, api_1.fetchApi)('/users/me/context');
160
+ if (context.type !== 'organization' || !context.organizationUsername) {
161
+ console.error(chalk_1.default.red('Not in organization context. Specify username or switch context.'));
162
+ console.log(chalk_1.default.dim('\n gb org info <username>'));
163
+ console.log(chalk_1.default.dim(' gb org switch <username>'));
164
+ return;
165
+ }
166
+ username = context.organizationUsername;
167
+ }
168
+ const org = await (0, api_1.fetchApi)(`/organizations/${username}`);
169
+ if (!org.id) {
170
+ // Non-member view
171
+ console.log(chalk_1.default.bold(`\nOrganization: ${chalk_1.default.cyan(org.username)}`));
172
+ console.log(` Display name: ${org.displayName}`);
173
+ if (org.description)
174
+ console.log(` Description: ${org.description}`);
175
+ console.log(chalk_1.default.dim('\n You are not a member of this organization.'));
176
+ return;
177
+ }
178
+ // Member view
179
+ console.log(chalk_1.default.bold(`\nOrganization: ${chalk_1.default.cyan(org.username)}`));
180
+ console.log(` Display name: ${org.displayName}`);
181
+ if (org.description)
182
+ console.log(` Description: ${org.description}`);
183
+ console.log(` Your role: ${chalk_1.default.cyan(org.myRole)}`);
184
+ console.log(` Tier: ${org.tier || 'free'}`);
185
+ console.log(` Credits: ${org.credits || 0} + ${org.addonCredits || 0} addon`);
186
+ if (org.members && org.members.length > 0) {
187
+ console.log(chalk_1.default.bold('\n Members:'));
188
+ for (const member of org.members) {
189
+ const roleColor = member.role === 'owner'
190
+ ? chalk_1.default.yellow
191
+ : member.role === 'admin'
192
+ ? chalk_1.default.blue
193
+ : chalk_1.default.dim;
194
+ console.log(` ${member.email} ${roleColor(`[${member.role}]`)}`);
195
+ }
196
+ }
197
+ if (org.pendingInvitations && org.pendingInvitations.length > 0) {
198
+ console.log(chalk_1.default.bold('\n Pending Invitations:'));
199
+ for (const inv of org.pendingInvitations) {
200
+ console.log(` ${inv.email} ${chalk_1.default.dim(`[${inv.role}]`)} - expires ${inv.expiresAt}`);
201
+ }
202
+ }
203
+ }
204
+ catch (error) {
205
+ if (error instanceof api_1.AuthenticationError) {
206
+ (0, api_1.handleApiError)(error);
207
+ return;
208
+ }
209
+ console.error(chalk_1.default.red('Error:'), error.message);
210
+ }
211
+ });
212
+ // Sub-command: switch context
213
+ exports.orgCommand
214
+ .command('switch <username>')
215
+ .description('Switch to organization context (use "personal" for personal account)')
216
+ .action(async (username) => {
217
+ try {
218
+ if (username.toLowerCase() === 'personal' || username.toLowerCase() === 'me') {
219
+ // Switch to personal context
220
+ await (0, api_1.fetchApi)('/users/me/context', {
221
+ method: 'PUT',
222
+ body: JSON.stringify({ type: 'personal' }),
223
+ });
224
+ console.log(chalk_1.default.green('✓ Switched to personal context'));
225
+ console.log(chalk_1.default.dim(' New genboxes will be created under your personal account.'));
226
+ return;
227
+ }
228
+ // Get organization details
229
+ const org = await (0, api_1.fetchApi)(`/organizations/${username}`);
230
+ if (!org.id) {
231
+ console.error(chalk_1.default.red(`You are not a member of organization '${username}'`));
232
+ return;
233
+ }
234
+ // Switch to organization context
235
+ await (0, api_1.fetchApi)('/users/me/context', {
236
+ method: 'PUT',
237
+ body: JSON.stringify({
238
+ type: 'organization',
239
+ organizationId: org.id,
240
+ }),
241
+ });
242
+ console.log(chalk_1.default.green(`✓ Switched to organization: ${org.displayName}`));
243
+ console.log(chalk_1.default.dim(` New genboxes will be created under ${org.username}.`));
244
+ console.log(chalk_1.default.dim(` URLs: https://{genbox}.app.${org.username}.genbox.dev`));
245
+ }
246
+ catch (error) {
247
+ if (error instanceof api_1.AuthenticationError) {
248
+ (0, api_1.handleApiError)(error);
249
+ return;
250
+ }
251
+ console.error(chalk_1.default.red('Error:'), error.message);
252
+ }
253
+ });
254
+ // Sub-command: invite member
255
+ exports.orgCommand
256
+ .command('invite <username> <email>')
257
+ .description('Invite a member to the organization')
258
+ .option('-r, --role <role>', 'Role for the new member (admin, member)', 'member')
259
+ .action(async (username, email, options) => {
260
+ try {
261
+ if (!['admin', 'member'].includes(options.role)) {
262
+ console.error(chalk_1.default.red('Role must be "admin" or "member"'));
263
+ return;
264
+ }
265
+ const result = await (0, api_1.fetchApi)(`/organizations/${username}/members`, {
266
+ method: 'POST',
267
+ body: JSON.stringify({ email, role: options.role }),
268
+ });
269
+ console.log(chalk_1.default.green(`✓ Invitation sent to ${email}`));
270
+ console.log(chalk_1.default.dim(` Role: ${result.role}`));
271
+ console.log(chalk_1.default.dim(` Expires: ${result.expiresAt}`));
272
+ }
273
+ catch (error) {
274
+ if (error instanceof api_1.AuthenticationError) {
275
+ (0, api_1.handleApiError)(error);
276
+ return;
277
+ }
278
+ console.error(chalk_1.default.red('Error:'), error.message);
279
+ }
280
+ });
281
+ // Sub-command: list members
282
+ exports.orgCommand
283
+ .command('members [username]')
284
+ .description('List organization members')
285
+ .action(async (username) => {
286
+ try {
287
+ // If no username provided, use current context
288
+ if (!username) {
289
+ const context = await (0, api_1.fetchApi)('/users/me/context');
290
+ if (context.type !== 'organization' || !context.organizationUsername) {
291
+ console.error(chalk_1.default.red('Not in organization context. Specify username or switch context.'));
292
+ return;
293
+ }
294
+ username = context.organizationUsername;
295
+ }
296
+ const result = await (0, api_1.fetchApi)(`/organizations/${username}/members`);
297
+ console.log(chalk_1.default.bold(`\nMembers of ${chalk_1.default.cyan(username)}`));
298
+ if (result.members && result.members.length > 0) {
299
+ for (const member of result.members) {
300
+ const roleColor = member.role === 'owner'
301
+ ? chalk_1.default.yellow
302
+ : member.role === 'admin'
303
+ ? chalk_1.default.blue
304
+ : chalk_1.default.dim;
305
+ console.log(` ${member.email} ${roleColor(`[${member.role}]`)}`);
306
+ }
307
+ }
308
+ if (result.pendingInvitations && result.pendingInvitations.length > 0) {
309
+ console.log(chalk_1.default.bold('\nPending Invitations'));
310
+ for (const inv of result.pendingInvitations) {
311
+ console.log(` ${inv.email} ${chalk_1.default.dim(`[${inv.role}]`)} - pending`);
312
+ }
313
+ }
314
+ }
315
+ catch (error) {
316
+ if (error instanceof api_1.AuthenticationError) {
317
+ (0, api_1.handleApiError)(error);
318
+ return;
319
+ }
320
+ console.error(chalk_1.default.red('Error:'), error.message);
321
+ }
322
+ });
323
+ // Sub-command: leave organization
324
+ exports.orgCommand
325
+ .command('leave <username>')
326
+ .description('Leave an organization')
327
+ .action(async (username) => {
328
+ try {
329
+ // Get current user info
330
+ const user = await (0, api_1.fetchApi)('/users/me');
331
+ // Confirm
332
+ const { confirm } = await inquirer_1.default.prompt([
333
+ {
334
+ type: 'confirm',
335
+ name: 'confirm',
336
+ message: `Are you sure you want to leave organization '${username}'?`,
337
+ default: false,
338
+ },
339
+ ]);
340
+ if (!confirm) {
341
+ console.log(chalk_1.default.dim('Cancelled.'));
342
+ return;
343
+ }
344
+ await (0, api_1.fetchApi)(`/organizations/${username}/members/${user.externalId}`, {
345
+ method: 'DELETE',
346
+ });
347
+ console.log(chalk_1.default.green(`✓ You have left organization '${username}'`));
348
+ // If we were in that org's context, switch to personal
349
+ const context = await (0, api_1.fetchApi)('/users/me/context');
350
+ if (context.type === 'organization' && context.organizationUsername === username) {
351
+ await (0, api_1.fetchApi)('/users/me/context', {
352
+ method: 'PUT',
353
+ body: JSON.stringify({ type: 'personal' }),
354
+ });
355
+ console.log(chalk_1.default.dim(' Switched to personal context.'));
356
+ }
357
+ }
358
+ catch (error) {
359
+ if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
360
+ console.log(chalk_1.default.dim('Cancelled.'));
361
+ return;
362
+ }
363
+ if (error instanceof api_1.AuthenticationError) {
364
+ (0, api_1.handleApiError)(error);
365
+ return;
366
+ }
367
+ console.error(chalk_1.default.red('Error:'), error.message);
368
+ }
369
+ });
370
+ // Sub-command: delete organization
371
+ exports.orgCommand
372
+ .command('delete <username>')
373
+ .description('Delete an organization (owner only)')
374
+ .action(async (username) => {
375
+ try {
376
+ // Confirm with org username
377
+ const { confirmName } = await inquirer_1.default.prompt([
378
+ {
379
+ type: 'input',
380
+ name: 'confirmName',
381
+ message: `Type the organization username "${username}" to confirm deletion:`,
382
+ },
383
+ ]);
384
+ if (confirmName !== username) {
385
+ console.log(chalk_1.default.red('Organization name does not match. Aborting.'));
386
+ return;
387
+ }
388
+ await (0, api_1.fetchApi)(`/organizations/${username}`, {
389
+ method: 'DELETE',
390
+ });
391
+ console.log(chalk_1.default.green(`✓ Organization '${username}' has been deleted`));
392
+ // Switch to personal context
393
+ await (0, api_1.fetchApi)('/users/me/context', {
394
+ method: 'PUT',
395
+ body: JSON.stringify({ type: 'personal' }),
396
+ });
397
+ }
398
+ catch (error) {
399
+ if (error.name === 'ExitPromptError' || error.message?.includes('force closed')) {
400
+ console.log(chalk_1.default.dim('Cancelled.'));
401
+ return;
402
+ }
403
+ if (error instanceof api_1.AuthenticationError) {
404
+ (0, api_1.handleApiError)(error);
405
+ return;
406
+ }
407
+ console.error(chalk_1.default.red('Error:'), error.message);
408
+ }
409
+ });
package/dist/index.js CHANGED
@@ -82,6 +82,7 @@ const logs_1 = require("./commands/logs");
82
82
  const setup_1 = require("./commands/setup");
83
83
  const new_1 = require("./commands/new");
84
84
  const username_1 = require("./commands/username");
85
+ const org_1 = require("./commands/org");
85
86
  const config_store_1 = require("./config-store");
86
87
  const logo_1 = require("./utils/logo");
87
88
  const fs = __importStar(require("fs"));
@@ -314,5 +315,6 @@ program
314
315
  .addCommand(setup_1.setupCommand)
315
316
  .addCommand(new_1.newCommand)
316
317
  .addCommand(username_1.usernameCommand)
318
+ .addCommand(org_1.orgCommand)
317
319
  .addCommand(completion_1.completeCommand, { hidden: true }); // Internal command for shell completions
318
320
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.190",
3
+ "version": "1.0.191",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {