@zero-transfer/ssh 0.4.2 → 0.4.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/README.md CHANGED
@@ -10,7 +10,7 @@ npm install @zero-transfer/ssh
10
10
 
11
11
  ## Overview
12
12
 
13
- Standalone, zero-dependency SSH 2.0 stack RFC 4253 transport (curve25519-sha256 KEX, AES-CTR + HMAC-SHA2), RFC 4252 user authentication (password, keyboard-interactive, publickey for Ed25519 / RSA-SHA2-256/512), RFC 5656 ECDSA host keys (P-256/384/521), RFC 4254 channels, OpenSSH `known_hosts` parsing, and host-key pinning. The same protocol stack that powers the SFTP provider, exposed for callers that need direct SSH features (custom subsystems, exec channels, port forwarding, custom RPC) capabilities the Node.js ecosystem otherwise lacks a maintained pure-JS solution for.
13
+ Standalone, zero-dependency SSH 2.0 stack - RFC 4253 transport (curve25519-sha256 KEX, AES-CTR + HMAC-SHA2), RFC 4252 user authentication (password, keyboard-interactive, publickey for Ed25519 / RSA-SHA2-256/512), RFC 5656 ECDSA host keys (P-256/384/521), RFC 4254 channels, OpenSSH `known_hosts` parsing, and host-key pinning. The same protocol stack that powers the SFTP provider, exposed for callers that need direct SSH features (custom subsystems, exec channels, port forwarding, custom RPC) - capabilities the Node.js ecosystem otherwise lacks a maintained pure-JS solution for.
14
14
 
15
15
  ## Usage
16
16
 
