spaps 0.7.5 ā 0.7.7
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/AI_TOOLS.json +5566 -38
- package/README.md +65 -13
- package/assets/local-runtime/Dockerfile +13 -0
- package/assets/local-runtime/docker-compose.yml +2 -1
- package/assets/local-runtime/manifest.json +3 -1
- package/bin/spaps.js +34 -8
- package/package.json +3 -4
- package/src/ai-helper.js +44 -10
- package/src/ai-tool-spec.js +19 -4
- package/src/cli-dispatcher.js +365 -91
- package/src/doctor.js +58 -1
- package/src/domain-cli.js +79 -0
- package/src/domains.js +193 -0
- package/src/fixture-kernel.js +898 -29
- package/src/handlers.js +471 -4
- package/src/home-view.js +200 -0
- package/src/local-runtime.js +19 -4
- package/src/local-server.js +30 -1
package/src/handlers.js
CHANGED
|
@@ -7,6 +7,7 @@ const { showInteractiveDocs, showQuickReference, searchDocs } = require('./docs-
|
|
|
7
7
|
const { getQuickStartInstructions, getServerStatus, runQuickTest } = require('./ai-helper');
|
|
8
8
|
const { buildToolSpec } = require('./ai-tool-spec');
|
|
9
9
|
const { runDoctor } = require('./doctor');
|
|
10
|
+
const { buildHomeView, renderHomeView } = require('./home-view');
|
|
10
11
|
const {
|
|
11
12
|
applyFixtures,
|
|
12
13
|
exportStorageState,
|
|
@@ -20,6 +21,7 @@ const {
|
|
|
20
21
|
whoamiHandler,
|
|
21
22
|
tokenHandler,
|
|
22
23
|
} = require('./auth/handlers');
|
|
24
|
+
const { callEndpoint, emit, emitAuthError } = require('./domain-cli');
|
|
23
25
|
|
|
24
26
|
function createHandlers(version, logo) {
|
|
25
27
|
function invalidArgument(message) {
|
|
@@ -28,7 +30,54 @@ function createHandlers(version, logo) {
|
|
|
28
30
|
return error;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
const verifyHandler = async ({ options }) => {
|
|
34
|
+
const result = await runQuickTest({
|
|
35
|
+
port: options.port,
|
|
36
|
+
serverUrl: options.serverUrl,
|
|
37
|
+
cwd: process.cwd(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (options.json) {
|
|
41
|
+
console.log(JSON.stringify(result, null, 2));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(chalk.yellow('\nš SPAPS Verify\n'));
|
|
46
|
+
console.log(result.success ? chalk.green(result.summary) : chalk.yellow(result.summary));
|
|
47
|
+
console.log();
|
|
48
|
+
(result.results || []).forEach((entry) => {
|
|
49
|
+
const mark = entry.success ? chalk.green('ā') : chalk.red('ā');
|
|
50
|
+
const message = entry.message ? `: ${entry.message}` : '';
|
|
51
|
+
console.log(` ${mark} ${entry.test}${message}`);
|
|
52
|
+
if (!entry.success && entry.fix) {
|
|
53
|
+
console.log(chalk.gray(` fix: ${entry.fix}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (Array.isArray(result.next_steps) && result.next_steps.length > 0) {
|
|
57
|
+
console.log();
|
|
58
|
+
console.log(chalk.bold('Next'));
|
|
59
|
+
result.next_steps.forEach((step) => {
|
|
60
|
+
console.log(chalk.cyan(` ${step}`));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
};
|
|
65
|
+
|
|
31
66
|
return {
|
|
67
|
+
home: async ({ options }) => {
|
|
68
|
+
const view = await buildHomeView({
|
|
69
|
+
port: options.port,
|
|
70
|
+
serverUrl: options.serverUrl,
|
|
71
|
+
cwd: process.cwd(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (options.json) {
|
|
75
|
+
console.log(JSON.stringify(view, null, 2));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
renderHomeView(view, { logo });
|
|
80
|
+
},
|
|
32
81
|
local: async ({ options }) => {
|
|
33
82
|
const isJson = options.json;
|
|
34
83
|
if (!isJson) console.log(logo);
|
|
@@ -127,10 +176,7 @@ function createHandlers(version, logo) {
|
|
|
127
176
|
}
|
|
128
177
|
}
|
|
129
178
|
},
|
|
130
|
-
|
|
131
|
-
const result = await runQuickTest(options.port);
|
|
132
|
-
console.log(JSON.stringify(result, null, 2));
|
|
133
|
-
},
|
|
179
|
+
verify: verifyHandler,
|
|
134
180
|
init: async ({ options }) => {
|
|
135
181
|
const isJson = options.json;
|
|
136
182
|
const envContent = `# SPAPS Local Development\nSPAPS_API_URL=http://localhost:${DEFAULT_PORT}\n# SPAPS_API_KEY=your-api-key-here\n`;
|
|
@@ -305,6 +351,8 @@ function createHandlers(version, logo) {
|
|
|
305
351
|
port: options.port,
|
|
306
352
|
baseUrl: options.baseUrl,
|
|
307
353
|
version,
|
|
354
|
+
persona: options.persona,
|
|
355
|
+
seed: options.seed,
|
|
308
356
|
});
|
|
309
357
|
break;
|
|
310
358
|
case 'reset':
|
|
@@ -313,6 +361,7 @@ function createHandlers(version, logo) {
|
|
|
313
361
|
port: options.port,
|
|
314
362
|
baseUrl: options.baseUrl,
|
|
315
363
|
version,
|
|
364
|
+
seed: options.seed,
|
|
316
365
|
});
|
|
317
366
|
break;
|
|
318
367
|
case 'storage-state':
|
|
@@ -325,6 +374,7 @@ function createHandlers(version, logo) {
|
|
|
325
374
|
baseUrl: options.baseUrl,
|
|
326
375
|
version,
|
|
327
376
|
persona: options.persona,
|
|
377
|
+
seed: options.seed,
|
|
328
378
|
});
|
|
329
379
|
break;
|
|
330
380
|
default:
|
|
@@ -362,6 +412,13 @@ function createHandlers(version, logo) {
|
|
|
362
412
|
});
|
|
363
413
|
}
|
|
364
414
|
|
|
415
|
+
if (Array.isArray(result.seeding?.personas) && result.seeding.personas.length > 0) {
|
|
416
|
+
console.log(chalk.green('\nSeeded personas:'));
|
|
417
|
+
result.seeding.personas.forEach((entry) => {
|
|
418
|
+
console.log(chalk.gray(` ⢠${entry.persona} (${entry.request_count} requests)`));
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
365
422
|
if (result.generated?.personas?.length) {
|
|
366
423
|
console.log(chalk.green('\nGenerated personas:'));
|
|
367
424
|
result.generated.personas.forEach((entry) => {
|
|
@@ -410,11 +467,421 @@ function createHandlers(version, logo) {
|
|
|
410
467
|
doctor: async ({ options }) => {
|
|
411
468
|
await runDoctor({ port: options.port || DEFAULT_PORT, stripe: options.stripe || null, json: options.json });
|
|
412
469
|
},
|
|
470
|
+
test: verifyHandler,
|
|
413
471
|
login: loginHandler,
|
|
414
472
|
logout: logoutHandler,
|
|
415
473
|
whoami: whoamiHandler,
|
|
416
474
|
token: tokenHandler,
|
|
475
|
+
dayrate: dayrateHandler,
|
|
476
|
+
email: emailHandler,
|
|
477
|
+
policy: policyHandler,
|
|
478
|
+
webhook: webhookHandler,
|
|
479
|
+
'issue-reports': issueReportsHandler,
|
|
417
480
|
};
|
|
418
481
|
}
|
|
419
482
|
|
|
483
|
+
function emitText({ intent, result, isJson, successMessage = null }) {
|
|
484
|
+
if (isJson) {
|
|
485
|
+
emit({ intent, result, isJson, successMessage });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (!result.ok) {
|
|
489
|
+
emit({ intent, result, isJson, successMessage });
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (successMessage) {
|
|
493
|
+
console.log(successMessage);
|
|
494
|
+
}
|
|
495
|
+
const output = typeof result.data === 'string' ? result.data : JSON.stringify(result.data, null, 2);
|
|
496
|
+
process.stdout.write(output);
|
|
497
|
+
if (!output.endsWith('\n')) process.stdout.write('\n');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function parseJsonFlag(raw, label) {
|
|
501
|
+
if (raw === null || raw === undefined) {
|
|
502
|
+
return { ok: true, value: null };
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
return { ok: true, value: JSON.parse(raw) };
|
|
506
|
+
} catch {
|
|
507
|
+
console.error(`${label}: must be valid JSON`);
|
|
508
|
+
process.exitCode = 2;
|
|
509
|
+
return { ok: false, value: null };
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function parseBooleanFlag(raw, label) {
|
|
514
|
+
if (raw === null || raw === undefined) {
|
|
515
|
+
return { ok: true, value: null };
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (typeof raw === 'boolean') {
|
|
519
|
+
return { ok: true, value: raw };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const normalized = String(raw).trim().toLowerCase();
|
|
523
|
+
if (['true', '1', 'yes', 'y'].includes(normalized)) {
|
|
524
|
+
return { ok: true, value: true };
|
|
525
|
+
}
|
|
526
|
+
if (['false', '0', 'no', 'n'].includes(normalized)) {
|
|
527
|
+
return { ok: true, value: false };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
console.error(`${label}: must be one of true|false|1|0|yes|no`);
|
|
531
|
+
process.exitCode = 2;
|
|
532
|
+
return { ok: false, value: null };
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function requireOption(value, message) {
|
|
536
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
console.error(message);
|
|
540
|
+
process.exitCode = 2;
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function assignIfDefined(target, key, value) {
|
|
545
|
+
if (value !== null && value !== undefined) {
|
|
546
|
+
target[key] = value;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
async function dayrateHandler({ options }) {
|
|
551
|
+
const isJson = Boolean(options.json);
|
|
552
|
+
const sub = options.subcommand;
|
|
553
|
+
if (sub !== 'config') {
|
|
554
|
+
console.error('dayrate: unknown subcommand. Supported: config');
|
|
555
|
+
process.exitCode = 2;
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
try {
|
|
559
|
+
const result = await callEndpoint({ options, method: 'GET', path: '/api/dayrate/admin/config' });
|
|
560
|
+
emit({ intent: 'dayrate.config', result, isJson });
|
|
561
|
+
if (!result.ok) process.exitCode = 1;
|
|
562
|
+
} catch (err) {
|
|
563
|
+
emitAuthError('dayrate.config', err, isJson);
|
|
564
|
+
process.exitCode = 1;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async function emailHandler({ options }) {
|
|
569
|
+
const isJson = Boolean(options.json);
|
|
570
|
+
const sub = options.subcommand;
|
|
571
|
+
|
|
572
|
+
try {
|
|
573
|
+
if (sub === 'send') {
|
|
574
|
+
if (!requireOption(options.templateKey, 'email send: --template-key is required')) return;
|
|
575
|
+
if (!requireOption(options.to, 'email send: --to is required')) return;
|
|
576
|
+
|
|
577
|
+
const context = parseJsonFlag(options.context, 'email send: --context');
|
|
578
|
+
if (!context.ok) return;
|
|
579
|
+
|
|
580
|
+
const body = {
|
|
581
|
+
template_key: options.templateKey,
|
|
582
|
+
to: options.to,
|
|
583
|
+
};
|
|
584
|
+
assignIfDefined(body, 'context', context.value);
|
|
585
|
+
assignIfDefined(body, 'user_id', options.userId);
|
|
586
|
+
assignIfDefined(body, 'owner_id', options.ownerId);
|
|
587
|
+
assignIfDefined(body, 'subject_override', options.subjectOverride);
|
|
588
|
+
assignIfDefined(body, 'body_override', options.bodyOverride);
|
|
589
|
+
|
|
590
|
+
const result = await callEndpoint({ options, method: 'POST', path: '/api/email/send', body });
|
|
591
|
+
emit({ intent: 'email.send', result, isJson });
|
|
592
|
+
if (!result.ok) process.exitCode = 1;
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (sub === 'get-template') {
|
|
597
|
+
if (!requireOption(options.templateKey, 'email get-template: --template-key is required')) return;
|
|
598
|
+
const result = await callEndpoint({
|
|
599
|
+
options,
|
|
600
|
+
method: 'GET',
|
|
601
|
+
path: `/api/email/templates/${encodeURIComponent(options.templateKey)}`,
|
|
602
|
+
});
|
|
603
|
+
emit({ intent: 'email.get-template', result, isJson });
|
|
604
|
+
if (!result.ok) process.exitCode = 1;
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (sub === 'preview') {
|
|
609
|
+
if (!requireOption(options.templateKey, 'email preview: --template-key is required')) return;
|
|
610
|
+
|
|
611
|
+
const context = parseJsonFlag(options.context, 'email preview: --context');
|
|
612
|
+
if (!context.ok) return;
|
|
613
|
+
|
|
614
|
+
const path = `/api/email/templates/${encodeURIComponent(options.templateKey)}/preview`;
|
|
615
|
+
const result = context.value === null
|
|
616
|
+
? await callEndpoint({ options, method: 'GET', path })
|
|
617
|
+
: await callEndpoint({
|
|
618
|
+
options,
|
|
619
|
+
method: 'POST',
|
|
620
|
+
path,
|
|
621
|
+
body: { context: context.value },
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
emitText({ intent: 'email.preview', result, isJson });
|
|
625
|
+
if (!result.ok) process.exitCode = 1;
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (sub === 'logs') {
|
|
630
|
+
const query = {};
|
|
631
|
+
if (options.ownerId) query.owner_id = options.ownerId;
|
|
632
|
+
if (options.userId) query.user_id = options.userId;
|
|
633
|
+
if (options.limit) query.limit = options.limit;
|
|
634
|
+
if (options.offset) query.offset = options.offset;
|
|
635
|
+
const result = await callEndpoint({ options, method: 'GET', path: '/api/email/logs', query });
|
|
636
|
+
emit({ intent: 'email.logs', result, isJson });
|
|
637
|
+
if (!result.ok) process.exitCode = 1;
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (sub === 'list-templates') {
|
|
642
|
+
const result = await callEndpoint({ options, method: 'GET', path: '/api/email/templates' });
|
|
643
|
+
emit({ intent: 'email.list-templates', result, isJson });
|
|
644
|
+
if (!result.ok) process.exitCode = 1;
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (sub === 'create-template') {
|
|
649
|
+
if (!requireOption(options.templateKey, 'email create-template: --template-key is required')) return;
|
|
650
|
+
if (!requireOption(options.name, 'email create-template: --name is required')) return;
|
|
651
|
+
if (!requireOption(options.subject, 'email create-template: --subject is required')) return;
|
|
652
|
+
if (!requireOption(options.htmlBody, 'email create-template: --html-body is required')) return;
|
|
653
|
+
|
|
654
|
+
const variables = parseJsonFlag(options.variables, 'email create-template: --variables');
|
|
655
|
+
if (!variables.ok) return;
|
|
656
|
+
const sampleContext = parseJsonFlag(options.sampleContext, 'email create-template: --sample-context');
|
|
657
|
+
if (!sampleContext.ok) return;
|
|
658
|
+
const isActive = parseBooleanFlag(options.isActive, 'email create-template: --is-active');
|
|
659
|
+
if (!isActive.ok) return;
|
|
660
|
+
|
|
661
|
+
const body = {
|
|
662
|
+
template_key: options.templateKey,
|
|
663
|
+
name: options.name,
|
|
664
|
+
subject: options.subject,
|
|
665
|
+
html_body: options.htmlBody,
|
|
666
|
+
};
|
|
667
|
+
assignIfDefined(body, 'description', options.description);
|
|
668
|
+
assignIfDefined(body, 'text_body', options.textBody);
|
|
669
|
+
assignIfDefined(body, 'from_email', options.fromEmail);
|
|
670
|
+
assignIfDefined(body, 'from_name', options.fromName);
|
|
671
|
+
assignIfDefined(body, 'reply_to', options.replyTo);
|
|
672
|
+
assignIfDefined(body, 'variables', variables.value);
|
|
673
|
+
assignIfDefined(body, 'sample_context', sampleContext.value);
|
|
674
|
+
assignIfDefined(body, 'is_active', isActive.value);
|
|
675
|
+
assignIfDefined(body, 'category', options.category);
|
|
676
|
+
|
|
677
|
+
const result = await callEndpoint({ options, method: 'POST', path: '/api/email/templates', body });
|
|
678
|
+
emit({ intent: 'email.create-template', result, isJson });
|
|
679
|
+
if (!result.ok) process.exitCode = 1;
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (sub === 'update-template') {
|
|
684
|
+
if (!requireOption(options.templateKey, 'email update-template: --template-key is required')) return;
|
|
685
|
+
|
|
686
|
+
const variables = parseJsonFlag(options.variables, 'email update-template: --variables');
|
|
687
|
+
if (!variables.ok) return;
|
|
688
|
+
const sampleContext = parseJsonFlag(options.sampleContext, 'email update-template: --sample-context');
|
|
689
|
+
if (!sampleContext.ok) return;
|
|
690
|
+
const isActive = parseBooleanFlag(options.isActive, 'email update-template: --is-active');
|
|
691
|
+
if (!isActive.ok) return;
|
|
692
|
+
|
|
693
|
+
const body = {};
|
|
694
|
+
assignIfDefined(body, 'name', options.name);
|
|
695
|
+
assignIfDefined(body, 'description', options.description);
|
|
696
|
+
assignIfDefined(body, 'subject', options.subject);
|
|
697
|
+
assignIfDefined(body, 'html_body', options.htmlBody);
|
|
698
|
+
assignIfDefined(body, 'text_body', options.textBody);
|
|
699
|
+
assignIfDefined(body, 'from_email', options.fromEmail);
|
|
700
|
+
assignIfDefined(body, 'from_name', options.fromName);
|
|
701
|
+
assignIfDefined(body, 'reply_to', options.replyTo);
|
|
702
|
+
assignIfDefined(body, 'variables', variables.value);
|
|
703
|
+
assignIfDefined(body, 'sample_context', sampleContext.value);
|
|
704
|
+
assignIfDefined(body, 'is_active', isActive.value);
|
|
705
|
+
assignIfDefined(body, 'category', options.category);
|
|
706
|
+
|
|
707
|
+
if (Object.keys(body).length === 0) {
|
|
708
|
+
console.error('email update-template: provide at least one field to update');
|
|
709
|
+
process.exitCode = 2;
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const result = await callEndpoint({
|
|
714
|
+
options,
|
|
715
|
+
method: 'PUT',
|
|
716
|
+
path: `/api/email/templates/${encodeURIComponent(options.templateKey)}`,
|
|
717
|
+
body,
|
|
718
|
+
});
|
|
719
|
+
emit({ intent: 'email.update-template', result, isJson });
|
|
720
|
+
if (!result.ok) process.exitCode = 1;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (sub === 'get-override') {
|
|
725
|
+
if (!requireOption(options.templateKey, 'email get-override: --template-key is required')) return;
|
|
726
|
+
const result = await callEndpoint({
|
|
727
|
+
options,
|
|
728
|
+
method: 'GET',
|
|
729
|
+
path: `/api/email/templates/${encodeURIComponent(options.templateKey)}/override`,
|
|
730
|
+
});
|
|
731
|
+
emit({ intent: 'email.get-override', result, isJson });
|
|
732
|
+
if (!result.ok) process.exitCode = 1;
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (sub === 'set-override') {
|
|
737
|
+
if (!requireOption(options.templateKey, 'email set-override: --template-key is required')) return;
|
|
738
|
+
const body = {};
|
|
739
|
+
assignIfDefined(body, 'subject_override', options.subjectOverride);
|
|
740
|
+
assignIfDefined(body, 'body_override', options.bodyOverride);
|
|
741
|
+
if (Object.keys(body).length === 0) {
|
|
742
|
+
console.error('email set-override: provide --subject-override and/or --body-override');
|
|
743
|
+
process.exitCode = 2;
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
const result = await callEndpoint({
|
|
747
|
+
options,
|
|
748
|
+
method: 'PUT',
|
|
749
|
+
path: `/api/email/templates/${encodeURIComponent(options.templateKey)}/override`,
|
|
750
|
+
body,
|
|
751
|
+
});
|
|
752
|
+
emit({ intent: 'email.set-override', result, isJson });
|
|
753
|
+
if (!result.ok) process.exitCode = 1;
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (sub === 'clear-override') {
|
|
758
|
+
if (!requireOption(options.templateKey, 'email clear-override: --template-key is required')) return;
|
|
759
|
+
const result = await callEndpoint({
|
|
760
|
+
options,
|
|
761
|
+
method: 'DELETE',
|
|
762
|
+
path: `/api/email/templates/${encodeURIComponent(options.templateKey)}/override`,
|
|
763
|
+
});
|
|
764
|
+
emit({ intent: 'email.clear-override', result, isJson });
|
|
765
|
+
if (!result.ok) process.exitCode = 1;
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
console.error(
|
|
770
|
+
'email: unknown subcommand. Supported: send, get-template, preview, logs, list-templates, create-template, update-template, get-override, set-override, clear-override'
|
|
771
|
+
);
|
|
772
|
+
process.exitCode = 2;
|
|
773
|
+
} catch (err) {
|
|
774
|
+
emitAuthError(`email.${sub || 'unknown'}`, err, isJson);
|
|
775
|
+
process.exitCode = 1;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
async function policyHandler({ options }) {
|
|
780
|
+
const isJson = Boolean(options.json);
|
|
781
|
+
const sub = options.subcommand;
|
|
782
|
+
try {
|
|
783
|
+
if (sub === 'list') {
|
|
784
|
+
const query = {};
|
|
785
|
+
if (options.isActive !== null && options.isActive !== undefined) query.is_active = options.isActive;
|
|
786
|
+
if (options.limit) query.limit = options.limit;
|
|
787
|
+
const result = await callEndpoint({ options, method: 'GET', path: '/api/policies', query });
|
|
788
|
+
emit({ intent: 'policy.list', result, isJson });
|
|
789
|
+
if (!result.ok) process.exitCode = 1;
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (sub === 'create') {
|
|
793
|
+
if (!options.name || !options.effect) {
|
|
794
|
+
console.error('policy create: --name and --effect are required');
|
|
795
|
+
process.exitCode = 2;
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
let conditions = {};
|
|
799
|
+
if (options.conditions) {
|
|
800
|
+
try { conditions = JSON.parse(options.conditions); }
|
|
801
|
+
catch { console.error('policy create: --conditions must be valid JSON'); process.exitCode = 2; return; }
|
|
802
|
+
}
|
|
803
|
+
const body = {
|
|
804
|
+
name: options.name,
|
|
805
|
+
effect: options.effect,
|
|
806
|
+
conditions,
|
|
807
|
+
description: options.description || null,
|
|
808
|
+
priority: options.priority || 0,
|
|
809
|
+
};
|
|
810
|
+
const result = await callEndpoint({ options, method: 'POST', path: '/api/policies', body });
|
|
811
|
+
emit({ intent: 'policy.create', result, isJson });
|
|
812
|
+
if (!result.ok) process.exitCode = 1;
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (sub === 'delete') {
|
|
816
|
+
if (!options.id) {
|
|
817
|
+
console.error('policy delete: --id is required');
|
|
818
|
+
process.exitCode = 2;
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const result = await callEndpoint({ options, method: 'DELETE', path: `/api/policies/${encodeURIComponent(options.id)}` });
|
|
822
|
+
emit({ intent: 'policy.delete', result, isJson });
|
|
823
|
+
if (!result.ok) process.exitCode = 1;
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
console.error('policy: unknown subcommand. Supported: list, create, delete');
|
|
827
|
+
process.exitCode = 2;
|
|
828
|
+
} catch (err) {
|
|
829
|
+
emitAuthError(`policy.${sub || 'unknown'}`, err, isJson);
|
|
830
|
+
process.exitCode = 1;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
async function webhookHandler({ options }) {
|
|
835
|
+
const isJson = Boolean(options.json);
|
|
836
|
+
const sub = options.subcommand;
|
|
837
|
+
try {
|
|
838
|
+
if (sub === 'list') {
|
|
839
|
+
const result = await callEndpoint({ options, method: 'GET', path: '/api/webhooks' });
|
|
840
|
+
emit({ intent: 'webhook.list', result, isJson });
|
|
841
|
+
if (!result.ok) process.exitCode = 1;
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (sub === 'register') {
|
|
845
|
+
if (!options.url || !options.events) {
|
|
846
|
+
console.error('webhook register: --url and --events (comma-separated) are required');
|
|
847
|
+
process.exitCode = 2;
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const events = String(options.events).split(',').map((e) => e.trim()).filter(Boolean);
|
|
851
|
+
const body = { url: options.url, events };
|
|
852
|
+
const result = await callEndpoint({ options, method: 'POST', path: '/api/webhooks', body });
|
|
853
|
+
emit({ intent: 'webhook.register', result, isJson });
|
|
854
|
+
if (!result.ok) process.exitCode = 1;
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
console.error('webhook: unknown subcommand. Supported: list, register');
|
|
858
|
+
process.exitCode = 2;
|
|
859
|
+
} catch (err) {
|
|
860
|
+
emitAuthError(`webhook.${sub || 'unknown'}`, err, isJson);
|
|
861
|
+
process.exitCode = 1;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async function issueReportsHandler({ options }) {
|
|
866
|
+
const isJson = Boolean(options.json);
|
|
867
|
+
const sub = options.subcommand;
|
|
868
|
+
if (sub !== 'list-mine') {
|
|
869
|
+
console.error('issue-reports: unknown subcommand. Supported: list-mine');
|
|
870
|
+
process.exitCode = 2;
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
try {
|
|
874
|
+
const query = {};
|
|
875
|
+
if (options.status) query.status = options.status;
|
|
876
|
+
if (options.limit) query.limit = options.limit;
|
|
877
|
+
if (options.offset) query.offset = options.offset;
|
|
878
|
+
const result = await callEndpoint({ options, method: 'GET', path: '/api/v1/issue-reports', query });
|
|
879
|
+
emit({ intent: 'issue-reports.list-mine', result, isJson });
|
|
880
|
+
if (!result.ok) process.exitCode = 1;
|
|
881
|
+
} catch (err) {
|
|
882
|
+
emitAuthError('issue-reports.list-mine', err, isJson);
|
|
883
|
+
process.exitCode = 1;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
420
887
|
module.exports = { createHandlers };
|