@nexical/cli 0.1.0
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/.github/workflows/deploy.yml +34 -0
- package/LICENSE +201 -0
- package/README.md +183 -0
- package/dist/chunk-FDJVHO4O.js +41 -0
- package/dist/chunk-FDJVHO4O.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/src/commands/admin/create-user.d.ts +15 -0
- package/dist/src/commands/admin/create-user.js +49 -0
- package/dist/src/commands/admin/create-user.js.map +1 -0
- package/dist/src/commands/branch/create.d.ts +19 -0
- package/dist/src/commands/branch/create.js +59 -0
- package/dist/src/commands/branch/create.js.map +1 -0
- package/dist/src/commands/branch/delete.d.ts +15 -0
- package/dist/src/commands/branch/delete.js +50 -0
- package/dist/src/commands/branch/delete.js.map +1 -0
- package/dist/src/commands/branch/get.d.ts +15 -0
- package/dist/src/commands/branch/get.js +53 -0
- package/dist/src/commands/branch/get.js.map +1 -0
- package/dist/src/commands/branch/list.d.ts +15 -0
- package/dist/src/commands/branch/list.js +51 -0
- package/dist/src/commands/branch/list.js.map +1 -0
- package/dist/src/commands/job/get.d.ts +15 -0
- package/dist/src/commands/job/get.js +62 -0
- package/dist/src/commands/job/get.js.map +1 -0
- package/dist/src/commands/job/list.d.ts +15 -0
- package/dist/src/commands/job/list.js +57 -0
- package/dist/src/commands/job/list.js.map +1 -0
- package/dist/src/commands/job/logs.d.ts +15 -0
- package/dist/src/commands/job/logs.js +67 -0
- package/dist/src/commands/job/logs.js.map +1 -0
- package/dist/src/commands/job/trigger.d.ts +19 -0
- package/dist/src/commands/job/trigger.js +74 -0
- package/dist/src/commands/job/trigger.js.map +1 -0
- package/dist/src/commands/login.d.ts +8 -0
- package/dist/src/commands/login.js +31 -0
- package/dist/src/commands/login.js.map +1 -0
- package/dist/src/commands/project/create.d.ts +24 -0
- package/dist/src/commands/project/create.js +63 -0
- package/dist/src/commands/project/create.js.map +1 -0
- package/dist/src/commands/project/delete.d.ts +20 -0
- package/dist/src/commands/project/delete.js +58 -0
- package/dist/src/commands/project/delete.js.map +1 -0
- package/dist/src/commands/project/get.d.ts +15 -0
- package/dist/src/commands/project/get.js +49 -0
- package/dist/src/commands/project/get.js.map +1 -0
- package/dist/src/commands/project/list.d.ts +15 -0
- package/dist/src/commands/project/list.js +45 -0
- package/dist/src/commands/project/list.js.map +1 -0
- package/dist/src/commands/project/update.d.ts +19 -0
- package/dist/src/commands/project/update.js +66 -0
- package/dist/src/commands/project/update.js.map +1 -0
- package/dist/src/commands/team/create.d.ts +19 -0
- package/dist/src/commands/team/create.js +45 -0
- package/dist/src/commands/team/create.js.map +1 -0
- package/dist/src/commands/team/delete.d.ts +20 -0
- package/dist/src/commands/team/delete.js +52 -0
- package/dist/src/commands/team/delete.js.map +1 -0
- package/dist/src/commands/team/get.d.ts +15 -0
- package/dist/src/commands/team/get.js +42 -0
- package/dist/src/commands/team/get.js.map +1 -0
- package/dist/src/commands/team/list.d.ts +8 -0
- package/dist/src/commands/team/list.js +30 -0
- package/dist/src/commands/team/list.js.map +1 -0
- package/dist/src/commands/team/member/invite.d.ts +20 -0
- package/dist/src/commands/team/member/invite.js +54 -0
- package/dist/src/commands/team/member/invite.js.map +1 -0
- package/dist/src/commands/team/member/remove.d.ts +15 -0
- package/dist/src/commands/team/member/remove.js +43 -0
- package/dist/src/commands/team/member/remove.js.map +1 -0
- package/dist/src/commands/team/update.d.ts +19 -0
- package/dist/src/commands/team/update.js +55 -0
- package/dist/src/commands/team/update.js.map +1 -0
- package/dist/src/commands/token/generate.d.ts +19 -0
- package/dist/src/commands/token/generate.js +48 -0
- package/dist/src/commands/token/generate.js.map +1 -0
- package/dist/src/commands/token/list.d.ts +8 -0
- package/dist/src/commands/token/list.js +31 -0
- package/dist/src/commands/token/list.js.map +1 -0
- package/dist/src/commands/token/revoke.d.ts +15 -0
- package/dist/src/commands/token/revoke.js +38 -0
- package/dist/src/commands/token/revoke.js.map +1 -0
- package/dist/src/commands/whoami.d.ts +8 -0
- package/dist/src/commands/whoami.js +26 -0
- package/dist/src/commands/whoami.js.map +1 -0
- package/dist/src/utils/nexical-client.d.ts +10 -0
- package/dist/src/utils/nexical-client.js +12 -0
- package/dist/src/utils/nexical-client.js.map +1 -0
- package/index.ts +14 -0
- package/package.json +32 -0
- package/src/commands/admin/create-user.ts +46 -0
- package/src/commands/branch/create.ts +57 -0
- package/src/commands/branch/delete.ts +47 -0
- package/src/commands/branch/get.ts +50 -0
- package/src/commands/branch/list.ts +50 -0
- package/src/commands/job/get.ts +59 -0
- package/src/commands/job/list.ts +56 -0
- package/src/commands/job/logs.ts +67 -0
- package/src/commands/job/trigger.ts +73 -0
- package/src/commands/login.ts +31 -0
- package/src/commands/project/create.ts +61 -0
- package/src/commands/project/delete.ts +56 -0
- package/src/commands/project/get.ts +46 -0
- package/src/commands/project/list.ts +44 -0
- package/src/commands/project/update.ts +63 -0
- package/src/commands/team/create.ts +43 -0
- package/src/commands/team/delete.ts +50 -0
- package/src/commands/team/get.ts +39 -0
- package/src/commands/team/list.ts +26 -0
- package/src/commands/team/member/invite.ts +56 -0
- package/src/commands/team/member/remove.ts +40 -0
- package/src/commands/team/update.ts +53 -0
- package/src/commands/token/generate.ts +45 -0
- package/src/commands/token/list.ts +27 -0
- package/src/commands/token/revoke.ts +35 -0
- package/src/commands/whoami.ts +21 -0
- package/src/utils/nexical-client.ts +40 -0
- package/test/e2e/.gitkeep +0 -0
- package/test/integration/commands/admin/create-user.test.ts +51 -0
- package/test/integration/commands/branch/create.test.ts +51 -0
- package/test/integration/commands/branch/delete.test.ts +43 -0
- package/test/integration/commands/branch/get.test.ts +49 -0
- package/test/integration/commands/branch/list.test.ts +47 -0
- package/test/integration/commands/job/get.test.ts +54 -0
- package/test/integration/commands/job/list.test.ts +47 -0
- package/test/integration/commands/job/logs.test.ts +47 -0
- package/test/integration/commands/job/trigger.test.ts +57 -0
- package/test/integration/commands/login.test.ts +62 -0
- package/test/integration/commands/project/create.test.ts +53 -0
- package/test/integration/commands/project/delete.test.ts +43 -0
- package/test/integration/commands/project/get.test.ts +51 -0
- package/test/integration/commands/project/list.test.ts +47 -0
- package/test/integration/commands/project/update.test.ts +53 -0
- package/test/integration/commands/team/create.test.ts +53 -0
- package/test/integration/commands/team/delete.test.ts +43 -0
- package/test/integration/commands/team/get.test.ts +50 -0
- package/test/integration/commands/team/list.test.ts +47 -0
- package/test/integration/commands/team/member/invite.test.ts +46 -0
- package/test/integration/commands/team/member/remove.test.ts +43 -0
- package/test/integration/commands/team/update.test.ts +50 -0
- package/test/integration/commands/token/generate.test.ts +51 -0
- package/test/integration/commands/token/list.test.ts +47 -0
- package/test/integration/commands/token/revoke.test.ts +43 -0
- package/test/integration/commands/whoami.test.ts +49 -0
- package/test/unit/commands/admin/create-user.test.ts +51 -0
- package/test/unit/commands/branch/create.test.ts +57 -0
- package/test/unit/commands/branch/delete.test.ts +49 -0
- package/test/unit/commands/branch/get.test.ts +67 -0
- package/test/unit/commands/branch/list.test.ts +62 -0
- package/test/unit/commands/job/get.test.ts +76 -0
- package/test/unit/commands/job/list.test.ts +62 -0
- package/test/unit/commands/job/logs.test.ts +60 -0
- package/test/unit/commands/job/trigger.test.ts +75 -0
- package/test/unit/commands/login.test.ts +64 -0
- package/test/unit/commands/project/create.test.ts +64 -0
- package/test/unit/commands/project/delete.test.ts +72 -0
- package/test/unit/commands/project/get.test.ts +73 -0
- package/test/unit/commands/project/list.test.ts +62 -0
- package/test/unit/commands/project/update.test.ts +58 -0
- package/test/unit/commands/team/create.test.ts +68 -0
- package/test/unit/commands/team/delete.test.ts +71 -0
- package/test/unit/commands/team/get.test.ts +70 -0
- package/test/unit/commands/team/list.test.ts +56 -0
- package/test/unit/commands/team/member/invite.test.ts +52 -0
- package/test/unit/commands/team/member/remove.test.ts +49 -0
- package/test/unit/commands/team/update.test.ts +63 -0
- package/test/unit/commands/token/generate.test.ts +65 -0
- package/test/unit/commands/token/list.test.ts +58 -0
- package/test/unit/commands/token/revoke.test.ts +49 -0
- package/test/unit/commands/whoami.test.ts +49 -0
- package/test/unit/utils/nexical-client.test.ts +98 -0
- package/test/utils/integration-helpers.ts +22 -0
- package/tsconfig.json +26 -0
- package/tsup.config.ts +18 -0
- package/vitest.config.ts +15 -0
- package/vitest.e2e.config.ts +10 -0
- package/vitest.integration.config.ts +22 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class ProjectsUpdateCommand extends BaseCommand {
|
|
6
|
+
static description = 'Update project details';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'teamId',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Team ID',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'projectId',
|
|
17
|
+
required: true,
|
|
18
|
+
description: 'Project ID',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
options: [
|
|
22
|
+
{
|
|
23
|
+
name: '--name <name>',
|
|
24
|
+
description: 'New name',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: '--repo <url>',
|
|
28
|
+
description: 'New repo URL',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: '--prod <url>',
|
|
32
|
+
description: 'New production URL',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: '--mode <mode>',
|
|
36
|
+
description: 'New mode',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
async run(options: any) {
|
|
42
|
+
const client = getClient();
|
|
43
|
+
const { teamId, projectId, name, repo, prod, mode } = options;
|
|
44
|
+
const tid = parseInt(teamId, 10);
|
|
45
|
+
const pid = parseInt(projectId, 10);
|
|
46
|
+
|
|
47
|
+
if (isNaN(tid) || isNaN(pid)) {
|
|
48
|
+
this.error('IDs must be numbers.');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const project = await client.projects.update(tid, pid, {
|
|
54
|
+
name,
|
|
55
|
+
repoUrl: repo,
|
|
56
|
+
productionUrl: prod,
|
|
57
|
+
});
|
|
58
|
+
this.success(`Project ${project.id} updated!`);
|
|
59
|
+
} catch (error: any) {
|
|
60
|
+
this.error(`Failed to update project: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsCreateCommand extends BaseCommand {
|
|
6
|
+
static description = 'Create a new team';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'name',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Name of the team',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
options: [
|
|
17
|
+
{
|
|
18
|
+
name: '--slug <slug>',
|
|
19
|
+
description: 'Custom URL slug for the team',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async run(options: any) {
|
|
25
|
+
const client = getClient();
|
|
26
|
+
const { name, slug } = options;
|
|
27
|
+
// Generate slug from name if not provided (simple version)
|
|
28
|
+
const finalSlug = slug || name.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const team = await client.teams.create({
|
|
32
|
+
name,
|
|
33
|
+
slug: finalSlug,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.success(`Team "${team.name}" created successfully!`);
|
|
37
|
+
this.info(`ID: ${team.id}`);
|
|
38
|
+
this.info(`Slug: ${team.slug}`);
|
|
39
|
+
} catch (error: any) {
|
|
40
|
+
this.error(`Failed to create team: ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsDeleteCommand extends BaseCommand {
|
|
6
|
+
static description = 'Delete a team';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'teamId',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Team ID',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
options: [
|
|
17
|
+
{
|
|
18
|
+
name: '--confirm',
|
|
19
|
+
description: 'Skip confirmation',
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
async run(options: any) {
|
|
26
|
+
const client = getClient();
|
|
27
|
+
const { teamId, confirm } = options;
|
|
28
|
+
const tid = parseInt(teamId, 10);
|
|
29
|
+
|
|
30
|
+
if (isNaN(tid)) {
|
|
31
|
+
this.error('Team ID must be a number.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!confirm) {
|
|
36
|
+
const answer = await this.prompt(`Are you sure you want to delete team ${tid}? (yes/no)`);
|
|
37
|
+
if (answer.toLowerCase() !== 'yes') {
|
|
38
|
+
this.info('Aborted.');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await client.teams.delete(tid);
|
|
45
|
+
this.success(`Team ${tid} deleted.`);
|
|
46
|
+
} catch (error: any) {
|
|
47
|
+
this.error(`Failed to delete team: ${error.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsGetCommand extends BaseCommand {
|
|
6
|
+
static description = 'Get team details';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'teamId',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Team ID',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async run(options: any) {
|
|
19
|
+
const client = getClient();
|
|
20
|
+
const { teamId } = options;
|
|
21
|
+
const tid = parseInt(teamId, 10);
|
|
22
|
+
|
|
23
|
+
if (isNaN(tid)) {
|
|
24
|
+
this.error('Team ID must be a number.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const team = await client.teams.get(tid);
|
|
30
|
+
this.info(`Team Details:`);
|
|
31
|
+
this.info(` ID: ${team.id}`);
|
|
32
|
+
this.info(` Name: ${team.name}`);
|
|
33
|
+
this.info(` Slug: ${team.slug}`);
|
|
34
|
+
this.info(` Role: ${team.role || 'member'}`);
|
|
35
|
+
} catch (error: any) {
|
|
36
|
+
this.error(`Failed to get team: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsListCommand extends BaseCommand {
|
|
6
|
+
static description = 'List all teams you belong to';
|
|
7
|
+
|
|
8
|
+
async run() {
|
|
9
|
+
const client = getClient();
|
|
10
|
+
try {
|
|
11
|
+
const teams = await client.teams.list();
|
|
12
|
+
|
|
13
|
+
if (teams.length === 0) {
|
|
14
|
+
this.info('You are not a member of any teams.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.info('Your Teams:');
|
|
19
|
+
for (const team of teams) {
|
|
20
|
+
this.info(`- ${team.name} (${team.slug}) [${team.role || 'member'}]`);
|
|
21
|
+
}
|
|
22
|
+
} catch (error: any) {
|
|
23
|
+
this.error(`Failed to list teams: ${error.message}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsInviteCommand extends BaseCommand {
|
|
6
|
+
static description = 'Invite a user to a team';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'teamId',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'ID of the team',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'email',
|
|
17
|
+
required: true,
|
|
18
|
+
description: 'Email of the user to invite',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
options: [
|
|
22
|
+
{
|
|
23
|
+
name: '--role <role>',
|
|
24
|
+
description: 'Role for the new member (admin, member)',
|
|
25
|
+
default: 'member',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
async run(options: any) {
|
|
31
|
+
const client = getClient();
|
|
32
|
+
const { teamId, email, role } = options;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// teamId from args is string, but SDK expects number usually?
|
|
36
|
+
// Docs say: Team { id: number; ... }
|
|
37
|
+
// SDK methods: inviteMember(id, data)
|
|
38
|
+
// BaseCommand options are typically strings from CLI args.
|
|
39
|
+
// I should parse teamId to number.
|
|
40
|
+
const tid = parseInt(teamId, 10);
|
|
41
|
+
if (isNaN(tid)) {
|
|
42
|
+
this.error('Team ID must be a number.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await client.teams.inviteMember(tid, {
|
|
47
|
+
email,
|
|
48
|
+
role: role as any, // Cast to expected enum if needed
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
this.success(`Invited ${email} to team ${tid} as ${role}.`);
|
|
52
|
+
} catch (error: any) {
|
|
53
|
+
this.error(`Failed to invite member: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsMembersRemoveCommand extends BaseCommand {
|
|
6
|
+
static description = 'Remove a member from a team';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'teamId',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Team ID',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'userId',
|
|
17
|
+
required: true,
|
|
18
|
+
description: 'User ID (UUID)',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
async run(options: any) {
|
|
24
|
+
const client = getClient();
|
|
25
|
+
const { teamId, userId } = options;
|
|
26
|
+
const tid = parseInt(teamId, 10);
|
|
27
|
+
|
|
28
|
+
if (isNaN(tid)) {
|
|
29
|
+
this.error('Team ID must be a number.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await client.teams.removeMember(tid, userId);
|
|
35
|
+
this.success(`User ${userId} removed from team ${tid}.`);
|
|
36
|
+
} catch (error: any) {
|
|
37
|
+
this.error(`Failed to remove member: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class TeamsUpdateCommand extends BaseCommand {
|
|
6
|
+
static description = 'Update team details';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'teamId',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Team ID',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
options: [
|
|
17
|
+
{
|
|
18
|
+
name: '--name <name>',
|
|
19
|
+
description: 'New name',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: '--slug <slug>',
|
|
23
|
+
description: 'New slug',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async run(options: any) {
|
|
29
|
+
const client = getClient();
|
|
30
|
+
const { teamId, name, slug } = options;
|
|
31
|
+
const tid = parseInt(teamId, 10);
|
|
32
|
+
|
|
33
|
+
if (isNaN(tid)) {
|
|
34
|
+
this.error('Team ID must be a number.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!name && !slug) {
|
|
39
|
+
this.warn('No updates provided. Use --name or --slug.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const team = await client.teams.update(tid, {
|
|
45
|
+
name,
|
|
46
|
+
});
|
|
47
|
+
this.success(`Team ${team.id} updated!`);
|
|
48
|
+
this.info(`Name: ${team.name}`);
|
|
49
|
+
} catch (error: any) {
|
|
50
|
+
this.error(`Failed to update team: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class AuthTokensGenerateCommand extends BaseCommand {
|
|
6
|
+
static description = 'Generate a new API token';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'name',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'Name of the token',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
options: [
|
|
17
|
+
{
|
|
18
|
+
name: '--scopes <scopes>',
|
|
19
|
+
description: 'Comma-separated list of scopes',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: '--expires <isoDate>',
|
|
23
|
+
description: 'Expiration date (ISO 8601)',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async run(options: any) {
|
|
29
|
+
const client = getClient();
|
|
30
|
+
const { name, scopes, expires } = options;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const token = await client.auth.generateToken({
|
|
34
|
+
name,
|
|
35
|
+
scopes: scopes ? scopes.split(',') : undefined,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.success(`Token "${name}" generated!`);
|
|
39
|
+
this.warn(`Token: ${token.token}`);
|
|
40
|
+
this.warn('Make sure to copy it now. You won\'t be able to see it again!');
|
|
41
|
+
} catch (error: any) {
|
|
42
|
+
this.error(`Failed to generate token: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class AuthTokensListCommand extends BaseCommand {
|
|
6
|
+
static description = 'List your API tokens';
|
|
7
|
+
|
|
8
|
+
async run() {
|
|
9
|
+
const client = getClient();
|
|
10
|
+
try {
|
|
11
|
+
const response = await client.auth.listTokens();
|
|
12
|
+
const tokens = response.tokens;
|
|
13
|
+
|
|
14
|
+
if (tokens.length === 0) {
|
|
15
|
+
this.info('No API tokens found.');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.info('Your API Tokens:');
|
|
20
|
+
for (const token of tokens) {
|
|
21
|
+
this.info(`- ${token.name} (${token.tokenPrefix}...) [Expires: ${token.expiresAt || 'Never'}]`);
|
|
22
|
+
}
|
|
23
|
+
} catch (error: any) {
|
|
24
|
+
this.error(`Failed to list tokens: ${error.message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class AuthTokensRevokeCommand extends BaseCommand {
|
|
6
|
+
static description = 'Revoke an API token';
|
|
7
|
+
|
|
8
|
+
static args = {
|
|
9
|
+
args: [
|
|
10
|
+
{
|
|
11
|
+
name: 'id',
|
|
12
|
+
required: true,
|
|
13
|
+
description: 'ID of the token to revoke',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async run(options: any) {
|
|
19
|
+
const client = getClient();
|
|
20
|
+
const { id } = options;
|
|
21
|
+
const tid = parseInt(id, 10);
|
|
22
|
+
|
|
23
|
+
if (isNaN(tid)) {
|
|
24
|
+
this.error('Token ID must be a number.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
await client.auth.revokeToken(tid);
|
|
30
|
+
this.success(`Token ${tid} revoked.`);
|
|
31
|
+
} catch (error: any) {
|
|
32
|
+
this.error(`Failed to revoke token: ${error.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import { BaseCommand } from '@nexical/cli-core';
|
|
3
|
+
import { getClient } from '../utils/nexical-client.js';
|
|
4
|
+
|
|
5
|
+
export default class WhoamiCommand extends BaseCommand {
|
|
6
|
+
static description = 'Show current logged in user';
|
|
7
|
+
|
|
8
|
+
async run() {
|
|
9
|
+
const client = getClient();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const user = await client.users.me();
|
|
13
|
+
this.info(`Logged in as:`);
|
|
14
|
+
this.info(` Name: ${user.fullName}`);
|
|
15
|
+
this.info(` Email: ${user.email}`);
|
|
16
|
+
this.info(` ID: ${user.id}`);
|
|
17
|
+
} catch (error: any) {
|
|
18
|
+
this.error('Not logged in or token expired. Run `astrical login`.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import { NexicalClient } from '@nexical/sdk';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
|
|
7
|
+
const CONFIG_DIR = path.join(os.homedir(), '.nexical');
|
|
8
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
interface Config {
|
|
11
|
+
token?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getConfig(): Config {
|
|
15
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
20
|
+
return JSON.parse(content);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function saveToken(token: string) {
|
|
27
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
28
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
const config = getConfig();
|
|
31
|
+
config.token = token;
|
|
32
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getClient(): NexicalClient {
|
|
36
|
+
const config = getConfig();
|
|
37
|
+
return new NexicalClient({
|
|
38
|
+
token: config.token,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import AdminUsersCreateSystemCommand from '../../../../src/commands/admin/create-user.js';
|
|
4
|
+
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
+
|
|
6
|
+
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
+
|
|
8
|
+
describe('AdminUsersCreateSystemCommand Integration', () => {
|
|
9
|
+
let command: AdminUsersCreateSystemCommand;
|
|
10
|
+
let mockClient: any;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.resetAllMocks();
|
|
14
|
+
|
|
15
|
+
mockClient = {
|
|
16
|
+
auth: {
|
|
17
|
+
createSystemUser: vi.fn(),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
+
|
|
22
|
+
command = new AdminUsersCreateSystemCommand([], {} as any);
|
|
23
|
+
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
+
vi.spyOn(command, 'success').mockImplementation(() => { });
|
|
25
|
+
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should create system user successfully', async () => {
|
|
29
|
+
mockClient.auth.createSystemUser.mockResolvedValue({
|
|
30
|
+
user: { fullName: 'Integration User', id: 'sys-int' }
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await command.run({ name: 'Integration User', email: 'sys-int@example.com', password: 'password123' });
|
|
34
|
+
|
|
35
|
+
expect(mockClient.auth.createSystemUser).toHaveBeenCalledWith({
|
|
36
|
+
fullName: 'Integration User',
|
|
37
|
+
email: 'sys-int@example.com',
|
|
38
|
+
password: 'password123'
|
|
39
|
+
});
|
|
40
|
+
expect(command.success).toHaveBeenCalledWith('System user "Integration User" created!');
|
|
41
|
+
expect(command.info).toHaveBeenCalledWith('ID: sys-int');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle creation failure', async () => {
|
|
45
|
+
mockClient.auth.createSystemUser.mockRejectedValue(new Error('Email already exists'));
|
|
46
|
+
|
|
47
|
+
await command.run({ name: 'Integration User', email: 'sys-int@example.com', password: 'password123' });
|
|
48
|
+
|
|
49
|
+
expect(command.error).toHaveBeenCalledWith('Failed to create system user: Email already exists');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import BranchesCreateCommand from '../../../../src/commands/branch/create.js';
|
|
4
|
+
import { getClient } from '../../../../src/utils/nexical-client.js';
|
|
5
|
+
|
|
6
|
+
vi.mock('../../../../src/utils/nexical-client.js');
|
|
7
|
+
|
|
8
|
+
describe('BranchesCreateCommand Integration', () => {
|
|
9
|
+
let command: BranchesCreateCommand;
|
|
10
|
+
let mockClient: any;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.resetAllMocks();
|
|
14
|
+
|
|
15
|
+
mockClient = {
|
|
16
|
+
branches: {
|
|
17
|
+
create: vi.fn(),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
vi.mocked(getClient).mockReturnValue(mockClient);
|
|
21
|
+
|
|
22
|
+
command = new BranchesCreateCommand([], {} as any);
|
|
23
|
+
vi.spyOn(command, 'info').mockImplementation(() => { });
|
|
24
|
+
vi.spyOn(command, 'success').mockImplementation(() => { });
|
|
25
|
+
vi.spyOn(command, 'error').mockImplementation(() => { });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should create branch successfully', async () => {
|
|
29
|
+
mockClient.branches.create.mockResolvedValue({
|
|
30
|
+
name: 'integration-branch',
|
|
31
|
+
id: 'br-int'
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await command.run({ teamId: '1', projectId: '2', name: 'integration-branch', preview: 'http://int.com' });
|
|
35
|
+
|
|
36
|
+
expect(mockClient.branches.create).toHaveBeenCalledWith(1, 2, {
|
|
37
|
+
name: 'integration-branch',
|
|
38
|
+
previewUrl: 'http://int.com',
|
|
39
|
+
});
|
|
40
|
+
expect(command.success).toHaveBeenCalledWith('Branch "integration-branch" created!');
|
|
41
|
+
expect(command.info).toHaveBeenCalledWith('ID: br-int');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle creation failure', async () => {
|
|
45
|
+
mockClient.branches.create.mockRejectedValue(new Error('Branch exists'));
|
|
46
|
+
|
|
47
|
+
await command.run({ teamId: '1', projectId: '2', name: 'main' });
|
|
48
|
+
|
|
49
|
+
expect(command.error).toHaveBeenCalledWith('Failed to create branch: Branch exists');
|
|
50
|
+
});
|
|
51
|
+
});
|