@klevar/portal-cli 0.1.11 → 0.1.13

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.
@@ -522,6 +522,74 @@ export declare const COMMAND_GROUPS: {
522
522
  description: string;
523
523
  };
524
524
  };
525
+ notes: {
526
+ 'notes.list': {
527
+ method: "GET";
528
+ path: string;
529
+ auth: "apiKey";
530
+ description: string;
531
+ queryParams: string[];
532
+ };
533
+ 'notes.create': {
534
+ method: "POST";
535
+ path: string;
536
+ auth: "apiKey";
537
+ description: string;
538
+ body: string[];
539
+ };
540
+ 'notes.update': {
541
+ method: "PATCH";
542
+ path: string;
543
+ auth: "apiKey";
544
+ description: string;
545
+ body: string[];
546
+ };
547
+ 'notes.comments': {
548
+ method: "GET";
549
+ path: string;
550
+ auth: "apiKey";
551
+ description: string;
552
+ };
553
+ 'notes.reply': {
554
+ method: "POST";
555
+ path: string;
556
+ auth: "apiKey";
557
+ description: string;
558
+ body: string[];
559
+ };
560
+ 'portal.notes': {
561
+ method: "GET";
562
+ path: string;
563
+ auth: "portalToken";
564
+ description: string;
565
+ };
566
+ 'portal.notes.project': {
567
+ method: "GET";
568
+ path: string;
569
+ auth: "portalToken";
570
+ description: string;
571
+ };
572
+ 'portal.notes.create': {
573
+ method: "POST";
574
+ path: string;
575
+ auth: "portalToken";
576
+ description: string;
577
+ body: string[];
578
+ };
579
+ 'portal.notes.comments': {
580
+ method: "GET";
581
+ path: string;
582
+ auth: "portalToken";
583
+ description: string;
584
+ };
585
+ 'portal.notes.reply': {
586
+ method: "POST";
587
+ path: string;
588
+ auth: "portalToken";
589
+ description: string;
590
+ body: string[];
591
+ };
592
+ };
525
593
  metrics: {
526
594
  'metrics.push': {
527
595
  method: "POST";
@@ -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 { noteCommands } from './notes.js';
6
7
  import { portalCommands } from './portal.js';
7
8
  import { projectCommands } from './projects.js';
8
9
  import { systemCommands } from './system.js';
@@ -18,6 +19,7 @@ export const COMMAND_GROUPS = {
18
19
  usage: usageCommands,
19
20
  updates: updateCommands,
20
21
  onboarding: onboardingCommands,
22
+ notes: noteCommands,
21
23
  metrics: metricCommands,
22
24
  docs: docsCommands,
23
25
  portal: portalCommands,
@@ -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,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,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,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,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,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,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,68 @@
1
+ export declare const noteCommands: {
2
+ 'notes.list': {
3
+ method: "GET";
4
+ path: string;
5
+ auth: "apiKey";
6
+ description: string;
7
+ queryParams: string[];
8
+ };
9
+ 'notes.create': {
10
+ method: "POST";
11
+ path: string;
12
+ auth: "apiKey";
13
+ description: string;
14
+ body: string[];
15
+ };
16
+ 'notes.update': {
17
+ method: "PATCH";
18
+ path: string;
19
+ auth: "apiKey";
20
+ description: string;
21
+ body: string[];
22
+ };
23
+ 'notes.comments': {
24
+ method: "GET";
25
+ path: string;
26
+ auth: "apiKey";
27
+ description: string;
28
+ };
29
+ 'notes.reply': {
30
+ method: "POST";
31
+ path: string;
32
+ auth: "apiKey";
33
+ description: string;
34
+ body: string[];
35
+ };
36
+ 'portal.notes': {
37
+ method: "GET";
38
+ path: string;
39
+ auth: "portalToken";
40
+ description: string;
41
+ };
42
+ 'portal.notes.project': {
43
+ method: "GET";
44
+ path: string;
45
+ auth: "portalToken";
46
+ description: string;
47
+ };
48
+ 'portal.notes.create': {
49
+ method: "POST";
50
+ path: string;
51
+ auth: "portalToken";
52
+ description: string;
53
+ body: string[];
54
+ };
55
+ 'portal.notes.comments': {
56
+ method: "GET";
57
+ path: string;
58
+ auth: "portalToken";
59
+ description: string;
60
+ };
61
+ 'portal.notes.reply': {
62
+ method: "POST";
63
+ path: string;
64
+ auth: "portalToken";
65
+ description: string;
66
+ body: string[];
67
+ };
68
+ };
@@ -0,0 +1,13 @@
1
+ export const noteCommands = {
2
+ 'notes.list': { method: 'GET', path: '/api/admin/notes', auth: 'apiKey', description: 'List client notes', queryParams: ['clientId', 'projectId', 'status'] },
3
+ 'notes.create': { method: 'POST', path: '/api/admin/notes', auth: 'apiKey', description: 'Create client note (--clientId --title --content/--content-file/--stdin --projectIds)', body: ['clientId', 'title', 'content', 'projectIds'] },
4
+ 'notes.update': { method: 'PATCH', path: '/api/admin/notes/:noteId', auth: 'apiKey', description: 'Update client note', body: ['title', 'content', 'status', 'projectIds'] },
5
+ 'notes.comments': { method: 'GET', path: '/api/admin/notes/:noteId/comments', auth: 'apiKey', description: 'List note comments' },
6
+ 'notes.reply': { method: 'POST', path: '/api/admin/notes/:noteId/comments', auth: 'apiKey', description: 'Reply to client note (--content, --content-file, or --stdin)', body: ['content'] },
7
+ 'portal.notes': { method: 'GET', path: '/api/portal/notes', auth: 'portalToken', description: 'List portal notes' },
8
+ 'portal.notes.project': { method: 'GET', path: '/api/portal/projects/:id/notes', auth: 'portalToken', description: 'List portal notes for a project' },
9
+ 'portal.notes.create': { method: 'POST', path: '/api/portal/notes', auth: 'portalToken', description: 'Create portal note (--title --content/--content-file/--stdin --projectIds)', body: ['title', 'content', 'projectIds'] },
10
+ 'portal.notes.comments': { method: 'GET', path: '/api/portal/notes/:noteId/comments', auth: 'portalToken', description: 'List portal note comments' },
11
+ 'portal.notes.reply': { method: 'POST', path: '/api/portal/notes/:noteId/comments', auth: 'portalToken', description: 'Reply to portal note (--content, --content-file, or --stdin)', body: ['content'] },
12
+ };
13
+ //# sourceMappingURL=notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.js","sourceRoot":"","sources":["../../commands/notes.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,YAAY,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE;IAC7J,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uFAAuF,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE;IACxO,cAAc,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,0BAA0B,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE;IAC5K,gBAAgB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,mCAAmC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACjI,aAAa,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mCAAmC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8DAA8D,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE;IAC5L,cAAc,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACnH,sBAAsB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,gCAAgC,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,iCAAiC,EAAE;IACtJ,qBAAqB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,4EAA4E,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE;IAC9N,uBAAuB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,oCAAoC,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,2BAA2B,EAAE;IACrJ,oBAAoB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oCAAoC,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,8DAA8D,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE;CACnL,CAAC"}
@@ -275,10 +275,24 @@ function normalizeCliMultiline(value) {
275
275
  : value;
276
276
  }
277
277
 
278
+ function normalizeCsvList(value) {
279
+ if (Array.isArray(value)) return value;
280
+ if (typeof value !== 'string') return value;
281
+ return value
282
+ .split(',')
283
+ .map((item) => item.trim())
284
+ .filter(Boolean);
285
+ }
286
+
278
287
  function supportsCommentContentInput(commandKey) {
279
288
  return commandKey === 'comments.create' ||
280
289
  commandKey === 'comments.update' ||
281
- commandKey === 'portal.comments.create';
290
+ commandKey === 'portal.comments.create' ||
291
+ commandKey === 'notes.create' ||
292
+ commandKey === 'notes.update' ||
293
+ commandKey === 'notes.reply' ||
294
+ commandKey === 'portal.notes.create' ||
295
+ commandKey === 'portal.notes.reply';
282
296
  }
283
297
 
284
298
  function interpolatePath(template, id) {
@@ -499,6 +513,33 @@ function formatOutput(data, commandKey) {
499
513
  return;
500
514
  }
501
515
 
516
+ if (commandKey === 'notes.list' || commandKey === 'portal.notes' || commandKey === 'portal.notes.project') {
517
+ const items = data.data || [];
518
+ if (items.length === 0) { console.log('No notes found.'); return; }
519
+ console.log(`\nClient Notes (${items.length}):`);
520
+ console.log('-'.repeat(80));
521
+ for (const note of items) {
522
+ const projects = note.projects?.length
523
+ ? ` -> ${note.projects.map((project) => project.name).join(', ')}`
524
+ : '';
525
+ console.log(` ${note.title} [${note.status}]${projects} [${note.id}]`);
526
+ if (note.commentCount) console.log(` Discussion: ${note.commentCount} repl${note.commentCount === 1 ? 'y' : 'ies'}`);
527
+ }
528
+ return;
529
+ }
530
+
531
+ if (commandKey === 'notes.comments' || commandKey === 'portal.notes.comments') {
532
+ const items = data.data || [];
533
+ if (items.length === 0) { console.log('No note replies yet.'); return; }
534
+ console.log(`\nNote Discussion (${items.length}):`);
535
+ for (const comment of items) {
536
+ const badge = comment.authorType === 'admin' ? '[klevar]' : '[client]';
537
+ console.log(` ${comment.authorName} ${badge} - ${timeAgo(comment.createdAt)}`);
538
+ console.log(` ${comment.content}`);
539
+ }
540
+ return;
541
+ }
542
+
502
543
  if (
503
544
  commandKey === 'clients.docs-invoices' ||
504
545
  commandKey === 'projects.docs-invoices'
@@ -593,6 +634,12 @@ function formatOutput(data, commandKey) {
593
634
  return;
594
635
  }
595
636
 
637
+ if ((commandKey === 'notes.reply' || commandKey === 'portal.notes.reply') && data.comment) {
638
+ const c = data.comment;
639
+ console.log(`Note reply posted by ${c.authorName} [${c.authorType}] [${c.id}]`);
640
+ return;
641
+ }
642
+
596
643
  // Onboarding create/process/reject
597
644
  if (commandKey === 'onboarding.create' && data.id) {
598
645
  console.log(`Submission created: ${data.id} [${data.status}]`);
@@ -646,6 +693,13 @@ function formatOutput(data, commandKey) {
646
693
  console.log(`${action}${vis}: ${u.content.slice(0, 80)}${u.content.length > 80 ? '...' : ''} [${u.id}]${edited}`);
647
694
  return;
648
695
  }
696
+ if (data.note) {
697
+ const n = data.note;
698
+ const projects = n.projects?.length ? ` (${n.projects.map((project) => project.name).join(', ')})` : '';
699
+ const action = commandKey === 'notes.update' ? 'Note updated' : 'Note created';
700
+ console.log(`${action}: ${n.title} [${n.status}]${projects} [${n.id}]`);
701
+ return;
702
+ }
649
703
  if (data.portalToken) {
650
704
  console.log(`New token: ${data.portalToken}`);
651
705
  return;
@@ -696,7 +750,9 @@ if (!resource || resource === 'help' || resource === '--help') {
696
750
  console.log('Commands:');
697
751
  const grouped = {};
698
752
  for (const [key, cmd] of Object.entries(COMMANDS)) {
699
- const [res, act] = key.includes('.') ? key.split('.') : [key, ''];
753
+ const parts = key.split('.');
754
+ const res = parts[0];
755
+ const act = parts.length > 1 ? parts.slice(1).join('.') : '';
700
756
  if (!grouped[res]) grouped[res] = [];
701
757
  grouped[res].push({ key, action: act, desc: (cmd.description || cmd.desc), body: cmd.body, queryParams: cmd.queryParams });
702
758
  }
@@ -804,6 +860,7 @@ if (cmd.fixedBody) {
804
860
  if (typeof val === 'string' && (val.startsWith('{') || val.startsWith('['))) {
805
861
  try { val = JSON.parse(val); } catch { /* keep as string */ }
806
862
  }
863
+ if (key === 'projectIds') val = normalizeCsvList(val);
807
864
  if (val === 'true') val = true;
808
865
  if (val === 'false') val = false;
809
866
  body[key] = val;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klevar/portal-cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "First-class npm CLI for the Klevar Client Management Portal",
5
5
  "type": "module",
6
6
  "bin": {