@klevar/portal-cli 0.1.20 → 0.1.22

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.
@@ -28,6 +28,12 @@ export declare const CLI_EXEMPTIONS: readonly [{
28
28
  readonly reason: "Inbound webhook consumer called by Klevar Docs, not by operators or clients.";
29
29
  readonly owner: "Klevar Docs";
30
30
  readonly trackedAs: "Phase 17 inbound-webhook exemption";
31
+ }, {
32
+ readonly method: "GET";
33
+ readonly path: "/api/portal/projects/:id/timeline";
34
+ readonly reason: "Client-facing timeline used by the portal project UI; admin/operator timeline CLI already covers recall workflows.";
35
+ readonly owner: "Klevar Portal";
36
+ readonly trackedAs: "Phase 17 client timeline portal-ui exemption";
31
37
  }, {
32
38
  readonly method: "GET";
33
39
  readonly path: "/api/portal/projects/:projectId/recurring-checkpoints";
@@ -34,6 +34,13 @@ export const CLI_EXEMPTIONS = [
34
34
  owner: 'Klevar Docs',
35
35
  trackedAs: 'Phase 17 inbound-webhook exemption',
36
36
  },
37
+ {
38
+ method: 'GET',
39
+ path: '/api/portal/projects/:id/timeline',
40
+ reason: 'Client-facing timeline used by the portal project UI; admin/operator timeline CLI already covers recall workflows.',
41
+ owner: 'Klevar Portal',
42
+ trackedAs: 'Phase 17 client timeline portal-ui exemption',
43
+ },
37
44
  {
38
45
  method: 'GET',
39
46
  path: '/api/portal/projects/:projectId/recurring-checkpoints',
@@ -1 +1 @@
1
- {"version":3,"file":"_exemptions.js","sourceRoot":"","sources":["../../commands/_exemptions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,uEAAuE;QAC/E,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,uEAAuE;QAC/E,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,0GAA0G;QAClH,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,yGAAyG;QACjH,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,qCAAqC;KACjD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,qCAAqC;QAC3C,MAAM,EAAE,8EAA8E;QACtF,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,oCAAoC;KAChD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,uDAAuD;QAC7D,MAAM,EAAE,qFAAqF;QAC7F,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,uDAAuD;QAC7D,MAAM,EAAE,uFAAuF;QAC/F,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,8CAA8C;QACpD,MAAM,EAAE,wDAAwD;QAChE,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,8CAA8C;QACpD,MAAM,EAAE,uDAAuD;QAC/D,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,mDAAmD;QACzD,MAAM,EAAE,yDAAyD;QACjE,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;CACO,CAAC"}
1
+ {"version":3,"file":"_exemptions.js","sourceRoot":"","sources":["../../commands/_exemptions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,uEAAuE;QAC/E,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,uEAAuE;QAC/E,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,0GAA0G;QAClH,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,yGAAyG;QACjH,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,qCAAqC;KACjD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,qCAAqC;QAC3C,MAAM,EAAE,8EAA8E;QACtF,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,oCAAoC;KAChD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,mCAAmC;QACzC,MAAM,EAAE,oHAAoH;QAC5H,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,8CAA8C;KAC1D;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,uDAAuD;QAC7D,MAAM,EAAE,qFAAqF;QAC7F,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,uDAAuD;QAC7D,MAAM,EAAE,uFAAuF;QAC/F,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,8CAA8C;QACpD,MAAM,EAAE,wDAAwD;QAChE,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,8CAA8C;QACpD,MAAM,EAAE,uDAAuD;QAC/D,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,mDAAmD;QACzD,MAAM,EAAE,yDAAyD;QACjE,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,wCAAwC;KACpD;CACO,CAAC"}
@@ -522,6 +522,144 @@ export declare const COMMAND_GROUPS: {
522
522
  description: string;
523
523
  };
524
524
  };
525
+ operator: {
526
+ inbox: {
527
+ method: "GET";
528
+ path: string;
529
+ auth: "apiKey";
530
+ description: string;
531
+ queryParams: string[];
532
+ };
533
+ 'inbox.mark-read': {
534
+ method: "POST";
535
+ path: string;
536
+ auth: "apiKey";
537
+ description: string;
538
+ };
539
+ 'inbox.mark-client-read': {
540
+ method: "POST";
541
+ path: string;
542
+ auth: "apiKey";
543
+ description: string;
544
+ };
545
+ 'inbox.mark-project-read': {
546
+ method: "POST";
547
+ path: string;
548
+ auth: "apiKey";
549
+ description: string;
550
+ };
551
+ 'inbox.mark-all-read': {
552
+ method: "POST";
553
+ path: string;
554
+ auth: "apiKey";
555
+ description: string;
556
+ };
557
+ 'reports.monthly': {
558
+ method: "GET";
559
+ path: string;
560
+ auth: "apiKey";
561
+ description: string;
562
+ queryParams: string[];
563
+ };
564
+ 'client.timeline': {
565
+ method: "GET";
566
+ path: string;
567
+ auth: "apiKey";
568
+ description: string;
569
+ queryParams: string[];
570
+ };
571
+ 'project.timeline': {
572
+ method: "GET";
573
+ path: string;
574
+ auth: "apiKey";
575
+ description: string;
576
+ queryParams: string[];
577
+ };
578
+ 'ops-health': {
579
+ method: "GET";
580
+ path: string;
581
+ auth: "apiKey";
582
+ description: string;
583
+ queryParams: string[];
584
+ };
585
+ 'smoke.portal': {
586
+ method: "GET";
587
+ path: string;
588
+ auth: "apiKey";
589
+ description: string;
590
+ };
591
+ 'smoke.docs': {
592
+ method: "GET";
593
+ path: string;
594
+ auth: "apiKey";
595
+ description: string;
596
+ };
597
+ 'smoke.capacity': {
598
+ method: "GET";
599
+ path: string;
600
+ auth: "apiKey";
601
+ description: string;
602
+ };
603
+ 'smoke.recurring': {
604
+ method: "GET";
605
+ path: string;
606
+ auth: "apiKey";
607
+ description: string;
608
+ };
609
+ 'smoke.notifications': {
610
+ method: "GET";
611
+ path: string;
612
+ auth: "apiKey";
613
+ description: string;
614
+ };
615
+ 'clients.notifications.get': {
616
+ method: "GET";
617
+ path: string;
618
+ auth: "apiKey";
619
+ description: string;
620
+ };
621
+ 'clients.notifications.update': {
622
+ method: "PATCH";
623
+ path: string;
624
+ auth: "apiKey";
625
+ description: string;
626
+ body: string[];
627
+ };
628
+ 'projects.notifications.get': {
629
+ method: "GET";
630
+ path: string;
631
+ auth: "apiKey";
632
+ description: string;
633
+ };
634
+ 'projects.notifications.update': {
635
+ method: "PATCH";
636
+ path: string;
637
+ auth: "apiKey";
638
+ description: string;
639
+ body: string[];
640
+ };
641
+ 'notifications.preview': {
642
+ method: "GET";
643
+ path: string;
644
+ auth: "apiKey";
645
+ description: string;
646
+ queryParams: string[];
647
+ };
648
+ 'audit.list': {
649
+ method: "GET";
650
+ path: string;
651
+ auth: "apiKey";
652
+ description: string;
653
+ queryParams: string[];
654
+ };
655
+ 'audit.target': {
656
+ method: "GET";
657
+ path: string;
658
+ auth: "apiKey";
659
+ description: string;
660
+ queryParams: string[];
661
+ };
662
+ };
525
663
  notes: {
526
664
  'notes.list': {
527
665
  method: "GET";
@@ -3,6 +3,7 @@ import { docsCommands } from './docs.js';
3
3
  import { capacityCommands } from './capacity.js';
4
4
  import { metricCommands } from './metrics.js';
5
5
  import { onboardingCommands } from './onboarding.js';
6
+ import { operatorCommands } from './operator.js';
6
7
  import { noteCommands } from './notes.js';
7
8
  import { portalCommands } from './portal.js';
8
9
  import { projectCommands } from './projects.js';
@@ -20,6 +21,7 @@ export const COMMAND_GROUPS = {
20
21
  usage: usageCommands,
21
22
  updates: updateCommands,
22
23
  onboarding: onboardingCommands,
24
+ operator: operatorCommands,
23
25
  notes: noteCommands,
24
26
  recurring: recurringCommands,
25
27
  metrics: metricCommands,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAK9C,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,eAAe;IACzB,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,gBAAgB;IAC1B,KAAK,EAAE,aAAa;IACpB,OAAO,EAAE,cAAc;IACvB,UAAU,EAAE,kBAAkB;IAC9B,KAAK,EAAE,YAAY;IACnB,SAAS,EAAE,iBAAiB;IAC5B,OAAO,EAAE,cAAc;IACvB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE,cAAc;CACgB,CAAC;AAEzC,MAAM,CAAC,MAAM,QAAQ,GAA+B,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAK9C,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,eAAe;IACzB,KAAK,EAAE,YAAY;IACnB,QAAQ,EAAE,gBAAgB;IAC1B,KAAK,EAAE,aAAa;IACpB,OAAO,EAAE,cAAc;IACvB,UAAU,EAAE,kBAAkB;IAC9B,QAAQ,EAAE,gBAAgB;IAC1B,KAAK,EAAE,YAAY;IACnB,SAAS,EAAE,iBAAiB;IAC5B,OAAO,EAAE,cAAc;IACvB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE,cAAc;CACgB,CAAC;AAEzC,MAAM,CAAC,MAAM,QAAQ,GAA+B,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC"}
@@ -0,0 +1,138 @@
1
+ export declare const operatorCommands: {
2
+ inbox: {
3
+ method: "GET";
4
+ path: string;
5
+ auth: "apiKey";
6
+ description: string;
7
+ queryParams: string[];
8
+ };
9
+ 'inbox.mark-read': {
10
+ method: "POST";
11
+ path: string;
12
+ auth: "apiKey";
13
+ description: string;
14
+ };
15
+ 'inbox.mark-client-read': {
16
+ method: "POST";
17
+ path: string;
18
+ auth: "apiKey";
19
+ description: string;
20
+ };
21
+ 'inbox.mark-project-read': {
22
+ method: "POST";
23
+ path: string;
24
+ auth: "apiKey";
25
+ description: string;
26
+ };
27
+ 'inbox.mark-all-read': {
28
+ method: "POST";
29
+ path: string;
30
+ auth: "apiKey";
31
+ description: string;
32
+ };
33
+ 'reports.monthly': {
34
+ method: "GET";
35
+ path: string;
36
+ auth: "apiKey";
37
+ description: string;
38
+ queryParams: string[];
39
+ };
40
+ 'client.timeline': {
41
+ method: "GET";
42
+ path: string;
43
+ auth: "apiKey";
44
+ description: string;
45
+ queryParams: string[];
46
+ };
47
+ 'project.timeline': {
48
+ method: "GET";
49
+ path: string;
50
+ auth: "apiKey";
51
+ description: string;
52
+ queryParams: string[];
53
+ };
54
+ 'ops-health': {
55
+ method: "GET";
56
+ path: string;
57
+ auth: "apiKey";
58
+ description: string;
59
+ queryParams: string[];
60
+ };
61
+ 'smoke.portal': {
62
+ method: "GET";
63
+ path: string;
64
+ auth: "apiKey";
65
+ description: string;
66
+ };
67
+ 'smoke.docs': {
68
+ method: "GET";
69
+ path: string;
70
+ auth: "apiKey";
71
+ description: string;
72
+ };
73
+ 'smoke.capacity': {
74
+ method: "GET";
75
+ path: string;
76
+ auth: "apiKey";
77
+ description: string;
78
+ };
79
+ 'smoke.recurring': {
80
+ method: "GET";
81
+ path: string;
82
+ auth: "apiKey";
83
+ description: string;
84
+ };
85
+ 'smoke.notifications': {
86
+ method: "GET";
87
+ path: string;
88
+ auth: "apiKey";
89
+ description: string;
90
+ };
91
+ 'clients.notifications.get': {
92
+ method: "GET";
93
+ path: string;
94
+ auth: "apiKey";
95
+ description: string;
96
+ };
97
+ 'clients.notifications.update': {
98
+ method: "PATCH";
99
+ path: string;
100
+ auth: "apiKey";
101
+ description: string;
102
+ body: string[];
103
+ };
104
+ 'projects.notifications.get': {
105
+ method: "GET";
106
+ path: string;
107
+ auth: "apiKey";
108
+ description: string;
109
+ };
110
+ 'projects.notifications.update': {
111
+ method: "PATCH";
112
+ path: string;
113
+ auth: "apiKey";
114
+ description: string;
115
+ body: string[];
116
+ };
117
+ 'notifications.preview': {
118
+ method: "GET";
119
+ path: string;
120
+ auth: "apiKey";
121
+ description: string;
122
+ queryParams: string[];
123
+ };
124
+ 'audit.list': {
125
+ method: "GET";
126
+ path: string;
127
+ auth: "apiKey";
128
+ description: string;
129
+ queryParams: string[];
130
+ };
131
+ 'audit.target': {
132
+ method: "GET";
133
+ path: string;
134
+ auth: "apiKey";
135
+ description: string;
136
+ queryParams: string[];
137
+ };
138
+ };
@@ -0,0 +1,139 @@
1
+ export const operatorCommands = {
2
+ inbox: {
3
+ method: 'GET',
4
+ path: '/api/admin/operator-inbox',
5
+ auth: 'apiKey',
6
+ description: 'Review operator inbox',
7
+ queryParams: ['clientId|client', 'projectId|project', 'since', 'unreadOnly|unread', 'limit'],
8
+ },
9
+ 'inbox.mark-read': {
10
+ method: 'POST',
11
+ path: '/api/admin/operator-inbox/read/:itemId',
12
+ auth: 'apiKey',
13
+ description: 'Mark one inbox item read',
14
+ },
15
+ 'inbox.mark-client-read': {
16
+ method: 'POST',
17
+ path: '/api/admin/operator-inbox/read-client/:clientId',
18
+ auth: 'apiKey',
19
+ description: 'Mark a client inbox section read',
20
+ },
21
+ 'inbox.mark-project-read': {
22
+ method: 'POST',
23
+ path: '/api/admin/operator-inbox/read-project/:projectId',
24
+ auth: 'apiKey',
25
+ description: 'Mark a project inbox section read',
26
+ },
27
+ 'inbox.mark-all-read': {
28
+ method: 'POST',
29
+ path: '/api/admin/operator-inbox/read-all',
30
+ auth: 'apiKey',
31
+ description: 'Mark all current inbox items read',
32
+ },
33
+ 'reports.monthly': {
34
+ method: 'GET',
35
+ path: '/api/admin/clients/:clientId/reports/monthly',
36
+ auth: 'apiKey',
37
+ description: 'Generate monthly client report; optionally scope with --project',
38
+ queryParams: ['period', 'projectId|project'],
39
+ },
40
+ 'client.timeline': {
41
+ method: 'GET',
42
+ path: '/api/admin/clients/:clientId/timeline',
43
+ auth: 'apiKey',
44
+ description: 'Show client timeline',
45
+ queryParams: ['projectId|project', 'type', 'since', 'until', 'limit'],
46
+ },
47
+ 'project.timeline': {
48
+ method: 'GET',
49
+ path: '/api/admin/projects/:projectId/timeline',
50
+ auth: 'apiKey',
51
+ description: 'Show project timeline',
52
+ queryParams: ['type', 'since', 'until', 'limit'],
53
+ },
54
+ 'ops-health': {
55
+ method: 'GET',
56
+ path: '/api/admin/ops-health',
57
+ auth: 'apiKey',
58
+ description: 'Show operational health summary; exits 1 only for critical unless --no-fail or --warn-only is set',
59
+ queryParams: ['noFail|no-fail', 'warnOnly|warn-only'],
60
+ },
61
+ 'smoke.portal': {
62
+ method: 'GET',
63
+ path: '/api/admin/clients/:id',
64
+ auth: 'apiKey',
65
+ description: 'Read-only portal smoke check',
66
+ },
67
+ 'smoke.docs': {
68
+ method: 'GET',
69
+ path: '/api/admin/clients/:id/docs/documents',
70
+ auth: 'apiKey',
71
+ description: 'Read-only Docs smoke check',
72
+ },
73
+ 'smoke.capacity': {
74
+ method: 'GET',
75
+ path: '/api/admin/projects/:projectId/capacity-summary',
76
+ auth: 'apiKey',
77
+ description: 'Read-only capacity smoke check',
78
+ },
79
+ 'smoke.recurring': {
80
+ method: 'GET',
81
+ path: '/api/admin/projects/:projectId/recurring-checkpoints',
82
+ auth: 'apiKey',
83
+ description: 'Read-only recurring smoke check',
84
+ },
85
+ 'smoke.notifications': {
86
+ method: 'GET',
87
+ path: '/api/admin/smoke/notifications/:clientId',
88
+ auth: 'apiKey',
89
+ description: 'Read-only notification smoke check',
90
+ },
91
+ 'clients.notifications.get': {
92
+ method: 'GET',
93
+ path: '/api/admin/clients/:id/notification-preferences',
94
+ auth: 'apiKey',
95
+ description: 'Show client notification preferences',
96
+ },
97
+ 'clients.notifications.update': {
98
+ method: 'PATCH',
99
+ path: '/api/admin/clients/:id/notification-preferences',
100
+ auth: 'apiKey',
101
+ description: 'Update client notification preferences',
102
+ body: ['mode', 'digestFrequency'],
103
+ },
104
+ 'projects.notifications.get': {
105
+ method: 'GET',
106
+ path: '/api/admin/projects/:id/notification-preferences',
107
+ auth: 'apiKey',
108
+ description: 'Show project notification preferences',
109
+ },
110
+ 'projects.notifications.update': {
111
+ method: 'PATCH',
112
+ path: '/api/admin/projects/:id/notification-preferences',
113
+ auth: 'apiKey',
114
+ description: 'Update project notification preferences',
115
+ body: ['mode', 'digestFrequency'],
116
+ },
117
+ 'notifications.preview': {
118
+ method: 'GET',
119
+ path: '/api/admin/notifications/preview/:eventType',
120
+ auth: 'apiKey',
121
+ description: 'Preview notification rendering without sending',
122
+ queryParams: ['clientId|client', 'projectId|project'],
123
+ },
124
+ 'audit.list': {
125
+ method: 'GET',
126
+ path: '/api/admin/audit-events',
127
+ auth: 'apiKey',
128
+ description: 'List operational audit events',
129
+ queryParams: ['clientId|client', 'projectId|project', 'actorId|actor', 'action', 'targetType', 'targetId', 'since', 'until', 'limit'],
130
+ },
131
+ 'audit.target': {
132
+ method: 'GET',
133
+ path: '/api/admin/audit-events',
134
+ auth: 'apiKey',
135
+ description: 'List audit events for one target',
136
+ queryParams: ['targetType', 'targetId', 'limit'],
137
+ },
138
+ };
139
+ //# sourceMappingURL=operator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator.js","sourceRoot":"","sources":["../../commands/operator.ts"],"names":[],"mappings":"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"}
@@ -299,6 +299,50 @@ function interpolatePath(template, id) {
299
299
  return template.replace(':id', id);
300
300
  }
301
301
 
302
+ function isUuid(value) {
303
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
304
+ }
305
+
306
+ function looksLikeOpaqueId(value) {
307
+ return isUuid(value) || /^[a-z]+-\d+$/i.test(value);
308
+ }
309
+
310
+ function pathParamEntity(commandKey, paramName) {
311
+ if (paramName.toLowerCase().includes('client')) return 'client';
312
+ if (paramName.toLowerCase().includes('project')) return 'project';
313
+ if (paramName === 'id' && commandKey.startsWith('clients.')) return 'client';
314
+ if (paramName === 'id' && commandKey.startsWith('projects.')) return 'project';
315
+ if (paramName === 'id' && (commandKey === 'smoke.portal' || commandKey === 'smoke.docs')) return 'client';
316
+ return null;
317
+ }
318
+
319
+ async function resolveNameParam(commandKey, paramName, value, flags) {
320
+ if (!value || looksLikeOpaqueId(value)) return value;
321
+ const entity = pathParamEntity(commandKey, paramName);
322
+ if (!entity) return value;
323
+ const params = new URLSearchParams({ q: value, type: entity, limit: '10' });
324
+ if (entity === 'project' && flags.clientId) params.set('clientId', flags.clientId);
325
+ else if (entity === 'project' && flags.client) params.set('client', flags.client);
326
+ const data = await api('GET', `/api/admin/search?${params.toString()}`);
327
+ const rows = entity === 'client' ? (data.clients || []) : (data.projects || []);
328
+ const exact = rows.filter((row) => (row.name || '').toLowerCase() === value.toLowerCase());
329
+ const candidates = exact.length > 0 ? exact : rows;
330
+ if (candidates.length === 1) return candidates[0].id;
331
+ if (candidates.length === 0) console.error(`No ${entity} found for "${value}".`);
332
+ else {
333
+ console.error(`Ambiguous ${entity} name "${value}". Use an ID or narrow the search.`);
334
+ for (const row of candidates.slice(0, 5)) console.error(` ${row.name} [${row.id}]`);
335
+ }
336
+ exitGracefully(2);
337
+ }
338
+
339
+ async function resolveQueryNameParam(commandKey, canonical, value, flags) {
340
+ if (!value || looksLikeOpaqueId(value)) return value;
341
+ if (canonical === 'clientId') return resolveNameParam(commandKey, 'clientId', value, flags);
342
+ if (canonical === 'projectId') return resolveNameParam(commandKey, 'projectId', value, flags);
343
+ return value;
344
+ }
345
+
302
346
  const HELP_ENUMS = {
303
347
  priority: 'low|normal|high|urgent',
304
348
  submittedBy: 'client|admin|va|system',
@@ -323,6 +367,8 @@ const HELP_ENUMS = {
323
367
  owner: 'admin|client|shared',
324
368
  requiresClientAcceptance: 'true|false',
325
369
  requiresAdminAcceptance: 'true|false',
370
+ mode: 'muted|portal_only|admin_only|material_updates|digest|all',
371
+ digestFrequency: 'daily|weekly|monthly',
326
372
  };
327
373
 
328
374
  function helpFlag(commandKey, field) {
@@ -356,6 +402,11 @@ function helpQueryFlag(field) {
356
402
  // ── Output Formatting ───────────────────────────────────────────
357
403
 
358
404
  function formatOutput(data, commandKey) {
405
+ if (commandKey === 'ops-health' && OUTPUT_JSON) {
406
+ console.log(COMPACT_JSON ? JSON.stringify(data) : JSON.stringify(data, null, 2));
407
+ if (data.status === 'critical' && flags.noFail !== 'true' && flags['no-fail'] !== 'true' && flags.warnOnly !== 'true' && flags['warn-only'] !== 'true') exitGracefully(1);
408
+ return;
409
+ }
359
410
  if (OUTPUT_JSON) {
360
411
  console.log(COMPACT_JSON ? JSON.stringify(data) : JSON.stringify(data, null, 2));
361
412
  return;
@@ -422,6 +473,111 @@ function formatOutput(data, commandKey) {
422
473
  return;
423
474
  }
424
475
 
476
+ if (commandKey === 'inbox') {
477
+ const sections = data.sections || {};
478
+ console.log('\nOperator Inbox');
479
+ console.log('-'.repeat(80));
480
+ for (const [key, section] of Object.entries(sections)) {
481
+ const items = section.items || [];
482
+ if (items.length === 0) continue;
483
+ console.log(`\n${key} (${items.length})`);
484
+ for (const item of items) {
485
+ const unread = item.unread ? '*' : ' ';
486
+ const scope = [item.clientName, item.projectName].filter(Boolean).join(' / ');
487
+ console.log(` ${unread} ${item.summary} ${scope ? `- ${scope}` : ''} [${item.id}]`);
488
+ }
489
+ }
490
+ return;
491
+ }
492
+
493
+ if (commandKey === 'reports.monthly') {
494
+ const report = data.report;
495
+ if (!report) { console.log(JSON.stringify(data, null, 2)); return; }
496
+ if (COMPACT_JSON) {
497
+ console.log(`${report.client.name}: ${report.period} - ${report.completedWork.length} done, ${report.openWork.length} open`);
498
+ return;
499
+ }
500
+ console.log(`\nMonthly Report: ${report.client.name} (${report.period})`);
501
+ console.log('-'.repeat(80));
502
+ console.log(`Completed work: ${report.completedWork.length}`);
503
+ for (const item of report.completedWork) console.log(` - ${item.title}`);
504
+ console.log(`Open work: ${report.openWork.length}`);
505
+ for (const item of report.openWork) console.log(` - ${item.title} [${item.status}]`);
506
+ console.log(`Updates: ${report.updates.length}`);
507
+ console.log(`Invoices: ${report.invoices.length}`);
508
+ console.log(`Capacity items: ${report.capacity.length}`);
509
+ return;
510
+ }
511
+
512
+ if (commandKey === 'client.timeline' || commandKey === 'project.timeline') {
513
+ const items = data.data || [];
514
+ if (items.length === 0) { console.log('No timeline events found.'); return; }
515
+ console.log(`\nTimeline (${items.length})`);
516
+ console.log('-'.repeat(80));
517
+ for (const item of items) {
518
+ const scope = [item.clientName, item.projectName].filter(Boolean).join(' / ');
519
+ console.log(` ${item.type}: ${item.summary}${scope ? ` - ${scope}` : ''} [${item.sourceId}]`);
520
+ }
521
+ return;
522
+ }
523
+
524
+ if (commandKey === 'ops-health') {
525
+ console.log(`Operational health: ${data.status}`);
526
+ for (const [key, value] of Object.entries(data.failures || {})) {
527
+ console.log(` ${key}: ${value}`);
528
+ }
529
+ if (data.status === 'critical' && flags.noFail !== 'true' && flags['no-fail'] !== 'true' && flags.warnOnly !== 'true' && flags['warn-only'] !== 'true') exitGracefully(1);
530
+ return;
531
+ }
532
+
533
+ if (commandKey === 'smoke.notifications') {
534
+ const smoke = data.smoke || {};
535
+ console.log(`Notification smoke: ${smoke.client?.name || 'client'}`);
536
+ console.log(` Read-only dry run: ${smoke.dryRun === false ? 'false' : 'true'}`);
537
+ console.log(` notificationsEnabled: ${smoke.client?.notificationsEnabled}`);
538
+ console.log(` preference: ${smoke.preference?.mode || 'n/a'}`);
539
+ console.log(` failed deliveries: ${smoke.failedCount ?? 0}`);
540
+ console.log(' Would send: none');
541
+ console.log(' Would create: none');
542
+ return;
543
+ }
544
+
545
+ if (
546
+ commandKey === 'clients.notifications.get' ||
547
+ commandKey === 'clients.notifications.update' ||
548
+ commandKey === 'projects.notifications.get' ||
549
+ commandKey === 'projects.notifications.update'
550
+ ) {
551
+ const preference = data.preference || {};
552
+ console.log(`Notification preference: ${preference.mode || 'n/a'}`);
553
+ if (preference.digestFrequency) console.log(` Digest: ${preference.digestFrequency}`);
554
+ if (preference.inheritedFromClient) console.log(' Inherited from client preference.');
555
+ if (preference.inheritedFromNotificationsEnabled) console.log(' Inherited from notificationsEnabled.');
556
+ return;
557
+ }
558
+
559
+ if (commandKey === 'notifications.preview') {
560
+ const preview = data.preview || {};
561
+ console.log(`Notification preview: ${preview.subject || preview.eventType}`);
562
+ console.log(` Audience: ${(preview.audience || []).join(', ') || 'none'}`);
563
+ console.log(` Mode: ${preview.mode || 'n/a'}`);
564
+ console.log(` Thread: ${preview.threadKey || 'n/a'}`);
565
+ console.log('');
566
+ console.log(preview.body || '');
567
+ return;
568
+ }
569
+
570
+ if (commandKey === 'audit.list' || commandKey === 'audit.target') {
571
+ const items = data.data || [];
572
+ if (items.length === 0) { console.log('No audit events found. Audit records operator actions from this release forward; older actions are not backfilled.'); return; }
573
+ console.log(`\nAudit Events (${items.length})`);
574
+ console.log('-'.repeat(80));
575
+ for (const item of items) {
576
+ console.log(` ${item.action}: ${item.targetType}/${item.targetId} [${item.id}]`);
577
+ }
578
+ return;
579
+ }
580
+
425
581
  if (commandKey === 'projects.get') {
426
582
  const p = data.project;
427
583
  console.log(`\n${p.name} [${p.status}]`);
@@ -778,6 +934,11 @@ if (!resource || resource === 'help' || resource === '--help') {
778
934
  }
779
935
  console.log(`\nConfig: PORTAL_API_URL, PORTAL_API_KEY, PORTAL_TOKEN (or ~/.klevar/portal.env)`);
780
936
  console.log(`Target: ${BASE_URL}`);
937
+ console.log('\nExamples:');
938
+ console.log(' klevar-portal inbox --unread --compact');
939
+ console.log(' klevar-portal client timeline "Acme Ltd" --type task');
940
+ console.log(' klevar-portal notifications preview project.status_changed --client "Acme Ltd" --project "Retainer"');
941
+ console.log(' klevar-portal audit target task <taskId>');
781
942
  exitGracefully(0);
782
943
  }
783
944
 
@@ -827,19 +988,25 @@ if (supportsCommentContentInput(commandKey)) {
827
988
  }
828
989
  }
829
990
 
991
+ if (commandKey === 'audit.target' && positional.length >= 2) {
992
+ flags.targetType = positional[0];
993
+ flags.targetId = positional[1];
994
+ }
995
+
830
996
  // Interpolate path
831
997
  let path = cmd.path;
832
998
  const pathParams = [...path.matchAll(/:([A-Za-z][A-Za-z0-9_]*)/g)].map((match) => match[1]);
833
999
  if (pathParams.length > 0) {
834
- pathParams.forEach((paramName, index) => {
835
- const value = positional[index];
1000
+ for (const [index, paramName] of pathParams.entries()) {
1001
+ let value = positional[index];
836
1002
  if (!value) {
837
1003
  const ordinal = index === 0 ? 'an ID argument' : `argument ${index + 1} for :${paramName}`;
838
1004
  console.error(`Command '${commandKey}' requires ${ordinal}.`);
839
1005
  exitGracefully(1);
840
1006
  }
1007
+ value = await resolveNameParam(commandKey, paramName, value, flags);
841
1008
  path = path.replace(`:${paramName}`, value);
842
- });
1009
+ }
843
1010
  }
844
1011
 
845
1012
  // Append query parameters for GET commands with filters
@@ -851,13 +1018,23 @@ if (cmd.queryParams || cmd.fixedQuery) {
851
1018
  for (const key of cmd.queryParams || []) {
852
1019
  const aliases = key.split('|');
853
1020
  const canonical = aliases[0];
854
- const value = aliases.map((alias) => flags[alias]).find((candidate) => candidate !== undefined);
855
- if (value !== undefined) params.set(canonical, value);
1021
+ let value = aliases.map((alias) => flags[alias]).find((candidate) => candidate !== undefined);
1022
+ if (value !== undefined) {
1023
+ value = await resolveQueryNameParam(commandKey, canonical, value, flags);
1024
+ flags[canonical] = value;
1025
+ for (const alias of aliases) flags[alias] = value;
1026
+ params.set(canonical, value);
1027
+ }
856
1028
  }
857
1029
  const qs = params.toString();
858
1030
  if (qs) path += (path.includes('?') ? '&' : '?') + qs;
859
1031
  }
860
1032
 
1033
+ if (commandKey.startsWith('smoke.') && (flags.write === 'true' || flags.send === 'true')) {
1034
+ console.error(`Command '${commandKey}' is read-only; --write/--send are not supported for this smoke check.`);
1035
+ exitGracefully(2);
1036
+ }
1037
+
861
1038
  // Build body from flags or fixedBody
862
1039
  let body = undefined;
863
1040
  if (cmd.fixedBody) {
@@ -887,6 +1064,13 @@ if (supportsCommentContentInput(commandKey) && !body && positional[pathParams.le
887
1064
  body = { content: normalizeCliMultiline(positional.slice(pathParams.length).join(' ')) };
888
1065
  }
889
1066
 
1067
+ if (flags['dry-run'] === 'true' && ['POST', 'PATCH', 'DELETE'].includes(cmd.method)) {
1068
+ console.log(`Dry run: ${cmd.method} ${path}`);
1069
+ if (body) console.log(COMPACT_JSON ? JSON.stringify(body) : JSON.stringify(body, null, 2));
1070
+ else console.log('No request body.');
1071
+ exitGracefully(0);
1072
+ }
1073
+
890
1074
  // ── Search Command ──
891
1075
  if (commandKey === 'search') {
892
1076
  const q = [id, ...positional.slice(1)].filter(Boolean).join(' ');
@@ -894,8 +1078,13 @@ if (commandKey === 'search') {
894
1078
  console.error('Usage: search <term>');
895
1079
  exitGracefully(1);
896
1080
  }
897
- const data = await api('GET', `/api/admin/search?q=${encodeURIComponent(q)}`);
898
- const hasResults = data.projects.length + data.tasks.length + data.updates.length > 0;
1081
+ const params = new URLSearchParams({ q });
1082
+ for (const key of ['client', 'clientId', 'project', 'projectId', 'type', 'limit']) {
1083
+ if (flags[key] !== undefined) params.set(key, flags[key]);
1084
+ }
1085
+ const data = await api('GET', `/api/admin/search?${params.toString()}`);
1086
+ const resultGroups = ['clients', 'projects', 'tasks', 'updates', 'comments', 'notes', 'docs', 'capacity'];
1087
+ const hasResults = resultGroups.some((group) => (data[group] || []).length > 0);
899
1088
 
900
1089
  if (!hasResults) {
901
1090
  console.log(`No results for "${q}"`);
@@ -905,7 +1094,15 @@ if (commandKey === 'search') {
905
1094
  console.log(`\nSearch: "${q}"`);
906
1095
  console.log('─'.repeat(50));
907
1096
 
908
- if (data.projects.length > 0) {
1097
+ if (data.clients?.length > 0) {
1098
+ console.log('\nClients:');
1099
+ data.clients.forEach((c) => {
1100
+ const company = c.company ? ` (${c.company})` : '';
1101
+ console.log(` • ${c.name}${company} [${c.status}] [${c.id}]`);
1102
+ });
1103
+ }
1104
+
1105
+ if ((data.projects || []).length > 0) {
909
1106
  console.log('\nProjects:');
910
1107
  data.projects.forEach((p) => {
911
1108
  const visibility = p.visibility ? ` [${p.visibility}]` : '';
@@ -915,7 +1112,7 @@ if (commandKey === 'search') {
915
1112
  console.log('\nProjects: (none)');
916
1113
  }
917
1114
 
918
- if (data.tasks.length > 0) {
1115
+ if ((data.tasks || []).length > 0) {
919
1116
  console.log('\nTasks:');
920
1117
  data.tasks.forEach((t) => {
921
1118
  const prio = t.priority && t.priority !== 'normal' ? ` (${t.priority})` : '';
@@ -925,7 +1122,7 @@ if (commandKey === 'search') {
925
1122
  console.log('\nTasks: (none)');
926
1123
  }
927
1124
 
928
- if (data.updates.length > 0) {
1125
+ if ((data.updates || []).length > 0) {
929
1126
  console.log('\nUpdates:');
930
1127
  data.updates.forEach((u) => {
931
1128
  const vis = u.visibility === 'internal' ? ' [INTERNAL]' : '';
@@ -935,6 +1132,16 @@ if (commandKey === 'search') {
935
1132
  console.log('\nUpdates: (none)');
936
1133
  }
937
1134
 
1135
+ for (const [label, key] of [['Comments', 'comments'], ['Notes', 'notes'], ['Docs', 'docs'], ['Capacity', 'capacity']]) {
1136
+ const items = data[key] || [];
1137
+ if (items.length === 0) continue;
1138
+ console.log(`\n${label}:`);
1139
+ items.forEach((item) => {
1140
+ const text = item.title || item.contentPreview || item.documentNumber || item.documentType || item.status || item.billableStatus || item.id;
1141
+ console.log(` • ${text} [${item.id}]`);
1142
+ });
1143
+ }
1144
+
938
1145
  exitGracefully(0);
939
1146
  }
940
1147
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klevar/portal-cli",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "First-class npm CLI for the Klevar Client Management Portal",
5
5
  "type": "module",
6
6
  "bin": {