@klevar/portal-cli 0.1.3 → 0.1.4

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.
@@ -1,5 +1,22 @@
1
1
  #!/usr/bin/env node
2
- // @ts-expect-error legacy JavaScript execution engine is copied during post-build.
3
- await import('../lib/legacy-runner.js');
2
+ try {
3
+ // @ts-expect-error legacy JavaScript execution engine is copied during post-build.
4
+ await import('../lib/legacy-runner.js');
5
+ }
6
+ catch (err) {
7
+ if (err &&
8
+ typeof err === 'object' &&
9
+ 'message' in err &&
10
+ typeof err.message === 'string' &&
11
+ err.message.startsWith('CLI_EXIT_')) {
12
+ process.exitCode =
13
+ 'code' in err && typeof err.code === 'number'
14
+ ? err.code
15
+ : Number(err.message.replace('CLI_EXIT_', '')) || 1;
16
+ }
17
+ else {
18
+ throw err;
19
+ }
20
+ }
4
21
  export {};
5
22
  //# sourceMappingURL=klevar-portal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"klevar-portal.js","sourceRoot":"","sources":["../../bin/klevar-portal.ts"],"names":[],"mappings":";AAEA,mFAAmF;AACnF,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"klevar-portal.js","sourceRoot":"","sources":["../../bin/klevar-portal.ts"],"names":[],"mappings":";AAEA,IAAI,CAAC;IACH,mFAAmF;IACnF,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAC1C,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,IACE,GAAG;QACH,OAAO,GAAG,KAAK,QAAQ;QACvB,SAAS,IAAI,GAAG;QAChB,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;QAC/B,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,EACnC,CAAC;QACD,OAAO,CAAC,QAAQ;YACd,MAAM,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAC3C,CAAC,CAAC,GAAG,CAAC,IAAI;gBACV,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -25,6 +25,18 @@ import { join } from 'node:path';
25
25
  import { homedir } from 'node:os';
26
26
  import { COMMANDS } from '../commands/index.js';
27
27
 
28
+ class CliExit extends Error {
29
+ constructor(code = 1) {
30
+ super(`CLI_EXIT_${code}`);
31
+ this.code = code;
32
+ }
33
+ }
34
+
35
+ function exitGracefully(code = 1) {
36
+ process.exitCode = code;
37
+ throw new CliExit(code);
38
+ }
39
+
28
40
  // ── Credential Loading ──────────────────────────────────────────
29
41
 
