@ktmcp-cli/authentiq-api 1.0.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 ADDED
@@ -0,0 +1,82 @@
1
+ # Authentiq API CLI
2
+
3
+ Production-ready command-line interface for the Authentiq API API.
4
+
5
+ > **⚠️ Unofficial CLI** - This tool is an independent project built on the public API.
6
+
7
+ ## Features
8
+
9
+ - Complete coverage of Authentiq API API endpoints
10
+ - Simple, intuitive command structure
11
+ - JSON and pretty-print output formats
12
+ - Persistent configuration storage
13
+ - Progress indicators for async operations
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install -g @ktmcp-cli/authentiq-api
19
+ ```
20
+
21
+ Or install locally:
22
+
23
+ ```bash
24
+ cd authentiq-api
25
+ npm install
26
+ npm link
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ ### Set API Key
32
+
33
+ ```bash
34
+ authentiq-api config set apiKey YOUR_KEY_HERE
35
+ ```
36
+
37
+ ### Environment Variables
38
+
39
+ ```bash
40
+ export API_KEY=your_key_here
41
+ export API_BASE_URL=https://6-dot-authentiqio.appspot.com
42
+ ```
43
+
44
+ ### View Configuration
45
+
46
+ ```bash
47
+ authentiq-api config list
48
+ authentiq-api config get apiKey
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### key
54
+
55
+ ```bash
56
+ # List items
57
+ authentiq-api key list
58
+
59
+ # Get item by ID
60
+ authentiq-api key get <id>
61
+
62
+ # Output as JSON
63
+ authentiq-api key list --json
64
+ ```
65
+
66
+ ## Available Resources
67
+
68
+ - `key`
69
+ - `login`
70
+ - `scope`
71
+
72
+ ## JSON Output
73
+
74
+ All commands support `--json` flag for machine-readable output:
75
+
76
+ ```bash
77
+ authentiq-api key list --json
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { readFileSync } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+ import chalk from 'chalk';
8
+
9
+ import config from '../src/commands/config.js';
10
+ import key from '../src/commands/key.js';
11
+ import login from '../src/commands/login.js';
12
+ import scope from '../src/commands/scope.js';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ const packageJson = JSON.parse(
18
+ readFileSync(join(__dirname, '../package.json'), 'utf-8')
19
+ );
20
+
21
+ const program = new Command();
22
+
23
+ program
24
+ .name('authentiq-api')
25
+ .description(chalk.cyan('Strong authentication, without the passwords.'))
26
+ .version(packageJson.version, '-v, --version', 'Output the current version')
27
+ .addHelpText('after', `
28
+ ${chalk.bold('Examples:')}
29
+ $ authentiq-api config set apiKey YOUR_KEY
30
+ $ authentiq-api key list
31
+ $ authentiq-api key list --json
32
+
33
+ ${chalk.bold('Environment Variables:')}
34
+ API_KEY API authentication key
35
+ API_BASE_URL API base URL
36
+ API_TIMEOUT Request timeout in ms (default: 30000)
37
+
38
+ ${chalk.bold('Documentation:')}
39
+ GitHub: ${chalk.blue('https://github.com/ktmcp-cli/authentiq-api')}
40
+ `);
41
+
42
+ program.addCommand(config);
43
+ program.addCommand(key);
44
+ program.addCommand(login);
45
+ program.addCommand(scope);
46
+
47
+ process.on('unhandledRejection', (error) => {
48
+ console.error(chalk.red('Unhandled error:'), error);
49
+ process.exit(1);
50
+ });
51
+
52
+ program.parse(process.argv);
53
+
54
+ if (!process.argv.slice(2).length) {
55
+ program.outputHelp();
56
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@ktmcp-cli/authentiq-api",
3
+ "version": "1.0.0",
4
+ "description": "Strong authentication, without the passwords.",
5
+ "type": "module",
6
+ "main": "bin/authentiq-api.js",
7
+ "bin": {
8
+ "authentiq-api": "./bin/authentiq-api.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node bin/authentiq-api.js",
12
+ "dev": "node bin/authentiq-api.js"
13
+ },
14
+ "keywords": [
15
+ "cli",
16
+ "api",
17
+ "authentiq-api",
18
+ "ktmcp"
19
+ ],
20
+ "author": "KTMCP",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "commander": "^12.0.0",
24
+ "axios": "^1.6.0",
25
+ "chalk": "^5.3.0",
26
+ "conf": "^12.0.0",
27
+ "dotenv": "^16.4.0",
28
+ "ora": "^8.0.1"
29
+ },
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ }
33
+ }
@@ -0,0 +1,42 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { loadConfig, setConfig, getConfig, saveConfig } from '../lib/config.js';
4
+
5
+ const config = new Command('config');
6
+
7
+ config.description('Manage CLI configuration');
8
+
9
+ config
10
+ .command('set <key> <value>')
11
+ .description('Set a configuration value')
12
+ .action((key, value) => {
13
+ if (setConfig(key, value)) {
14
+ console.log(chalk.green(`✓ Set ${key} = ${value}`));
15
+ } else {
16
+ console.error(chalk.red('Failed to save configuration'));
17
+ process.exit(1);
18
+ }
19
+ });
20
+
21
+ config
22
+ .command('get <key>')
23
+ .description('Get a configuration value')
24
+ .action((key) => {
25
+ const value = getConfig(key);
26
+ if (value !== undefined) {
27
+ console.log(chalk.cyan(`${key} = ${value}`));
28
+ } else {
29
+ console.log(chalk.yellow(`${key} is not set`));
30
+ }
31
+ });
32
+
33
+ config
34
+ .command('list')
35
+ .description('List all configuration values')
36
+ .action(() => {
37
+ const all = loadConfig();
38
+ console.log(chalk.cyan('Current configuration:'));
39
+ console.log(JSON.stringify(all, null, 2));
40
+ });
41
+
42
+ export default config;
@@ -0,0 +1,332 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { createClient, handleError } from '../lib/api.js';
5
+
6
+ const key = new Command('key');
7
+
8
+ key
9
+ .description('Manage key');
10
+
11
+ key
12
+ .command('delete')
13
+ .description('Revoke an Authentiq ID using email & phone.
14
+
15
+ If called with `email` and `phone` only, a verification code
16
+ will be sent by email. Do a second call adding `code` to
17
+ complete the revocation.
18
+ ')
19
+ .requiredOption('--email <value>', 'primary email associated to Key (ID)')
20
+ .requiredOption('--phone <value>', 'primary phone number, international representation')
21
+ .option('--code <value>', 'verification code sent by email')
22
+ .option('--json', 'Output as JSON')
23
+ .option('-v, --verbose', 'Verbose output')
24
+ .action(async (options) => {
25
+ const spinner = ora('Revoke an Authentiq ID using email & phone.
26
+
27
+ If called with `email` and `phone` only, a verification code
28
+ will be sent by email. Do a second call adding `code` to
29
+ complete the revocation.
30
+ ...').start();
31
+ try {
32
+ const client = createClient();
33
+ const queryParams = {};
34
+ if (options.email) queryParams.email = options.email;
35
+ if (options.phone) queryParams.phone = options.phone;
36
+ if (options.code) queryParams.code = options.code;
37
+
38
+ const response = await client.delete(`/key`);
39
+ const result = { success: true, status: response.status, data: response.data };
40
+
41
+ spinner.succeed('Success');
42
+
43
+ if (result.success) {
44
+ if (options.json) {
45
+ console.log(JSON.stringify(result.data, null, 2));
46
+ } else {
47
+ console.log(chalk.green('✓ Deleted successfully'));
48
+ }
49
+ process.exit(0);
50
+ } else {
51
+ spinner.fail(`Failed (${result.status})`);
52
+ console.error(chalk.red(result.message));
53
+ if (options.verbose && result.data) {
54
+ console.error(chalk.yellow('\nResponse data:'));
55
+ console.error(JSON.stringify(result.data, null, 2));
56
+ }
57
+ process.exit(1);
58
+ }
59
+ } catch (error) {
60
+ spinner.fail('Error occurred');
61
+ console.error(chalk.red('Error:'), error.message);
62
+ if (options.verbose) {
63
+ console.error(error.stack);
64
+ }
65
+ process.exit(1);
66
+ }
67
+ });
68
+
69
+ key
70
+ .command('create')
71
+ .description('Register a new ID `JWT(sub, devtoken)`
72
+
73
+ v5: `JWT(sub, pk, devtoken, ...)`
74
+
75
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
76
+ ')
77
+ .option('--json', 'Output as JSON')
78
+ .option('-v, --verbose', 'Verbose output')
79
+ .action(async (options) => {
80
+ const spinner = ora('Register a new ID `JWT(sub, devtoken)`
81
+
82
+ v5: `JWT(sub, pk, devtoken, ...)`
83
+
84
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
85
+ ...').start();
86
+ try {
87
+ const client = createClient();
88
+ const response = await client.post(`/key`);
89
+ const result = { success: true, status: response.status, data: response.data };
90
+
91
+ spinner.succeed('Success');
92
+
93
+ if (result.success) {
94
+ if (options.json) {
95
+ console.log(JSON.stringify(result.data, null, 2));
96
+ } else {
97
+ // Create output
98
+ const item = result.data;
99
+ console.log(chalk.green('✓ Created successfully!'));
100
+ if (item.id) console.log('ID:', chalk.cyan(item.id));
101
+ }
102
+ process.exit(0);
103
+ } else {
104
+ spinner.fail(`Failed (${result.status})`);
105
+ console.error(chalk.red(result.message));
106
+ if (options.verbose && result.data) {
107
+ console.error(chalk.yellow('\nResponse data:'));
108
+ console.error(JSON.stringify(result.data, null, 2));
109
+ }
110
+ process.exit(1);
111
+ }
112
+ } catch (error) {
113
+ spinner.fail('Error occurred');
114
+ console.error(chalk.red('Error:'), error.message);
115
+ if (options.verbose) {
116
+ console.error(error.stack);
117
+ }
118
+ process.exit(1);
119
+ }
120
+ });
121
+
122
+ key
123
+ .command('delete <pk>')
124
+ .description('Revoke an Identity (Key) with a revocation secret')
125
+ .requiredOption('--secret <value>', 'revokation secret')
126
+ .option('--json', 'Output as JSON')
127
+ .option('-v, --verbose', 'Verbose output')
128
+ .action(async (pK, options) => {
129
+ const spinner = ora('Revoke an Identity (Key) with a revocation secret...').start();
130
+ try {
131
+ const client = createClient();
132
+ const queryParams = {};
133
+ if (options.secret) queryParams.secret = options.secret;
134
+
135
+ const response = await client.delete(`/key/${pK}`);
136
+ const result = { success: true, status: response.status, data: response.data };
137
+
138
+ spinner.succeed('Success');
139
+
140
+ if (result.success) {
141
+ if (options.json) {
142
+ console.log(JSON.stringify(result.data, null, 2));
143
+ } else {
144
+ console.log(chalk.green('✓ Deleted successfully'));
145
+ }
146
+ process.exit(0);
147
+ } else {
148
+ spinner.fail(`Failed (${result.status})`);
149
+ console.error(chalk.red(result.message));
150
+ if (options.verbose && result.data) {
151
+ console.error(chalk.yellow('\nResponse data:'));
152
+ console.error(JSON.stringify(result.data, null, 2));
153
+ }
154
+ process.exit(1);
155
+ }
156
+ } catch (error) {
157
+ spinner.fail('Error occurred');
158
+ console.error(chalk.red('Error:'), error.message);
159
+ if (options.verbose) {
160
+ console.error(error.stack);
161
+ }
162
+ process.exit(1);
163
+ }
164
+ });
165
+
166
+ key
167
+ .command('get <pk>')
168
+ .description('Get public details of an Authentiq ID.
169
+ ')
170
+ .option('--json', 'Output as JSON')
171
+ .option('-v, --verbose', 'Verbose output')
172
+ .action(async (pK, options) => {
173
+ const spinner = ora('Get public details of an Authentiq ID.
174
+ ...').start();
175
+ try {
176
+ const client = createClient();
177
+ const response = await client.get(`/key/${pK}`);
178
+ const result = { success: true, status: response.status, data: response.data };
179
+
180
+ spinner.succeed('Success');
181
+
182
+ if (result.success) {
183
+ if (options.json) {
184
+ console.log(JSON.stringify(result.data, null, 2));
185
+ } else {
186
+ // Detail output
187
+ const item = result.data;
188
+ console.log(chalk.bold('Details:'));
189
+ console.log(chalk.gray('─'.repeat(60)));
190
+ Object.entries(item).slice(0, 10).forEach(([key, value]) => {
191
+ console.log(chalk.cyan(key + ':'), String(value).slice(0, 100));
192
+ });
193
+ }
194
+ process.exit(0);
195
+ } else {
196
+ spinner.fail(`Failed (${result.status})`);
197
+ console.error(chalk.red(result.message));
198
+ if (options.verbose && result.data) {
199
+ console.error(chalk.yellow('\nResponse data:'));
200
+ console.error(JSON.stringify(result.data, null, 2));
201
+ }
202
+ process.exit(1);
203
+ }
204
+ } catch (error) {
205
+ spinner.fail('Error occurred');
206
+ console.error(chalk.red('Error:'), error.message);
207
+ if (options.verbose) {
208
+ console.error(error.stack);
209
+ }
210
+ process.exit(1);
211
+ }
212
+ });
213
+
214
+ key
215
+ .command('update <pk>')
216
+ .description('update properties of an Authentiq ID.
217
+ (not operational in v4; use PUT for now)
218
+
219
+ v5: POST issuer-signed email & phone scopes in
220
+ a self-signed JWT
221
+
222
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
223
+ ')
224
+ .option('--json', 'Output as JSON')
225
+ .option('-v, --verbose', 'Verbose output')
226
+ .action(async (pK, options) => {
227
+ const spinner = ora('update properties of an Authentiq ID.
228
+ (not operational in v4; use PUT for now)
229
+
230
+ v5: POST issuer-signed email & phone scopes in
231
+ a self-signed JWT
232
+
233
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
234
+ ...').start();
235
+ try {
236
+ const client = createClient();
237
+ const response = await client.post(`/key/${pK}`);
238
+ const result = { success: true, status: response.status, data: response.data };
239
+
240
+ spinner.succeed('Success');
241
+
242
+ if (result.success) {
243
+ if (options.json) {
244
+ console.log(JSON.stringify(result.data, null, 2));
245
+ } else {
246
+ // Update/other output
247
+ console.log(chalk.green('✓ Operation completed successfully'));
248
+ if (options.verbose) {
249
+ console.log(JSON.stringify(result.data, null, 2));
250
+ }
251
+ }
252
+ process.exit(0);
253
+ } else {
254
+ spinner.fail(`Failed (${result.status})`);
255
+ console.error(chalk.red(result.message));
256
+ if (options.verbose && result.data) {
257
+ console.error(chalk.yellow('\nResponse data:'));
258
+ console.error(JSON.stringify(result.data, null, 2));
259
+ }
260
+ process.exit(1);
261
+ }
262
+ } catch (error) {
263
+ spinner.fail('Error occurred');
264
+ console.error(chalk.red('Error:'), error.message);
265
+ if (options.verbose) {
266
+ console.error(error.stack);
267
+ }
268
+ process.exit(1);
269
+ }
270
+ });
271
+
272
+ key
273
+ .command('update <pk>')
274
+ .description('Update Authentiq ID by replacing the object.
275
+
276
+ v4: `JWT(sub,email,phone)` to bind email/phone hash;
277
+
278
+ v5: POST issuer-signed email & phone scopes
279
+ and PUT to update registration `JWT(sub, pk, devtoken, ...)`
280
+
281
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
282
+ ')
283
+ .option('--json', 'Output as JSON')
284
+ .option('-v, --verbose', 'Verbose output')
285
+ .action(async (pK, options) => {
286
+ const spinner = ora('Update Authentiq ID by replacing the object.
287
+
288
+ v4: `JWT(sub,email,phone)` to bind email/phone hash;
289
+
290
+ v5: POST issuer-signed email & phone scopes
291
+ and PUT to update registration `JWT(sub, pk, devtoken, ...)`
292
+
293
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
294
+ ...').start();
295
+ try {
296
+ const client = createClient();
297
+ const response = await client.put(`/key/${pK}`);
298
+ const result = { success: true, status: response.status, data: response.data };
299
+
300
+ spinner.succeed('Success');
301
+
302
+ if (result.success) {
303
+ if (options.json) {
304
+ console.log(JSON.stringify(result.data, null, 2));
305
+ } else {
306
+ // Update/other output
307
+ console.log(chalk.green('✓ Operation completed successfully'));
308
+ if (options.verbose) {
309
+ console.log(JSON.stringify(result.data, null, 2));
310
+ }
311
+ }
312
+ process.exit(0);
313
+ } else {
314
+ spinner.fail(`Failed (${result.status})`);
315
+ console.error(chalk.red(result.message));
316
+ if (options.verbose && result.data) {
317
+ console.error(chalk.yellow('\nResponse data:'));
318
+ console.error(JSON.stringify(result.data, null, 2));
319
+ }
320
+ process.exit(1);
321
+ }
322
+ } catch (error) {
323
+ spinner.fail('Error occurred');
324
+ console.error(chalk.red('Error:'), error.message);
325
+ if (options.verbose) {
326
+ console.error(error.stack);
327
+ }
328
+ process.exit(1);
329
+ }
330
+ });
331
+
332
+ export default key;
@@ -0,0 +1,62 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { createClient, handleError } from '../lib/api.js';
5
+
6
+ const login = new Command('login');
7
+
8
+ login
9
+ .description('Manage login');
10
+
11
+ login
12
+ .command('create')
13
+ .description('push sign-in request
14
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
15
+ ')
16
+ .requiredOption('--callback <value>', 'URI App will connect to')
17
+ .option('--json', 'Output as JSON')
18
+ .option('-v, --verbose', 'Verbose output')
19
+ .action(async (options) => {
20
+ const spinner = ora('push sign-in request
21
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
22
+ ...').start();
23
+ try {
24
+ const client = createClient();
25
+ const queryParams = {};
26
+ if (options.callback) queryParams.callback = options.callback;
27
+
28
+ const response = await client.post(`/login`);
29
+ const result = { success: true, status: response.status, data: response.data };
30
+
31
+ spinner.succeed('Success');
32
+
33
+ if (result.success) {
34
+ if (options.json) {
35
+ console.log(JSON.stringify(result.data, null, 2));
36
+ } else {
37
+ // Create output
38
+ const item = result.data;
39
+ console.log(chalk.green('✓ Created successfully!'));
40
+ if (item.id) console.log('ID:', chalk.cyan(item.id));
41
+ }
42
+ process.exit(0);
43
+ } else {
44
+ spinner.fail(`Failed (${result.status})`);
45
+ console.error(chalk.red(result.message));
46
+ if (options.verbose && result.data) {
47
+ console.error(chalk.yellow('\nResponse data:'));
48
+ console.error(JSON.stringify(result.data, null, 2));
49
+ }
50
+ process.exit(1);
51
+ }
52
+ } catch (error) {
53
+ spinner.fail('Error occurred');
54
+ console.error(chalk.red('Error:'), error.message);
55
+ if (options.verbose) {
56
+ console.error(error.stack);
57
+ }
58
+ process.exit(1);
59
+ }
60
+ });
61
+
62
+ export default login;
@@ -0,0 +1,239 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { createClient, handleError } from '../lib/api.js';
5
+
6
+ const scope = new Command('scope');
7
+
8
+ scope
9
+ .description('Manage scope');
10
+
11
+ scope
12
+ .command('create')
13
+ .description('scope verification request
14
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
15
+ ')
16
+ .option('--test <value>', 'test only mode, using test issuer')
17
+ .option('--json', 'Output as JSON')
18
+ .option('-v, --verbose', 'Verbose output')
19
+ .action(async (options) => {
20
+ const spinner = ora('scope verification request
21
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
22
+ ...').start();
23
+ try {
24
+ const client = createClient();
25
+ const queryParams = {};
26
+ if (options.test) queryParams.test = options.test;
27
+
28
+ const response = await client.post(`/scope`);
29
+ const result = { success: true, status: response.status, data: response.data };
30
+
31
+ spinner.succeed('Success');
32
+
33
+ if (result.success) {
34
+ if (options.json) {
35
+ console.log(JSON.stringify(result.data, null, 2));
36
+ } else {
37
+ // Create output
38
+ const item = result.data;
39
+ console.log(chalk.green('✓ Created successfully!'));
40
+ if (item.id) console.log('ID:', chalk.cyan(item.id));
41
+ }
42
+ process.exit(0);
43
+ } else {
44
+ spinner.fail(`Failed (${result.status})`);
45
+ console.error(chalk.red(result.message));
46
+ if (options.verbose && result.data) {
47
+ console.error(chalk.yellow('\nResponse data:'));
48
+ console.error(JSON.stringify(result.data, null, 2));
49
+ }
50
+ process.exit(1);
51
+ }
52
+ } catch (error) {
53
+ spinner.fail('Error occurred');
54
+ console.error(chalk.red('Error:'), error.message);
55
+ if (options.verbose) {
56
+ console.error(error.stack);
57
+ }
58
+ process.exit(1);
59
+ }
60
+ });
61
+
62
+ scope
63
+ .command('delete <job>')
64
+ .description('delete a verification job')
65
+ .option('--json', 'Output as JSON')
66
+ .option('-v, --verbose', 'Verbose output')
67
+ .action(async (job, options) => {
68
+ const spinner = ora('delete a verification job...').start();
69
+ try {
70
+ const client = createClient();
71
+ const response = await client.delete(`/scope/${job}`);
72
+ const result = { success: true, status: response.status, data: response.data };
73
+
74
+ spinner.succeed('Success');
75
+
76
+ if (result.success) {
77
+ if (options.json) {
78
+ console.log(JSON.stringify(result.data, null, 2));
79
+ } else {
80
+ console.log(chalk.green('✓ Deleted successfully'));
81
+ }
82
+ process.exit(0);
83
+ } else {
84
+ spinner.fail(`Failed (${result.status})`);
85
+ console.error(chalk.red(result.message));
86
+ if (options.verbose && result.data) {
87
+ console.error(chalk.yellow('\nResponse data:'));
88
+ console.error(JSON.stringify(result.data, null, 2));
89
+ }
90
+ process.exit(1);
91
+ }
92
+ } catch (error) {
93
+ spinner.fail('Error occurred');
94
+ console.error(chalk.red('Error:'), error.message);
95
+ if (options.verbose) {
96
+ console.error(error.stack);
97
+ }
98
+ process.exit(1);
99
+ }
100
+ });
101
+
102
+ scope
103
+ .command('get <job>')
104
+ .description('get the status / current content of a verification job')
105
+ .option('--json', 'Output as JSON')
106
+ .option('-v, --verbose', 'Verbose output')
107
+ .action(async (job, options) => {
108
+ const spinner = ora('get the status / current content of a verification job...').start();
109
+ try {
110
+ const client = createClient();
111
+ const response = await client.get(`/scope/${job}`);
112
+ const result = { success: true, status: response.status, data: response.data };
113
+
114
+ spinner.succeed('Success');
115
+
116
+ if (result.success) {
117
+ if (options.json) {
118
+ console.log(JSON.stringify(result.data, null, 2));
119
+ } else {
120
+ // Detail output
121
+ const item = result.data;
122
+ console.log(chalk.bold('Details:'));
123
+ console.log(chalk.gray('─'.repeat(60)));
124
+ Object.entries(item).slice(0, 10).forEach(([key, value]) => {
125
+ console.log(chalk.cyan(key + ':'), String(value).slice(0, 100));
126
+ });
127
+ }
128
+ process.exit(0);
129
+ } else {
130
+ spinner.fail(`Failed (${result.status})`);
131
+ console.error(chalk.red(result.message));
132
+ if (options.verbose && result.data) {
133
+ console.error(chalk.yellow('\nResponse data:'));
134
+ console.error(JSON.stringify(result.data, null, 2));
135
+ }
136
+ process.exit(1);
137
+ }
138
+ } catch (error) {
139
+ spinner.fail('Error occurred');
140
+ console.error(chalk.red('Error:'), error.message);
141
+ if (options.verbose) {
142
+ console.error(error.stack);
143
+ }
144
+ process.exit(1);
145
+ }
146
+ });
147
+
148
+ scope
149
+ .command('create <job>')
150
+ .description('this is a scope confirmation')
151
+ .option('--json', 'Output as JSON')
152
+ .option('-v, --verbose', 'Verbose output')
153
+ .action(async (job, options) => {
154
+ const spinner = ora('this is a scope confirmation...').start();
155
+ try {
156
+ const client = createClient();
157
+ const response = await client.post(`/scope/${job}`);
158
+ const result = { success: true, status: response.status, data: response.data };
159
+
160
+ spinner.succeed('Success');
161
+
162
+ if (result.success) {
163
+ if (options.json) {
164
+ console.log(JSON.stringify(result.data, null, 2));
165
+ } else {
166
+ // Create output
167
+ const item = result.data;
168
+ console.log(chalk.green('✓ Created successfully!'));
169
+ if (item.id) console.log('ID:', chalk.cyan(item.id));
170
+ }
171
+ process.exit(0);
172
+ } else {
173
+ spinner.fail(`Failed (${result.status})`);
174
+ console.error(chalk.red(result.message));
175
+ if (options.verbose && result.data) {
176
+ console.error(chalk.yellow('\nResponse data:'));
177
+ console.error(JSON.stringify(result.data, null, 2));
178
+ }
179
+ process.exit(1);
180
+ }
181
+ } catch (error) {
182
+ spinner.fail('Error occurred');
183
+ console.error(chalk.red('Error:'), error.message);
184
+ if (options.verbose) {
185
+ console.error(error.stack);
186
+ }
187
+ process.exit(1);
188
+ }
189
+ });
190
+
191
+ scope
192
+ .command('update <job>')
193
+ .description('authority updates a JWT with its signature
194
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
195
+ ')
196
+ .option('--json', 'Output as JSON')
197
+ .option('-v, --verbose', 'Verbose output')
198
+ .action(async (job, options) => {
199
+ const spinner = ora('authority updates a JWT with its signature
200
+ See: https://github.com/skion/authentiq/wiki/JWT-Examples
201
+ ...').start();
202
+ try {
203
+ const client = createClient();
204
+ const response = await client.put(`/scope/${job}`);
205
+ const result = { success: true, status: response.status, data: response.data };
206
+
207
+ spinner.succeed('Success');
208
+
209
+ if (result.success) {
210
+ if (options.json) {
211
+ console.log(JSON.stringify(result.data, null, 2));
212
+ } else {
213
+ // Update/other output
214
+ console.log(chalk.green('✓ Operation completed successfully'));
215
+ if (options.verbose) {
216
+ console.log(JSON.stringify(result.data, null, 2));
217
+ }
218
+ }
219
+ process.exit(0);
220
+ } else {
221
+ spinner.fail(`Failed (${result.status})`);
222
+ console.error(chalk.red(result.message));
223
+ if (options.verbose && result.data) {
224
+ console.error(chalk.yellow('\nResponse data:'));
225
+ console.error(JSON.stringify(result.data, null, 2));
226
+ }
227
+ process.exit(1);
228
+ }
229
+ } catch (error) {
230
+ spinner.fail('Error occurred');
231
+ console.error(chalk.red('Error:'), error.message);
232
+ if (options.verbose) {
233
+ console.error(error.stack);
234
+ }
235
+ process.exit(1);
236
+ }
237
+ });
238
+
239
+ export default scope;
package/src/lib/api.js ADDED
@@ -0,0 +1,59 @@
1
+ import axios from 'axios';
2
+ import { loadConfig } from './config.js';
3
+
4
+ /**
5
+ * Create axios instance with configuration
6
+ */
7
+ function createClient(options = {}) {
8
+ const config = loadConfig();
9
+ const baseURL = options.baseUrl || config.baseUrl || 'https://6-dot-authentiqio.appspot.com';
10
+
11
+ const instance = axios.create({
12
+ baseURL,
13
+ timeout: options.timeout || config.timeout || 30000,
14
+ headers: {
15
+ 'User-Agent': '@ktmcp-cli/authentiq-api/1.0.0',
16
+ 'Content-Type': 'application/json'
17
+ }
18
+ });
19
+
20
+ // Add authentication
21
+ instance.interceptors.request.use((config) => {
22
+ // No authentication configured
23
+ return config;
24
+ });
25
+
26
+ return instance;
27
+ }
28
+
29
+ /**
30
+ * Handle API errors consistently
31
+ */
32
+ function handleError(error) {
33
+ if (error.response) {
34
+ return {
35
+ success: false,
36
+ status: error.response.status,
37
+ statusText: error.response.statusText,
38
+ message: error.response.data?.message ||
39
+ error.response.data?.error ||
40
+ 'API request failed',
41
+ data: error.response.data
42
+ };
43
+ } else if (error.request) {
44
+ return {
45
+ success: false,
46
+ status: 0,
47
+ message: 'No response from server',
48
+ error: error.message
49
+ };
50
+ } else {
51
+ return {
52
+ success: false,
53
+ status: 0,
54
+ message: error.message
55
+ };
56
+ }
57
+ }
58
+
59
+ export { createClient, handleError };
@@ -0,0 +1,58 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import dotenv from 'dotenv';
5
+
6
+ dotenv.config();
7
+
8
+ const CONFIG_DIR = join(homedir(), '.authentiq-api');
9
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
10
+
11
+ const DEFAULT_CONFIG = {
12
+ baseUrl: process.env.API_BASE_URL || 'https://6-dot-authentiqio.appspot.com',
13
+ apiKey: process.env.API_KEY || '',
14
+ timeout: parseInt(process.env.API_TIMEOUT || '30000', 10)
15
+ };
16
+
17
+ export function loadConfig() {
18
+ if (existsSync(CONFIG_FILE)) {
19
+ try {
20
+ const fileConfig = JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
21
+ return { ...DEFAULT_CONFIG, ...fileConfig };
22
+ } catch (error) {
23
+ console.error('Error reading config file:', error.message);
24
+ }
25
+ }
26
+ return DEFAULT_CONFIG;
27
+ }
28
+
29
+ export function saveConfig(config) {
30
+ try {
31
+ if (!existsSync(CONFIG_DIR)) {
32
+ mkdirSync(CONFIG_DIR, { recursive: true });
33
+ }
34
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
35
+ return true;
36
+ } catch (error) {
37
+ console.error('Error saving config:', error.message);
38
+ return false;
39
+ }
40
+ }
41
+
42
+ export function getConfig(key) {
43
+ const config = loadConfig();
44
+ return config[key];
45
+ }
46
+
47
+ export function setConfig(key, value) {
48
+ const config = loadConfig();
49
+ config[key] = value;
50
+ return saveConfig(config);
51
+ }
52
+
53
+ export default {
54
+ loadConfig,
55
+ saveConfig,
56
+ getConfig,
57
+ setConfig
58
+ };