@@ -24,7 +24,7 @@ import {
24
24
 
25
25
  ## Public surface
26
26
 
27
- This package publishes a narrowed surface of **23** exports. These symbols are also available from [`@zero-transfer/sdk`](https://www.npmjs.com/package/@zero-transfer/sdk); the table below links into the full API reference:
27
+ This package publishes a narrowed surface of **26** exports. These symbols are also available from [`@zero-transfer/sdk`](https://www.npmjs.com/package/@zero-transfer/sdk); the table below links into the full API reference:
28
28
 
29
29
  | Symbol | Kind | Notes |
30
30
  | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------------------ |
@@ -51,6 +51,9 @@ This package publishes a narrowed surface of **23** exports. These symbols are a
51
51
  | [`matchKnownHostsEntry`](https://github.com/tonywied17/zero-transfer/blob/main/docs/api-md/functions/matchKnownHostsEntry.md) | Function | See API reference. |
52
52
  | [`KnownHostsEntry`](https://github.com/tonywied17/zero-transfer/blob/main/docs/api-md/interfaces/KnownHostsEntry.md) | Interface | See API reference. |
53
53
  | [`KnownHostsMarker`](https://github.com/tonywied17/zero-transfer/blob/main/docs/api-md/type-aliases/KnownHostsMarker.md) | Type | See API reference. |
54
+ | [`runSshCommand`](https://github.com/tonywied17/zero-transfer/blob/main/docs/api-md/functions/runSshCommand.md) | Function | See API reference. |
55
+ | [`RunSshCommandOptions`](https://github.com/tonywied17/zero-transfer/blob/main/docs/api-md/interfaces/RunSshCommandOptions.md) | Interface | See API reference. |
56
+ | [`RunSshCommandResult`](https://github.com/tonywied17/zero-transfer/blob/main/docs/api-md/interfaces/RunSshCommandResult.md) | Interface | See API reference. |
54
57
 
55
58
  ## Examples
56
59
 
package/dist/index.cjs CHANGED
@@ -92,6 +92,7 @@ __export(ssh_exports, {
92
92
  importOpenSshConfig: () => importOpenSshConfig,
93
93
  importWinScpSessions: () => importWinScpSessions,
94
94
  isClassicProviderId: () => isClassicProviderId,
95
+ isMainModule: () => isMainModule,
95
96
  isSensitiveKey: () => isSensitiveKey,
96
97
  joinRemotePath: () => joinRemotePath,
97
98
  matchKnownHosts: () => matchKnownHosts,
@@ -113,6 +114,7 @@ __export(ssh_exports, {
113
114
  resolveProviderId: () => resolveProviderId,
114
115
  resolveSecret: () => resolveSecret,
115
116
  runConnectionDiagnostics: () => runConnectionDiagnostics,
117
+ runSshCommand: () => runSshCommand,
116
118
  serializeRemoteManifest: () => serializeRemoteManifest,
117
119
  sortRemoteEntries: () => sortRemoteEntries,
118
120
  summarizeClientDiagnostics: () => summarizeClientDiagnostics,
@@ -4901,6 +4903,19 @@ function isModifiedAtDifferent2(source, destination, toleranceMs) {
4901
4903
  return Math.abs(sourceTime - destinationTime) > toleranceMs;
4902
4904
  }
4903
4905
 
4906
+ // src/utils/mainModule.ts
4907
+ var import_node_url = require("url");
4908
+ function isMainModule(importMetaUrl) {
4909
+ if (typeof process === "undefined" || !process.argv || process.argv.length < 2) {
4910
+ return false;
4911
+ }
4912
+ try {
4913
+ return process.argv[1] === (0, import_node_url.fileURLToPath)(importMetaUrl);
4914
+ } catch {
4915
+ return false;
4916
+ }
4917
+ }
4918
+
4904
4919
  // src/protocols/ssh/transport/SshTransportConnection.ts
4905
4920
  var import_node_buffer15 = require("buffer");
4906
4921
 
@@ -6271,7 +6286,7 @@ var SshTransportPacketUnprotector = class {
6271
6286
  }
6272
6287
  /**
6273
6288
  * Feeds raw encrypted bytes from the socket and returns any fully decoded payloads.
6274
- * Maintains internal framing state across calls pass each `data` event chunk directly.
6289
+ * Maintains internal framing state across calls - pass each `data` event chunk directly.
6275
6290
  */
6276
6291
  pushBytes(chunk) {
6277
6292
  this.framePendingRaw = import_node_buffer14.Buffer.concat([this.framePendingRaw, chunk]);
@@ -6779,7 +6794,7 @@ var SshTransportConnection = class {
6779
6794
  assertConnected() {
6780
6795
  if (!this.connected) {
6781
6796
  throw new ProtocolError({
6782
- message: "SshTransportConnection is not yet connected \u2014 call connect() first",
6797
+ message: "SshTransportConnection is not yet connected - call connect() first",
6783
6798
  protocol: "sftp",
6784
6799
  retryable: false
6785
6800
  });
@@ -7735,6 +7750,84 @@ var SshConnectionManager = class {
7735
7750
  }
7736
7751
  }
7737
7752
  };
7753
+
7754
+ // src/protocols/ssh/runSshCommand.ts
7755
+ var import_node_net = require("net");
7756
+ var DEFAULT_PORT = 22;
7757
+ var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
7758
+ var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
7759
+ var DEFAULT_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
7760
+ async function runSshCommand(options) {
7761
+ const {
7762
+ host,
7763
+ port = DEFAULT_PORT,
7764
+ command,
7765
+ auth,
7766
+ transport: transportOptions,
7767
+ connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS,
7768
+ maxOutputBytes = DEFAULT_MAX_OUTPUT_BYTES
7769
+ } = options;
7770
+ const socket = await openTcpSocket(host, port, connectTimeoutMs);
7771
+ const transport = new SshTransportConnection({
7772
+ handshakeTimeoutMs: DEFAULT_HANDSHAKE_TIMEOUT_MS,
7773
+ ...transportOptions
7774
+ });
7775
+ try {
7776
+ const handshake = await transport.connect(socket);
7777
+ const authSession = new SshAuthSession(transport);
7778
+ await authSession.authenticate({
7779
+ credential: auth,
7780
+ sessionId: handshake.keyExchange.sessionId
7781
+ });
7782
+ const conn = new SshConnectionManager(transport);
7783
+ const channel = await conn.openExecChannel(command);
7784
+ const pump = conn.start();
7785
+ pump.catch(() => {
7786
+ });
7787
+ const chunks = [];
7788
+ let bytesReceived = 0;
7789
+ try {
7790
+ for await (const chunk of channel.receiveData()) {
7791
+ bytesReceived += chunk.length;
7792
+ if (bytesReceived > maxOutputBytes) {
7793
+ throw new Error(
7794
+ `runSshCommand: stdout exceeded ${maxOutputBytes} bytes (set maxOutputBytes to allow more)`
7795
+ );
7796
+ }
7797
+ chunks.push(chunk);
7798
+ }
7799
+ } finally {
7800
+ channel.close();
7801
+ }
7802
+ const stdout = Buffer.concat(chunks);
7803
+ return {
7804
+ stdout,
7805
+ stdoutText: stdout.toString("utf8"),
7806
+ bytesReceived
7807
+ };
7808
+ } finally {
7809
+ transport.disconnect();
7810
+ }
7811
+ }
7812
+ function openTcpSocket(host, port, timeoutMs) {
7813
+ return new Promise((resolve, reject) => {
7814
+ const socket = (0, import_node_net.connect)({ host, port });
7815
+ const timer = setTimeout(() => {
7816
+ socket.destroy();
7817
+ reject(
7818
+ new Error(`runSshCommand: TCP connect to ${host}:${port} timed out after ${timeoutMs}ms`)
7819
+ );
7820
+ }, timeoutMs);
7821
+ socket.once("connect", () => {
7822
+ clearTimeout(timer);
7823
+ resolve(socket);
7824
+ });
7825
+ socket.once("error", (error) => {
7826
+ clearTimeout(timer);
7827
+ reject(error);
7828
+ });
7829
+ });
7830
+ }
7738
7831
  // Annotate the CommonJS export names for ESM import in node:
7739
7832
  0 && (module.exports = {
7740
7833
  AbortError,
@@ -7799,6 +7892,7 @@ var SshConnectionManager = class {
7799
7892
  importOpenSshConfig,
7800
7893
  importWinScpSessions,
7801
7894
  isClassicProviderId,
7895
+ isMainModule,
7802
7896
  isSensitiveKey,
7803
7897
  joinRemotePath,
7804
7898
  matchKnownHosts,
@@ -7820,6 +7914,7 @@ var SshConnectionManager = class {
7820
7914
  resolveProviderId,
7821
7915
  resolveSecret,
7822
7916
  runConnectionDiagnostics,
7917
+ runSshCommand,
7823
7918
  serializeRemoteManifest,
7824
7919
  sortRemoteEntries,
7825
7920
  summarizeClientDiagnostics,