nexusapp-cli 1.2.4 → 2.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/README.md +4 -6
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +152 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/member.d.ts +3 -0
- package/dist/commands/member.d.ts.map +1 -0
- package/dist/commands/member.js +175 -0
- package/dist/commands/member.js.map +1 -0
- package/dist/commands/token.d.ts +3 -0
- package/dist/commands/token.d.ts.map +1 -0
- package/dist/commands/token.js +179 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/nexusapp-cli-2.0.0.tgz +0 -0
- package/package.json +2 -8
- package/src/commands/deploy.ts +126 -1
- package/src/commands/member.ts +168 -0
- package/src/commands/token.ts +173 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { client, apiError, unwrap } from '../client.js';
|
|
5
|
+
import { printTable, printJson, success, errorMsg, timeAgo } from '../output.js';
|
|
6
|
+
|
|
7
|
+
function parseDuration(str: string): Date {
|
|
8
|
+
const match = str.match(/^(\d+)(d|h|m)$/);
|
|
9
|
+
if (!match) {
|
|
10
|
+
throw new Error(`Invalid duration "${str}". Use format like: 90d, 4h, 30m`);
|
|
11
|
+
}
|
|
12
|
+
const value = parseInt(match[1], 10);
|
|
13
|
+
const unit = match[2];
|
|
14
|
+
const msMap: Record<string, number> = { d: 86400000, h: 3600000, m: 60000 };
|
|
15
|
+
return new Date(Date.now() + value * msMap[unit]);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function formatScopes(scopes: string): string {
|
|
19
|
+
return scopes
|
|
20
|
+
.split(',')
|
|
21
|
+
.map((s) => s.trim())
|
|
22
|
+
.join(', ');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function tokenStatus(t: any): string {
|
|
26
|
+
if (t.revokedAt) return chalk.red('revoked');
|
|
27
|
+
if (t.expiresAt && new Date(t.expiresAt) < new Date()) return chalk.yellow('expired');
|
|
28
|
+
return chalk.green('active');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function registerToken(program: Command): void {
|
|
32
|
+
const token = program.command('token').description('Access token management commands');
|
|
33
|
+
|
|
34
|
+
// list
|
|
35
|
+
token
|
|
36
|
+
.command('list')
|
|
37
|
+
.description('List access tokens')
|
|
38
|
+
.option('--json', 'Output raw JSON')
|
|
39
|
+
.option('--show-last-used', 'Show last used column')
|
|
40
|
+
.option('--no-expiry', 'Show only tokens with no expiry date')
|
|
41
|
+
.option('--unused-since <days>', 'Show tokens unused for N or more days')
|
|
42
|
+
.action(async (opts) => {
|
|
43
|
+
try {
|
|
44
|
+
const res = await client.get('/api/tokens');
|
|
45
|
+
let tokens: any[] = unwrap(res.data);
|
|
46
|
+
if (!Array.isArray(tokens)) tokens = [];
|
|
47
|
+
|
|
48
|
+
if (opts.noExpiry) {
|
|
49
|
+
tokens = tokens.filter((t: any) => !t.expiresAt);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (opts.unusedSince) {
|
|
53
|
+
const days = parseInt(opts.unusedSince, 10);
|
|
54
|
+
if (isNaN(days) || days < 0) {
|
|
55
|
+
errorMsg('--unused-since requires a positive integer (number of days)');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const cutoff = new Date(Date.now() - days * 86400000);
|
|
59
|
+
tokens = tokens.filter((t: any) => {
|
|
60
|
+
if (!t.lastUsedAt) return true;
|
|
61
|
+
return new Date(t.lastUsedAt) < cutoff;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (opts.json) { printJson(tokens); return; }
|
|
66
|
+
if (!tokens.length) { console.log('No tokens found.'); return; }
|
|
67
|
+
|
|
68
|
+
const headers = ['ID', 'NAME', 'SCOPES', 'STATUS', 'EXPIRES', 'CREATED'];
|
|
69
|
+
if (opts.showLastUsed) headers.push('LAST USED');
|
|
70
|
+
|
|
71
|
+
printTable(
|
|
72
|
+
headers,
|
|
73
|
+
tokens.map((t: any) => {
|
|
74
|
+
const row: (string | null)[] = [
|
|
75
|
+
t.id,
|
|
76
|
+
t.name,
|
|
77
|
+
formatScopes(t.scopes || ''),
|
|
78
|
+
tokenStatus(t),
|
|
79
|
+
t.expiresAt ? timeAgo(t.expiresAt) : '—',
|
|
80
|
+
t.createdAt ? timeAgo(t.createdAt) : '—',
|
|
81
|
+
];
|
|
82
|
+
if (opts.showLastUsed) {
|
|
83
|
+
row.push(t.lastUsedAt ? timeAgo(t.lastUsedAt) : 'never');
|
|
84
|
+
}
|
|
85
|
+
return row;
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
errorMsg(apiError(err));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// create
|
|
95
|
+
token
|
|
96
|
+
.command('create')
|
|
97
|
+
.description('Create a new access token')
|
|
98
|
+
.requiredOption('--name <name>', 'Token name')
|
|
99
|
+
.requiredOption('--scopes <scopes>', 'Comma-separated scopes (e.g. deploy:write,secrets:read)')
|
|
100
|
+
.option('--expires <duration>', 'Expiry duration (e.g. 90d, 4h, 30m)')
|
|
101
|
+
.option('--json', 'Output raw JSON')
|
|
102
|
+
.action(async (opts) => {
|
|
103
|
+
let expiresAt: string | undefined;
|
|
104
|
+
|
|
105
|
+
if (opts.expires) {
|
|
106
|
+
try {
|
|
107
|
+
expiresAt = parseDuration(opts.expires).toISOString();
|
|
108
|
+
} catch (e: any) {
|
|
109
|
+
errorMsg(e.message);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const res = await client.post('/api/tokens', {
|
|
116
|
+
name: opts.name,
|
|
117
|
+
scopes: opts.scopes,
|
|
118
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const data = unwrap(res.data);
|
|
122
|
+
|
|
123
|
+
if (opts.json) { printJson(data); return; }
|
|
124
|
+
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(chalk.bold('Token created'));
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(` ${chalk.dim('ID:')} ${data.id}`);
|
|
129
|
+
console.log(` ${chalk.dim('Name:')} ${data.name}`);
|
|
130
|
+
console.log(` ${chalk.dim('Scopes:')} ${formatScopes(data.scopes)}`);
|
|
131
|
+
if (data.expiresAt) {
|
|
132
|
+
console.log(` ${chalk.dim('Expires:')} ${new Date(data.expiresAt).toLocaleDateString()}`);
|
|
133
|
+
}
|
|
134
|
+
console.log('');
|
|
135
|
+
console.log(` ${chalk.dim('Token value (shown once — copy it now):')}`)
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log(` ${chalk.cyan(data.token)}`);
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log(chalk.yellow(' This value will not be shown again.'));
|
|
140
|
+
console.log('');
|
|
141
|
+
} catch (err) {
|
|
142
|
+
errorMsg(apiError(err));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// revoke
|
|
148
|
+
token
|
|
149
|
+
.command('revoke <id>')
|
|
150
|
+
.description('Revoke an access token')
|
|
151
|
+
.option('--yes', 'Skip confirmation prompt')
|
|
152
|
+
.action(async (id, opts) => {
|
|
153
|
+
if (!opts.yes) {
|
|
154
|
+
const { confirm } = await inquirer.prompt([
|
|
155
|
+
{
|
|
156
|
+
type: 'confirm',
|
|
157
|
+
name: 'confirm',
|
|
158
|
+
message: `Revoke token "${id}"? This cannot be undone.`,
|
|
159
|
+
default: false,
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
162
|
+
if (!confirm) { console.log('Cancelled.'); return; }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await client.post(`/api/tokens/${id}/revoke`, {});
|
|
167
|
+
success(`Token ${id} revoked.`);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
errorMsg(apiError(err));
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { registerDeploy } from './commands/deploy.js';
|
|
|
5
5
|
import { registerSecret } from './commands/secret.js';
|
|
6
6
|
import { registerProject } from './commands/project.js';
|
|
7
7
|
import { registerDomain } from './commands/domain.js';
|
|
8
|
+
import { registerToken } from './commands/token.js';
|
|
9
|
+
import { registerMember } from './commands/member.js';
|
|
8
10
|
|
|
9
11
|
const program = new Command();
|
|
10
12
|
|
|
@@ -18,6 +20,8 @@ registerDeploy(program);
|
|
|
18
20
|
registerSecret(program);
|
|
19
21
|
registerProject(program);
|
|
20
22
|
registerDomain(program);
|
|
23
|
+
registerToken(program);
|
|
24
|
+
registerMember(program);
|
|
21
25
|
|
|
22
26
|
program.parseAsync(process.argv).catch((err) => {
|
|
23
27
|
console.error(err.message || String(err));
|