30
42
  function loadEnvFile(filePath) {
@@ -58,7 +70,7 @@ function requireApiKey() {
58
70
  if (!API_KEY) {
59
71
  console.error('Error: PORTAL_API_KEY is required');
60
72
  console.error('Set it via env var or create ~/.klevar/portal.env');
61
- process.exit(2);
73
+ exitGracefully(2);
62
74
  }
63
75
  }
64
76
 
@@ -66,7 +78,7 @@ function requirePortalToken() {
66
78
  if (!PORTAL_TOKEN) {
67
79
  console.error('Error: PORTAL_TOKEN is required for portal commands');
68
80
  console.error('Set it via env var, --portal-token, or ~/.klevar/portal.env');
69
- process.exit(2);
81
+ exitGracefully(2);
70
82
  }
71
83
  }
72
84
 
@@ -113,7 +125,7 @@ async function api(method, path, body, auth = 'apiKey') {
113
125
  if (!res.ok) {
114
126
  const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
115
127
  printApiError(res.status, err);
116
- process.exit(1);
128
+ exitGracefully(1);
117
129
  }
118
130
 
119
131
  if (res.status === 204) return null;
@@ -137,7 +149,7 @@ async function apiDownload(path, outputPath, auth = 'apiKey') {
137
149
  if (!res.ok) {
138
150
  const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
139
151
  printApiError(res.status, err);
140
- process.exit(1);
152
+ exitGracefully(1);
141
153
  }
142
154
 
143
155
  const contentType = res.headers.get('content-type') || '';
@@ -158,7 +170,7 @@ async function apiDownload(path, outputPath, auth = 'apiKey') {
158
170
 
159
171
  if (!outputPath) {
160
172
  console.error('Error: --output is required when the API returns PDF bytes');
161
- process.exit(1);
173
+ exitGracefully(1);
162
174
  }
163
175
 
164
176
  const bytes = Buffer.from(await res.arrayBuffer());
@@ -170,7 +182,7 @@ async function downloadPublicUrl(url, outputPath) {
170
182
  const res = await fetch(url);
171
183
  if (!res.ok) {
172
184
  console.error(`Error ${res.status}: failed to download signed URL`);
173
- process.exit(1);
185
+ exitGracefully(1);
174
186
  }
175
187
  const bytes = Buffer.from(await res.arrayBuffer());
176
188
  writeFileSync(outputPath, bytes);
@@ -209,7 +221,7 @@ async function apiMultipart(method, path, fields, filePaths) {
209
221
  if (!res.ok) {
210
222
  const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
211
223
  printApiError(res.status, err);
212
- process.exit(1);
224
+ exitGracefully(1);
213
225
  }
214
226
  if (res.status === 204) return null;
215
227
  return res.json();
@@ -613,7 +625,7 @@ if (!resource || resource === 'help' || resource === '--help') {
613
625
  }
614
626
  console.log(`\nConfig: PORTAL_API_URL, PORTAL_API_KEY, PORTAL_TOKEN (or ~/.klevar/portal.env)`);
615
627
  console.log(`Target: ${BASE_URL}`);
616
- process.exit(0);
628
+ exitGracefully(0);
617
629
  }
618
630
 
619
631
  // Resolve command key — handle single-word commands (e.g. "search <term>")
@@ -637,7 +649,7 @@ if (!cmd && COMMANDS[resource]) {
637
649
  if (!cmd) {
638
650
  console.error(`Unknown command: ${commandKey}`);
639
651
  console.error(`Run 'klevar-portal help' for available commands.`);
640
- process.exit(1);
652
+ exitGracefully(1);
641
653
  }
642
654
 
643
655
  // Parse remaining args
@@ -659,7 +671,7 @@ if (pathParams.length > 0) {
659
671
  if (!value) {
660
672
  const ordinal = index === 0 ? 'an ID argument' : `argument ${index + 1} for :${paramName}`;
661
673
  console.error(`Command '${commandKey}' requires ${ordinal}.`);
662
- process.exit(1);
674
+ exitGracefully(1);
663
675
  }
664
676
  path = path.replace(`:${paramName}`, value);
665
677
  });
@@ -708,14 +720,14 @@ if (commandKey === 'search') {
708
720
  const q = [id, ...positional.slice(1)].filter(Boolean).join(' ');
709
721
  if (!q) {
710
722
  console.error('Usage: search <term>');
711
- process.exit(1);
723
+ exitGracefully(1);
712
724
  }
713
725
  const data = await api('GET', `/api/admin/search?q=${encodeURIComponent(q)}`);
714
726
  const hasResults = data.projects.length + data.tasks.length + data.updates.length > 0;
715
727
 
716
728
  if (!hasResults) {
717
729
  console.log(`No results for "${q}"`);
718
- process.exit(0);
730
+ exitGracefully(0);
719
731
  }
720
732
 
721
733
  console.log(`\nSearch: "${q}"`);
@@ -750,7 +762,7 @@ if (commandKey === 'search') {
750
762
  console.log('\nUpdates: (none)');
751
763
  }
752
764
 
753
- process.exit(0);
765
+ exitGracefully(0);
754
766
  }
755
767
 
756
768
  // ── Milestone Commands (custom logic: fetch → modify → PATCH) ──
@@ -759,7 +771,7 @@ if (cmd.method === 'CUSTOM' && resource === 'milestones') {
759
771
  const projectId = id;
760
772
  if (!projectId) {
761
773
  console.error('Usage: milestones <action> <projectId> [--name "..."] or [index]');
762
- process.exit(1);
774
+ exitGracefully(1);
763
775
  }
764
776
 
765
777
  // Fetch current project
@@ -777,23 +789,23 @@ if (cmd.method === 'CUSTOM' && resource === 'milestones') {
777
789
  console.log(` ${i + 1}. ${icon} ${m.name} [${m.status}]`);
778
790
  });
779
791
  }
780
- process.exit(0);
792
+ exitGracefully(0);
781
793
  }
782
794
 
783
795
  if (action === 'add') {
784
796
  const name = flags.name || positional[1];
785
- if (!name) { console.error('Usage: milestones add <projectId> --name "Milestone name"'); process.exit(1); }
797
+ if (!name) { console.error('Usage: milestones add <projectId> --name "Milestone name"'); exitGracefully(1); }
786
798
  milestones.push({ name, status: 'pending' });
787
799
  await api('PATCH', `/api/admin/projects/${projectId}`, { milestonesJson: milestones });
788
800
  console.log(`Added milestone: ${name} (${milestones.length} total)`);
789
- process.exit(0);
801
+ exitGracefully(0);
790
802
  }
791
803
 
792
804
  if (action === 'done' || action === 'undo' || action === 'remove') {
793
805
  const idx = parseInt(positional[1] || flags.index, 10) - 1;
794
806
  if (isNaN(idx) || idx < 0 || idx >= milestones.length) {
795
807
  console.error(`Invalid index. Use 1-${milestones.length}.`);
796
- process.exit(1);
808
+ exitGracefully(1);
797
809
  }
798
810
  if (action === 'done') {
799
811
  milestones[idx].status = 'done';
@@ -808,11 +820,11 @@ if (cmd.method === 'CUSTOM' && resource === 'milestones') {
808
820
  await api('PATCH', `/api/admin/projects/${projectId}`, { milestonesJson: milestones });
809
821
  console.log(`Removed: ${removed[0].name} (${milestones.length} remaining)`);
810
822
  }
811
- process.exit(0);
823
+ exitGracefully(0);
812
824
  }
813
825
 
814
826
  console.error(`Unknown milestone action: ${action}`);
815
- process.exit(1);
827
+ exitGracefully(1);
816
828
  }
817
829
 
818
830
  // ── File Upload (multipart) ──
@@ -826,12 +838,12 @@ if (cmd.supportsFiles && flags.file) {
826
838
  if (!fields.visibility) fields.visibility = 'client';
827
839
  const data = await apiMultipart(cmd.method, path, fields, filePaths);
828
840
  formatOutput(data, commandKey);
829
- process.exit(0);
841
+ exitGracefully(0);
830
842
  }
831
843
 
832
844
  if (cmd.method === 'DOWNLOAD') {
833
845
  await apiDownload(path, flags.output, cmd.auth);
834
- process.exit(0);
846
+ exitGracefully(0);
835
847
  }
836
848
 
837
849
  // Execute
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klevar/portal-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "First-class npm CLI for the Klevar Client Management Portal",
5
5
  "type": "module",
6
6
  "bin": {