@qodalis/cli-scp 2.0.0-beta.9 → 2.0.0-rc.2

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/public-api.mjs CHANGED
@@ -1,15 +1,11 @@
1
- import { DefaultLibraryAuthor, ICliFileTransferService_TOKEN } from '@qodalis/cli-core';
1
+ import { createCliToken, DefaultLibraryAuthor, resolveServerHeaders, ICliFileTransferService_TOKEN, resolveHeaders, splitShellWords } from '@qodalis/cli-core';
2
2
 
3
3
  // src/lib/processors/cli-scp-command-processor.ts
4
4
 
5
5
  // src/lib/version.ts
6
- var LIBRARY_VERSION = "2.0.0-beta.9";
6
+ var LIBRARY_VERSION = "2.0.0-rc.2";
7
7
  var API_VERSION = 2;
8
-
9
- // src/lib/interfaces/index.ts
10
- var IScpTransferService_TOKEN = "scp-transfer-service";
11
-
12
- // src/lib/processors/scp-utils.ts
8
+ var IScpTransferService_TOKEN = createCliToken("scp-transfer-service");
13
9
  function resolveServer(serverName, context) {
14
10
  const servers = context.options?.servers;
15
11
  if (!servers || servers.length === 0) {
@@ -47,10 +43,10 @@ function parseServerAndPath(value, context, args) {
47
43
  if (serverArg && pathArg) {
48
44
  return [String(serverArg), String(pathArg)];
49
45
  }
50
- if (!value?.trim()) {
46
+ const parts = splitShellWords(value ?? "");
47
+ if (parts.length === 0) {
51
48
  return null;
52
49
  }
53
- const parts = value.trim().split(/\s+/);
54
50
  if (parts.length < 2) {
55
51
  if (serverArg && parts.length >= 1) {
56
52
  return [String(serverArg), parts.join(" ")];
@@ -62,8 +58,6 @@ function parseServerAndPath(value, context, args) {
62
58
  }
63
59
  return [parts[0], parts.slice(1).join(" ")];
64
60
  }
65
-
66
- // src/lib/processors/cli-scp-ls-processor.ts
67
61
  var CliScpLsProcessor = class {
68
62
  constructor() {
69
63
  this.command = "ls";
@@ -94,7 +88,7 @@ var CliScpLsProcessor = class {
94
88
  const [serverName, path] = parsed;
95
89
  const server = resolveServer(serverName, context);
96
90
  if (!server) return;
97
- const transferService = context.services.get(
91
+ const transferService = context.services.getRequired(
98
92
  IScpTransferService_TOKEN
99
93
  );
100
94
  if (!transferService) {
@@ -106,7 +100,7 @@ var CliScpLsProcessor = class {
106
100
  const entries = await transferService.ls(
107
101
  serverUrl(server),
108
102
  path,
109
- server.headers
103
+ resolveServerHeaders(context.services, server.name, server.headers)
110
104
  );
111
105
  context.spinner?.hide();
112
106
  if (entries.length === 0) {
@@ -131,8 +125,6 @@ var CliScpLsProcessor = class {
131
125
  context.writer.writeln("List files and directories on a remote server.");
132
126
  }
133
127
  };
134
-
135
- // src/lib/processors/cli-scp-cat-processor.ts
136
128
  var CliScpCatProcessor = class {
137
129
  constructor() {
138
130
  this.command = "cat";
@@ -163,7 +155,7 @@ var CliScpCatProcessor = class {
163
155
  const [serverName, path] = parsed;
164
156
  const server = resolveServer(serverName, context);
165
157
  if (!server) return;
166
- const transferService = context.services.get(
158
+ const transferService = context.services.getRequired(
167
159
  IScpTransferService_TOKEN
168
160
  );
169
161
  if (!transferService) {
@@ -175,7 +167,7 @@ var CliScpCatProcessor = class {
175
167
  const content = await transferService.cat(
176
168
  serverUrl(server),
177
169
  path,
178
- server.headers
170
+ resolveServerHeaders(context.services, server.name, server.headers)
179
171
  );
180
172
  context.spinner?.hide();
181
173
  context.writer.writeln(content);
@@ -233,12 +225,11 @@ var CliScpDownloadProcessor = class {
233
225
  remotePath = String(args["remote-path"] || args["path"]);
234
226
  localPath = args["local-path"] ? String(args["local-path"]) : void 0;
235
227
  } else {
236
- const value = command.value?.trim();
237
- if (!value) {
228
+ const parts = command.positionals ?? [];
229
+ if (parts.length === 0) {
238
230
  context.writer.writeError("Usage: scp download <server> <remote-path> [local-path]");
239
231
  return;
240
232
  }
241
- const parts = value.split(/\s+/);
242
233
  if (parts.length < 2) {
243
234
  context.writer.writeError("Usage: scp download <server> <remote-path> [local-path]");
244
235
  return;
@@ -254,7 +245,7 @@ var CliScpDownloadProcessor = class {
254
245
  localPath = localPath || remotePath.split("/").pop() || "download";
255
246
  const server = resolveServer(serverName, context);
256
247
  if (!server) return;
257
- const transferService = context.services.get(
248
+ const transferService = context.services.getRequired(
258
249
  IScpTransferService_TOKEN
259
250
  );
260
251
  if (!transferService) {
@@ -263,6 +254,7 @@ var CliScpDownloadProcessor = class {
263
254
  }
264
255
  const useBrowserDownload = command.args?.["save"] === true;
265
256
  context.spinner?.show(`Downloading ${remotePath} from ${server.name}...`);
257
+ context.notifier.info(`scp: downloading from ${server.name}`);
266
258
  const abortController = new AbortController();
267
259
  const abortSub = context.onAbort.subscribe(() => abortController.abort());
268
260
  try {
@@ -270,7 +262,7 @@ var CliScpDownloadProcessor = class {
270
262
  const { content, size } = await transferService.download(
271
263
  serverUrl(server),
272
264
  remotePath,
273
- server.headers,
265
+ resolveServerHeaders(context.services, server.name, server.headers),
274
266
  (received, total) => {
275
267
  const now = Date.now();
276
268
  if (now - lastProgressUpdate < 200) return;
@@ -280,10 +272,12 @@ var CliScpDownloadProcessor = class {
280
272
  context.spinner?.show(
281
273
  `Downloading ${remotePath}... ${pct}% (${formatBytes(received)}/${formatBytes(total)})`
282
274
  );
275
+ context.notifier.info(`scp: download ${pct}%`);
283
276
  } else {
284
277
  context.spinner?.show(
285
278
  `Downloading ${remotePath}... ${formatBytes(received)}`
286
279
  );
280
+ context.notifier.info(`scp: download ${formatBytes(received)}`);
287
281
  }
288
282
  },
289
283
  abortController.signal
@@ -372,14 +366,13 @@ var CliScpUploadProcessor = class {
372
366
  localPath = args["local-path"] ? String(args["local-path"]) : void 0;
373
367
  remotePath = String(args["remote-path"]);
374
368
  } else {
375
- const value = command.value?.trim();
376
- if (!value) {
369
+ const parts = command.positionals ?? [];
370
+ if (parts.length === 0) {
377
371
  context.writer.writeError(
378
372
  useLocalPicker ? "Usage: scp upload --local <server> <remote-path>" : "Usage: scp upload <server> <local-path> <remote-path>"
379
373
  );
380
374
  return;
381
375
  }
382
- const parts = value.split(/\s+/);
383
376
  if (useLocalPicker) {
384
377
  if (parts.length < 2) {
385
378
  context.writer.writeError("Usage: scp upload --local <server> <remote-path>");
@@ -412,7 +405,7 @@ var CliScpUploadProcessor = class {
412
405
  context.writer.writeError("File transfer service not available.");
413
406
  return;
414
407
  }
415
- const transferService = context.services.get(
408
+ const transferService = context.services.getRequired(
416
409
  IScpTransferService_TOKEN
417
410
  );
418
411
  if (!transferService) {
@@ -442,13 +435,14 @@ var CliScpUploadProcessor = class {
442
435
  context.spinner?.show(
443
436
  `Uploading "${filename}" (${formatBytes(content.length)}) to ${server.name}:${remotePath}...`
444
437
  );
438
+ context.notifier.info(`scp: uploading to ${server.name}`);
445
439
  try {
446
440
  await transferService.upload(
447
441
  serverUrl(server),
448
442
  remotePath,
449
443
  content,
450
444
  filename,
451
- server.headers
445
+ resolveServerHeaders(context.services, server.name, server.headers)
452
446
  );
453
447
  context.spinner?.hide();
454
448
  context.writer.writeSuccess(
@@ -467,8 +461,6 @@ var CliScpUploadProcessor = class {
467
461
  context.writer.writeln(" scp upload --local <server> <remote-path> Pick from your machine via browser");
468
462
  }
469
463
  };
470
-
471
- // src/lib/processors/cli-scp-rm-processor.ts
472
464
  var CliScpRmProcessor = class {
473
465
  constructor() {
474
466
  this.command = "rm";
@@ -499,7 +491,7 @@ var CliScpRmProcessor = class {
499
491
  const [serverName, path] = parsed;
500
492
  const server = resolveServer(serverName, context);
501
493
  if (!server) return;
502
- const transferService = context.services.get(
494
+ const transferService = context.services.getRequired(
503
495
  IScpTransferService_TOKEN
504
496
  );
505
497
  if (!transferService) {
@@ -511,7 +503,7 @@ var CliScpRmProcessor = class {
511
503
  await transferService.rm(
512
504
  serverUrl(server),
513
505
  path,
514
- server.headers
506
+ resolveServerHeaders(context.services, server.name, server.headers)
515
507
  );
516
508
  context.spinner?.hide();
517
509
  context.writer.writeSuccess(`Deleted: ${server.name}:${path}`);
@@ -524,8 +516,6 @@ var CliScpRmProcessor = class {
524
516
  context.writer.writeln("Delete a file on a remote server.");
525
517
  }
526
518
  };
527
-
528
- // src/lib/processors/cli-scp-mkdir-processor.ts
529
519
  var CliScpMkdirProcessor = class {
530
520
  constructor() {
531
521
  this.command = "mkdir";
@@ -556,7 +546,7 @@ var CliScpMkdirProcessor = class {
556
546
  const [serverName, path] = parsed;
557
547
  const server = resolveServer(serverName, context);
558
548
  if (!server) return;
559
- const transferService = context.services.get(
549
+ const transferService = context.services.getRequired(
560
550
  IScpTransferService_TOKEN
561
551
  );
562
552
  if (!transferService) {
@@ -568,7 +558,7 @@ var CliScpMkdirProcessor = class {
568
558
  await transferService.mkdir(
569
559
  serverUrl(server),
570
560
  path,
571
- server.headers
561
+ resolveServerHeaders(context.services, server.name, server.headers)
572
562
  );
573
563
  context.spinner?.hide();
574
564
  context.writer.writeSuccess(`Created directory: ${server.name}:${path}`);
@@ -581,8 +571,6 @@ var CliScpMkdirProcessor = class {
581
571
  context.writer.writeln("Create a directory on a remote server.");
582
572
  }
583
573
  };
584
-
585
- // src/lib/processors/cli-scp-stat-processor.ts
586
574
  var CliScpStatProcessor = class {
587
575
  constructor() {
588
576
  this.command = "stat";
@@ -613,7 +601,7 @@ var CliScpStatProcessor = class {
613
601
  const [serverName, path] = parsed;
614
602
  const server = resolveServer(serverName, context);
615
603
  if (!server) return;
616
- const transferService = context.services.get(
604
+ const transferService = context.services.getRequired(
617
605
  IScpTransferService_TOKEN
618
606
  );
619
607
  if (!transferService) {
@@ -625,7 +613,7 @@ var CliScpStatProcessor = class {
625
613
  const stat = await transferService.stat(
626
614
  serverUrl(server),
627
615
  path,
628
- server.headers
616
+ resolveServerHeaders(context.services, server.name, server.headers)
629
617
  );
630
618
  context.spinner?.hide();
631
619
  const entries = {
@@ -669,33 +657,25 @@ var CliScpCommandCommandProcessor = class {
669
657
  ];
670
658
  }
671
659
  async processCommand(command, context) {
672
- const value = command.value?.trim();
673
- if (value && value.includes(":") && !value.startsWith("-")) {
674
- const colonIdx = value.indexOf(":");
675
- const spaceBeforeColon = value.lastIndexOf(" ", colonIdx);
676
- if (spaceBeforeColon === -1) {
677
- const serverName = value.substring(0, colonIdx);
678
- const rest = value.substring(colonIdx + 1);
679
- const parts2 = rest.trim().split(/\s+/);
680
- const remotePath = parts2[0];
681
- const localPath = parts2[1] || remotePath.split("/").pop() || "download";
660
+ const [first = "", second] = command.positionals ?? [];
661
+ if (first && !first.startsWith("-")) {
662
+ const colonIdx = first.indexOf(":");
663
+ if (colonIdx !== -1) {
664
+ const serverName = first.substring(0, colonIdx);
665
+ const remotePath = first.substring(colonIdx + 1);
666
+ const localPath = second || remotePath.split("/").pop() || "download";
682
667
  if (remotePath) {
683
668
  await this.doDownload(serverName, remotePath, localPath, command, context);
684
669
  return;
685
670
  }
686
671
  }
687
- const parts = value.split(/\s+/);
688
- if (parts.length >= 2) {
689
- const secondPart = parts[1];
690
- if (secondPart.includes(":")) {
691
- const localPath = parts[0];
692
- const srvColonIdx = secondPart.indexOf(":");
693
- const serverName = secondPart.substring(0, srvColonIdx);
694
- const remotePath = secondPart.substring(srvColonIdx + 1);
695
- if (remotePath) {
696
- await this.doUpload(serverName, localPath, remotePath, context);
697
- return;
698
- }
672
+ if (second?.includes(":")) {
673
+ const srvColonIdx = second.indexOf(":");
674
+ const serverName = second.substring(0, srvColonIdx);
675
+ const remotePath = second.substring(srvColonIdx + 1);
676
+ if (remotePath) {
677
+ await this.doUpload(serverName, first, remotePath, context);
678
+ return;
699
679
  }
700
680
  }
701
681
  }
@@ -707,7 +687,7 @@ var CliScpCommandCommandProcessor = class {
707
687
  async doDownload(serverName, remotePath, localPath, command, context) {
708
688
  const server = resolveServer(serverName, context);
709
689
  if (!server) return;
710
- const transferService = context.services.get(
690
+ const transferService = context.services.getRequired(
711
691
  IScpTransferService_TOKEN
712
692
  );
713
693
  if (!transferService) {
@@ -716,12 +696,13 @@ var CliScpCommandCommandProcessor = class {
716
696
  }
717
697
  const useBrowserDownload = command.args?.["save"] === true;
718
698
  context.spinner?.show(`Downloading ${remotePath} from ${server.name}...`);
699
+ context.notifier.info(`scp: downloading from ${server.name}`);
719
700
  try {
720
701
  let lastProgressUpdate = 0;
721
702
  const { content, size } = await transferService.download(
722
703
  serverUrl(server),
723
704
  remotePath,
724
- server.headers,
705
+ resolveServerHeaders(context.services, server.name, server.headers),
725
706
  (received, total) => {
726
707
  const now = Date.now();
727
708
  if (now - lastProgressUpdate < 200) return;
@@ -731,10 +712,12 @@ var CliScpCommandCommandProcessor = class {
731
712
  context.spinner?.show(
732
713
  `Downloading ${remotePath}... ${pct}% (${formatBytes(received)}/${formatBytes(total)})`
733
714
  );
715
+ context.notifier.info(`scp: download ${pct}%`);
734
716
  } else {
735
717
  context.spinner?.show(
736
718
  `Downloading ${remotePath}... ${formatBytes(received)}`
737
719
  );
720
+ context.notifier.info(`scp: download ${formatBytes(received)}`);
738
721
  }
739
722
  }
740
723
  );
@@ -772,7 +755,7 @@ var CliScpCommandCommandProcessor = class {
772
755
  context.writer.writeError("File transfer service not available.");
773
756
  return;
774
757
  }
775
- const transferService = context.services.get(
758
+ const transferService = context.services.getRequired(
776
759
  IScpTransferService_TOKEN
777
760
  );
778
761
  if (!transferService) {
@@ -780,6 +763,7 @@ var CliScpCommandCommandProcessor = class {
780
763
  return;
781
764
  }
782
765
  context.spinner?.show(`Reading local file "${localPath}"...`);
766
+ context.notifier.info(`scp: reading ${localPath}`);
783
767
  try {
784
768
  const content = await fileService.readFile(localPath);
785
769
  if (content === null) {
@@ -791,12 +775,13 @@ var CliScpCommandCommandProcessor = class {
791
775
  context.spinner?.show(
792
776
  `Uploading "${filename}" (${formatBytes(content.length)}) to ${server.name}:${remotePath}...`
793
777
  );
778
+ context.notifier.info(`scp: uploading to ${server.name}`);
794
779
  await transferService.upload(
795
780
  serverUrl(server),
796
781
  remotePath,
797
782
  content,
798
783
  filename,
799
- server.headers
784
+ resolveServerHeaders(context.services, server.name, server.headers)
800
785
  );
801
786
  context.spinner?.hide();
802
787
  context.writer.writeSuccess(
@@ -808,32 +793,30 @@ var CliScpCommandCommandProcessor = class {
808
793
  }
809
794
  }
810
795
  };
811
-
812
- // src/lib/services/scp-transfer.service.ts
813
796
  var ScpTransferService = class {
814
797
  async ls(serverUrl2, path, headers) {
815
798
  const res = await this._fetch(`${serverUrl2}/api/qcli/fs/ls?path=${encodeURIComponent(path)}`, {
816
- headers
799
+ headers: resolveHeaders(headers)
817
800
  });
818
801
  const data = await res.json();
819
802
  return data.entries;
820
803
  }
821
804
  async cat(serverUrl2, path, headers) {
822
805
  const res = await this._fetch(`${serverUrl2}/api/qcli/fs/cat?path=${encodeURIComponent(path)}`, {
823
- headers
806
+ headers: resolveHeaders(headers)
824
807
  });
825
808
  const data = await res.json();
826
809
  return data.content;
827
810
  }
828
811
  async stat(serverUrl2, path, headers) {
829
812
  const res = await this._fetch(`${serverUrl2}/api/qcli/fs/stat?path=${encodeURIComponent(path)}`, {
830
- headers
813
+ headers: resolveHeaders(headers)
831
814
  });
832
815
  return await res.json();
833
816
  }
834
817
  async download(serverUrl2, path, headers, onProgress, signal) {
835
818
  const res = await this._fetch(`${serverUrl2}/api/qcli/fs/download?path=${encodeURIComponent(path)}`, {
836
- headers,
819
+ headers: resolveHeaders(headers),
837
820
  signal
838
821
  });
839
822
  const contentLength = parseInt(res.headers.get("content-length") || "0", 10);
@@ -869,20 +852,20 @@ var ScpTransferService = class {
869
852
  await this._fetch(`${serverUrl2}/api/qcli/fs/upload`, {
870
853
  method: "POST",
871
854
  body: formData,
872
- headers
855
+ headers: resolveHeaders(headers)
873
856
  });
874
857
  }
875
858
  async mkdir(serverUrl2, path, headers) {
876
859
  await this._fetch(`${serverUrl2}/api/qcli/fs/mkdir`, {
877
860
  method: "POST",
878
- headers: { "Content-Type": "application/json", ...headers },
861
+ headers: { "Content-Type": "application/json", ...resolveHeaders(headers) },
879
862
  body: JSON.stringify({ path })
880
863
  });
881
864
  }
882
865
  async rm(serverUrl2, path, headers) {
883
866
  await this._fetch(`${serverUrl2}/api/qcli/fs/rm?path=${encodeURIComponent(path)}`, {
884
867
  method: "DELETE",
885
- headers
868
+ headers: resolveHeaders(headers)
886
869
  });
887
870
  }
888
871
  async _fetch(url, init) {