# core/adapters/shell.adapter.ts

> 59 lines of code and 8 definitions.

Tree: Bane's Lab Deploy
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/deploy#file-deploy-core-adapters-shell-adapter-ts
Source text: https://banes-lab.com/source/deploy/core/adapters/shell.adapter.ts.txt

Listed in [core/adapters](https://banes-lab.com/api/source/deploy/core/adapters.md), after [core/adapters/server.adapter.ts](https://banes-lab.com/source/deploy/core/adapters/server.adapter.ts.md) and before [core/adapters/site.adapter.ts](https://banes-lab.com/source/deploy/core/adapters/site.adapter.ts.md).

## Definitions

- `guard` (lexical_declaration, line 26, exported)
- `transferOf` (lexical_declaration, line 9)
- `privateKey` (lexical_declaration, line 19)
- `createShell` (lexical_declaration, line 23, exported)
- `connect` (lexical_declaration, line 31, exported)
- `ssh` (lexical_declaration, line 24, exported)
- `connected` (lexical_declaration, line 25, exported)
- `result` (lexical_declaration, line 54, exported)

## Contained in

- [core/adapters](https://banes-lab.com/anatomy/deploy/folder-deploy-core-adapters.md)

## Linked from

- [core/adapters](https://banes-lab.com/anatomy/deploy/folder-deploy-core-adapters.md)

## Source

```typescript
import type { ByteStep, Secrets, Shell, TransferOptions } from "#types/deployment.types";
import { NOT_CONNECTED } from "#configuration/strings/deployment.strings";
import { NodeSSH } from "node-ssh";
import { SSH_FOLDER } from "#configuration/constants/ssh.constants";
import { homedir } from "node:os";
import { join } from "node:path";
import { readFileSync } from "node:fs";

const transferOf = function transferOf(step: ByteStep | undefined): TransferOptions | null {
    return step === undefined
        ? null
        : {
              step: (transferred: number, _chunk: number, size: number) => {
                  step(transferred, size);
              },
          };
};

const privateKey = function privateKey(keyFile: string): string {
    return readFileSync(join(homedir(), SSH_FOLDER, keyFile), "utf8");
};

export const createShell = function createShell(secrets: Secrets): Shell {
    const ssh = new NodeSSH();
    let connected = false;
    const guard = function guard(): void {
        if (!connected) {
            throw new Error(NOT_CONNECTED);
        }
    };
    const connect = async function connect(): Promise<void> {
        await ssh.connect({
            host: secrets.host,
            privateKey: privateKey(secrets.keyFile),
            username: secrets.user,
            ...(secrets.passphrase === null ? {} : { passphrase: secrets.passphrase }),
        });
        connected = true;
    };
    return {
        connect,
        dispose: () => {
            if (connected) {
                ssh.dispose();
                connected = false;
            }
        },
        download: async (localPath, remotePath, step) => {
            guard();
            await ssh.getFile(localPath, remotePath, null, transferOf(step));
        },
        run: async (command) => {
            guard();
            const result = await ssh.execCommand(command);
            return { code: result.code, stderr: result.stderr, stdout: result.stdout };
        },
        upload: async (localPath, remotePath, step) => {
            guard();
            await ssh.putFile(localPath, remotePath, null, transferOf(step));
        },
    };
};
```
