diff --git a/cw/package.json b/cw/package.json
index 25a16108b816722e7898acb03ed20f1dbd1bcbab..6cf4f41f906c4e28bea161291919b96034d3c1bb 100644
--- a/cw/package.json
+++ b/cw/package.json
@@ -14,6 +14,7 @@
     "async-retry": "^1.3.3",
     "commander": "^9.2.0",
     "eciesjs": "^0.3.14",
-    "koa-compose": "^4.1.0"
+    "koa-compose": "^4.1.0",
+    "tweetnacl": "^1.0.3"
   }
 }
diff --git a/cw/src/client.d.ts b/cw/src/client.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7ca5fbf77a92fb4ae612ccf7c5ce7ac0d740bb4
--- /dev/null
+++ b/cw/src/client.d.ts
@@ -0,0 +1,7 @@
+/// <reference types="node" />
+import { Socket } from "net";
+export declare function connect(): Promise<Socket>;
+export declare type Context = {
+    conn: Socket;
+} & Record<any, any>;
+export declare const capabilities: string[];
diff --git a/cw/src/client.js b/cw/src/client.js
new file mode 100644
index 0000000000000000000000000000000000000000..e1d720df76d1377be9a1e83e8d9c57e2a4f1c8a7
--- /dev/null
+++ b/cw/src/client.js
@@ -0,0 +1,93 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.capabilities = exports.connect = void 0;
+const net_1 = require("net");
+const utils_1 = require("./utils");
+const messages_1 = require("./messages");
+const secp256k1_1 = require("@noble/secp256k1");
+const crypto_1 = require("crypto");
+const stream_1 = require("stream");
+async function connect() {
+    const sock = (0, net_1.createConnection)({ host: "localhost", port: 30522 });
+    return await new Promise(r => {
+        sock.on("connect", () => r(sock));
+    });
+}
+exports.connect = connect;
+exports.capabilities = [
+    "text:0.0.1",
+    "sha256:0.0.1",
+    "aes-256-ctr:0.0.1",
+    "secp256k1:0.0.1"
+];
+async function main() {
+    const conn = await connect();
+    (0, utils_1.registerRecvBuffer)(conn);
+    conn.setKeepAlive();
+    conn.setNoDelay(true);
+    conn.on("error", (err) => {
+        console.error(err);
+    });
+    conn.on("end", () => {
+        throw new Error(`Stream closed`);
+    });
+    // @ts-ignore
+    conn._writableState.highWaterMark = 1;
+    // @ts-ignore
+    conn._readableState.highWaterMark = 1;
+    const destroy = utils_1.destroyUB.bind(undefined, conn);
+    const write = utils_1.writeUB.bind(undefined, conn);
+    const waitForData = utils_1.waitForDataUB.bind(undefined, conn);
+    await write((0, messages_1.genIntro)());
+    console.log("written intro");
+    if ((0, messages_1.parseIntro)(await waitForData()))
+        destroy("Invalid intro");
+    console.log("intro done");
+    await (0, messages_1.sendCapabilities)(write, exports.capabilities);
+    console.log("sent capabilities: ", exports.capabilities);
+    const serverCaps = await (0, messages_1.receiveCapabilities)(waitForData, destroy);
+    console.log("server capabilities: ", serverCaps);
+    // agreement.
+    await write((0, utils_1.setMessageID)(Buffer.alloc(2), 1));
+    const serverPubKey = await (0, messages_1.receivePubKey)(waitForData, destroy);
+    console.log("received pubkey ", serverPubKey);
+    // console.log("ServerPubKey: ", serverPubKey)
+    const privKey = Buffer.from(secp256k1_1.utils.randomPrivateKey());
+    const pubKey = Buffer.from((0, secp256k1_1.getPublicKey)(privKey));
+    // console.log("pubKey: ", pubKey)
+    await (0, messages_1.sendPubKey)(write, pubKey);
+    console.log("sent pubkey");
+    await (0, messages_1.receiveEncCheck)(write, waitForData, destroy, privKey);
+    await (0, messages_1.sendEncCheck)(write, waitForData, destroy, serverPubKey);
+    const { key, iv } = await (0, messages_1.receiveSymKey)(waitForData, destroy, write, privKey, serverPubKey);
+    console.log("received encryption key");
+    let cipher = (0, crypto_1.createCipheriv)("aes-256-ctr", key, iv);
+    let decipher = (0, crypto_1.createDecipheriv)("aes-256-ctr", key, iv);
+    const writeEnc = utils_1.writeEncUB.bind(undefined, conn, cipher);
+    const waitForEncData = utils_1.waitForEncDataUB.bind(undefined, conn, decipher);
+    console.log("done");
+    // await writeEnc(genIntro())
+    // console.log("p2pem:", (await waitForEncData()).toString("utf8"))
+    let state = { s: "waiting" };
+    // @ts-ignore
+    const msgListener = utils_1.msgListenerUB.bind(undefined, writeEnc, waitForEncData, destroy, decipher, state, exports.capabilities);
+    // now we event-emitter bind to the socket to allow for unprompted comms
+    conn.on("data", async (d) => {
+        //@ts-ignore
+        await msgListener(d);
+    });
+    state.s = "transmitting";
+    const data = "Hello, World!";
+    const reqStatus = await (0, messages_1.sendMsgReq)(writeEnc, waitForEncData, "text:0.0.1", BigInt(data.length));
+    if (reqStatus) {
+        console.log("sending message ", data);
+        const bData = stream_1.Readable.from(Buffer.from(data, "utf8"));
+        // const bData = createReadStream("./llama.png")
+        await (0, messages_1.sendMsg)(writeEnc, waitForEncData, bData);
+    }
+    else {
+        console.log("validation failed");
+    }
+    console.log("done");
+}
+main();
diff --git a/cw/src/client.ts b/cw/src/client.ts
index e1e06ba03249141426c61476c2358ca9eaf55efc..2a7b8404de1d72c5eb830f0e3cb2e95967fe4dea 100644
--- a/cw/src/client.ts
+++ b/cw/src/client.ts
@@ -5,6 +5,7 @@ import { genIntro, parseIntro, receiveCapabilities, receiveEncCheck, receivePubK
 import { getPublicKey, utils } from "@noble/secp256k1"
 import { createCipheriv, createDecipheriv } from "crypto";
 import { createReadStream } from "fs";
+import { Readable } from "stream";
 
 
 export async function connect(): Promise<Socket> {
@@ -21,8 +22,9 @@ export type Context = {
 
 export const capabilities = [
     "text:0.0.1",
-    "test:0.0.1",
-    "data:0.0.1"
+    "sha256:0.0.1",
+    "aes-256-ctr:0.0.1",
+    "secp256k1:0.0.1"
 ]
 
 async function main() {
@@ -36,7 +38,7 @@ async function main() {
         console.error(err)
     })
     conn.on("end", () => {
-        throw new Error(`Stream closed`)
+        throw new Error(`Stream closed - closing client`)
     })
 
     // @ts-ignore
@@ -50,20 +52,26 @@ async function main() {
 
     await write(genIntro())
 
+    console.log("written intro")
+
     if (parseIntro(await waitForData())) destroy("Invalid intro")
 
     console.log("intro done")
 
     await sendCapabilities(write, capabilities)
 
+    console.log("sent capabilities: ", capabilities)
+
     const serverCaps = await receiveCapabilities(waitForData, destroy)
     console.log("server capabilities: ", serverCaps)
 
     // agreement.
     await write(setMessageID(Buffer.alloc(2), 1))
 
+
     const serverPubKey = await receivePubKey(waitForData, destroy)
 
+    console.log("received pubkey ", serverPubKey)
     // console.log("ServerPubKey: ", serverPubKey)
 
     const privKey = Buffer.from(utils.randomPrivateKey())
@@ -73,11 +81,14 @@ async function main() {
 
     await sendPubKey(write, pubKey);
 
+    console.log("sent pubkey")
+
     await receiveEncCheck(write, waitForData, destroy, privKey)
 
     await sendEncCheck(write, waitForData, destroy, serverPubKey)
 
     const { key, iv } = await receiveSymKey(waitForData, destroy, write, privKey, serverPubKey)
+    console.log("received encryption key")
 
     let cipher = createCipheriv("aes-256-ctr", key, iv)
     let decipher = createDecipheriv("aes-256-ctr", key, iv)
@@ -103,10 +114,11 @@ async function main() {
 
     state.s = "transmitting"
     const data = "Hello, World!"
-    const reqStatus = await sendMsgReq(writeEnc, waitForEncData, "data:0.0.1", BigInt(data.length));
+    const reqStatus = await sendMsgReq(writeEnc, waitForEncData, "text:0.0.1", BigInt(data.length));
     if (reqStatus) {
-        //const bData = Buffer.from(data, "utf8")
-        const bData = createReadStream("./llama.png")
+        console.log("sending message ", data)
+        const bData = Readable.from(Buffer.from(data, "utf8"))
+        // const bData = createReadStream("./llama.png")
         await sendMsg(writeEnc, waitForEncData, bData)
     } else {
         console.log("validation failed");
diff --git a/cw/src/messages.d.ts b/cw/src/messages.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..618cb0dd7dd4e788aab428d2bbcfb5322b6dbc5f
--- /dev/null
+++ b/cw/src/messages.d.ts
@@ -0,0 +1,24 @@
+/// <reference types="node" />
+import { destroy, waitForData, write } from "./utils";
+import { Readable } from "stream";
+export declare function genIntro(): Buffer;
+export declare function parseIntro(b: Buffer): boolean;
+export declare function sendCapabilities(write: (d: any) => Promise<unknown>, capabilities: string[]): Promise<void>;
+export declare function receiveCapabilities(waitForData: waitForData, destroy: any): Promise<string[]>;
+export declare function sendPubKey(write: write, pubKey: Buffer): Promise<void>;
+export declare function receivePubKey(waitForData: waitForData, destroy: destroy): Promise<Buffer>;
+export declare function sendEncCheck(write: write, waitForData: waitForData, destroy: destroy, clientPubKey: Buffer): Promise<void>;
+export declare function receiveEncCheck(write: write, waitForData: waitForData, destroy: destroy, privKey: Buffer): Promise<void>;
+export declare function sendEncCheckED25519(write: write, waitForData: waitForData, destroy: destroy, clientPubKey: Buffer): Promise<void>;
+export declare function receiveEncCheckED25519(write: write, waitForData: waitForData, destroy: destroy, privKey: Buffer): Promise<void>;
+export declare function genAndSendSymKey(write: write, waitForData: waitForData, destroy: destroy, privKey: Buffer, clientPubKey: Buffer): Promise<{
+    key: Buffer;
+    iv: Buffer;
+}>;
+export declare function receiveSymKey(waitForData: waitForData, destroy: destroy, write: write, privKey: Buffer, serverPubKey: Buffer): Promise<{
+    key: Buffer;
+    iv: Buffer;
+}>;
+export declare function sendConfirmation(write: write): Promise<void>;
+export declare function sendMsgReq(write: write, waitForData: waitForData, capability: string, size: bigint): Promise<Boolean>;
+export declare function sendMsg(write: write, waitForData: waitForData, data: Readable): Promise<void>;
diff --git a/cw/src/messages.js b/cw/src/messages.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8716aca406eb005fe300c4b49a77f6e2ddddae1
--- /dev/null
+++ b/cw/src/messages.js
@@ -0,0 +1,252 @@
+"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    var desc = Object.getOwnPropertyDescriptor(m, k);
+    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+      desc = { enumerable: true, get: function() { return m[k]; } };
+    }
+    Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.sendMsg = exports.sendMsgReq = exports.sendConfirmation = exports.receiveSymKey = exports.genAndSendSymKey = exports.receiveEncCheckED25519 = exports.sendEncCheckED25519 = exports.receiveEncCheck = exports.sendEncCheck = exports.receivePubKey = exports.sendPubKey = exports.receiveCapabilities = exports.sendCapabilities = exports.parseIntro = exports.genIntro = void 0;
+const utils_1 = require("./utils");
+const Crypto = __importStar(require("crypto"));
+const eciesjs_1 = require("eciesjs");
+const crypto_1 = require("crypto");
+const secp256k1_1 = require("@noble/secp256k1");
+const tweetnacl_1 = __importDefault(require("tweetnacl"));
+function genIntro() {
+    let t = Buffer.alloc(7);
+    t.writeIntBE(0, 0, 2);
+    t.write("P2PEM", 2, "utf8");
+    console.log("sending intro ");
+    return t;
+}
+exports.genIntro = genIntro;
+function parseIntro(b) {
+    console.log("received intro: ", b.toString("utf8", 2));
+    return (b.readIntBE(0, 2) != 0 || b.toString("utf8", 2) !== "P2PEM");
+}
+exports.parseIntro = parseIntro;
+async function sendCapabilities(write, capabilities) {
+    //cap primer
+    const capLength = capabilities.length;
+    let t = (0, utils_1.setMessageID)(Buffer.alloc(4), 5);
+    t.writeUIntBE(capLength, 2, 2);
+    await write(t);
+    for (let i = 0; i < capLength; i++) {
+        let cap = capabilities[i];
+        t = (0, utils_1.setMessageID)(Buffer.alloc(2 + 1 + cap.length), 6);
+        t.writeIntBE(cap.length, 2, 1);
+        t.write(cap, 3, "utf8");
+        await write(t);
+    }
+}
+exports.sendCapabilities = sendCapabilities;
+async function receiveCapabilities(waitForData, destroy) {
+    const capPrimer = await waitForData();
+    if ((0, utils_1.getMessageID)(capPrimer) != 5)
+        destroy("Invalid cap primer message ID");
+    const capLength = capPrimer.readUIntBE(2, 2);
+    if (capLength > 1024 || capLength < 0)
+        destroy("Invalid capability primer");
+    const clientCaps = [];
+    for (let i = 0; i < capLength; i++) {
+        let d = await waitForData();
+        if ((0, utils_1.getMessageID)(d) != 6)
+            destroy("Invalid cap message ID");
+        const capLength = d.readUIntBE(2, 1);
+        const recvCap = d.toString("utf8", 3);
+        if (recvCap.length != capLength) {
+            console.log(d);
+            destroy(`Received capability wrong length ${capLength} - ${recvCap}`);
+        }
+        clientCaps.push(recvCap);
+    }
+    return clientCaps;
+}
+exports.receiveCapabilities = receiveCapabilities;
+async function sendPubKey(write, pubKey) {
+    let t = (0, utils_1.setMessageID)(Buffer.alloc(pubKey.byteLength + 2), 2);
+    pubKey.copy(t, 2);
+    await write(t);
+}
+exports.sendPubKey = sendPubKey;
+async function receivePubKey(waitForData, destroy) {
+    let data = await waitForData();
+    if ((0, utils_1.getMessageID)(data) !== 2)
+        destroy("Expected pubKey message");
+    // const serverPubKey = data.toString("binary", 2)
+    return data.subarray(2);
+}
+exports.receivePubKey = receivePubKey;
+async function sendEncCheck(write, waitForData, destroy, clientPubKey) {
+    const byteCount = Crypto.randomInt(1_024, (65_536 - 97));
+    const bytes = Crypto.randomBytes(byteCount);
+    const message = (0, utils_1.setMessageID)(Buffer.alloc(byteCount + 97 + 2), 3);
+    const enc = (0, eciesjs_1.encrypt)(clientPubKey, bytes);
+    enc.copy(message, 2);
+    await write(message);
+    const data = await waitForData();
+    if ((0, utils_1.getMessageID)(data) !== 4)
+        destroy("Expected message ID 4");
+    if (Buffer.compare(bytes, data.subarray(2)) !== 0)
+        destroy("Encryption ID check buffer content mismatch");
+    await sendConfirmation(write);
+}
+exports.sendEncCheck = sendEncCheck;
+async function receiveEncCheck(write, waitForData, destroy, privKey) {
+    let d = await waitForData();
+    if ((0, utils_1.getMessageID)(d) !== 3)
+        destroy("Expected message ID 3");
+    const data = (0, eciesjs_1.decrypt)(privKey, d.subarray(2));
+    let msg = (0, utils_1.setMessageID)(Buffer.alloc(data.length + 2), 4);
+    data.copy(msg, 2);
+    await write(msg);
+    d = await waitForData();
+    if ((0, utils_1.getMessageID)(d) !== 1)
+        destroy("Expected message ID 1");
+}
+exports.receiveEncCheck = receiveEncCheck;
+async function sendEncCheckED25519(write, waitForData, destroy, clientPubKey) {
+    const byteCount = Crypto.randomInt(1_024, (65_536 - 97));
+    const bytes = Crypto.randomBytes(byteCount);
+    const message = (0, utils_1.setMessageID)(Buffer.alloc(byteCount + 97 + 2), 3);
+    //const enc = encrypt(clientPubKey, bytes)
+    const enc = Buffer.from(tweetnacl_1.default.box.after(message, clientPubKey.subarray(clientPubKey.length - 1, clientPubKey.length), clientPubKey.subarray(0, clientPubKey.length - 1)));
+    enc.copy(message, 2);
+    await write(message);
+    const data = await waitForData();
+    if ((0, utils_1.getMessageID)(data) !== 4)
+        destroy("Expected message ID 4");
+    if (Buffer.compare(bytes, data.subarray(2)) !== 0)
+        destroy("Encryption ID check buffer content mismatch");
+    await sendConfirmation(write);
+}
+exports.sendEncCheckED25519 = sendEncCheckED25519;
+async function receiveEncCheckED25519(write, waitForData, destroy, privKey) {
+    let d = await waitForData();
+    if ((0, utils_1.getMessageID)(d) !== 3)
+        destroy("Expected message ID 3");
+    const data = tweetnacl_1.default.box.open.after(d.subarray(2), privKey.subarray(privKey.length - 1, privKey.length), privKey.subarray(0, privKey.length - 1));
+    if (!data) {
+        destroy("enc check no data");
+        return;
+    }
+    let msg = (0, utils_1.setMessageID)(Buffer.alloc(data.length + 2), 4);
+    Buffer.from(data).copy(msg, 2);
+    await write(msg);
+    d = await waitForData();
+    if ((0, utils_1.getMessageID)(d) !== 1)
+        destroy("Expected message ID 1");
+}
+exports.receiveEncCheckED25519 = receiveEncCheckED25519;
+async function genAndSendSymKey(write, waitForData, destroy, privKey, clientPubKey) {
+    const key = (0, crypto_1.randomBytes)(32);
+    const iv = (0, crypto_1.randomBytes)(16);
+    const fullKey = Buffer.alloc(key.length + iv.length);
+    key.copy(fullKey, 0);
+    iv.copy(fullKey, 32);
+    const sig = Buffer.from(await (0, secp256k1_1.sign)(await secp256k1_1.utils.sha256(fullKey), privKey)); // 70 bytes
+    const encKey = (0, eciesjs_1.encrypt)(clientPubKey, fullKey); //145 bytes
+    let t = (0, utils_1.setMessageID)(Buffer.alloc(2 + 2 + encKey.length + 2 + sig.length), 7);
+    t.writeIntBE(encKey.length, 2, 2);
+    encKey.copy(t, 4);
+    t.writeIntBE(sig.length, encKey.length + 4, 2);
+    sig.copy(t, encKey.length + 6);
+    console.log({
+        keyLength: encKey.length, sigLength: sig.length,
+        kfb: encKey.at(0), klb: encKey.at(-1),
+        sfb: sig.at(0), slb: sig.at(-1)
+    });
+    await write(t);
+    if ((0, utils_1.getMessageID)(await waitForData()) !== 1)
+        destroy("expected confirmation of key validity");
+    return { key, iv };
+}
+exports.genAndSendSymKey = genAndSendSymKey;
+async function receiveSymKey(waitForData, destroy, write, privKey, serverPubKey) {
+    let d = await waitForData();
+    if ((0, utils_1.getMessageID)(d) !== 7)
+        destroy("expected ID 7");
+    const keyLength = d.readIntBE(2, 2);
+    const encKey = d.subarray(4, keyLength + 4);
+    const sigLength = d.readIntBE(keyLength + 4, 2);
+    const sig = d.subarray(keyLength + 6, keyLength + 6 + sigLength);
+    console.log({
+        keyLength, sigLength,
+        kfb: encKey.at(0), klb: encKey.at(-1),
+        sfb: sig.at(0), slb: sig.at(-1)
+    });
+    const fullKey = (0, eciesjs_1.decrypt)(privKey, encKey);
+    if (!(0, secp256k1_1.verify)(sig, await secp256k1_1.utils.sha256(fullKey), serverPubKey))
+        destroy("Invalid signature for encrypted symKey");
+    await sendConfirmation(write);
+    const key = fullKey.subarray(0, 32);
+    const iv = fullKey.subarray(32);
+    return { key, iv };
+}
+exports.receiveSymKey = receiveSymKey;
+async function sendConfirmation(write) {
+    await write((0, utils_1.setMessageID)(Buffer.alloc(2), 1));
+}
+exports.sendConfirmation = sendConfirmation;
+async function sendMsgReq(write, waitForData, capability, size) {
+    let m = (0, utils_1.setMessageID)(Buffer.alloc(2 + 1 + capability.length + 8), 8);
+    m.writeUIntBE(capability.length, 2, 1);
+    m.write(capability, 3, "utf8");
+    m.writeBigUInt64BE(size, capability.length + 3);
+    // m.writeUBigIntBE(size, capability.length + 2, 8)
+    await write(m);
+    const res = await waitForData();
+    if ((0, utils_1.getMessageID)(res) === 9 && res.readIntBE(2, 1) === 1)
+        return true;
+    return false;
+}
+exports.sendMsgReq = sendMsgReq;
+async function sendMsg(write, waitForData, data) {
+    const ckr = new utils_1.SizeChunker({
+        chunkSize: 1_000_000,
+        flushTail: true
+    });
+    data.pipe(ckr);
+    for await (const chunk of ckr) {
+        const data = chunk.data;
+        let m = (0, utils_1.setMessageID)(Buffer.alloc(2 + 8), 10);
+        m.writeBigUInt64BE(BigInt(data.length), 2);
+        const hash = Buffer.from(await secp256k1_1.utils.sha256(data));
+        // hash.copy(m, 10)
+        // data.copy(m, m.length)
+        m = Buffer.concat([m, hash, data]);
+        let i = 0;
+        do {
+            await write(m);
+            const res = await waitForData();
+            if ((0, utils_1.getMessageID)(res) !== 11)
+                console.warn("Chunk ack bad ID");
+            if (res.at(2) === 1)
+                i = 4;
+            i++;
+        } while (i < 3);
+    }
+    console.log("done");
+}
+exports.sendMsg = sendMsg;
diff --git a/cw/src/messages.ts b/cw/src/messages.ts
index 29d012af665ff72c2f21094748a37ed91be4d66a..0e6ab51d007ffe34c6b93e2ab9c097f992cfbfb1 100644
--- a/cw/src/messages.ts
+++ b/cw/src/messages.ts
@@ -5,15 +5,18 @@ import { encrypt, decrypt } from 'eciesjs'
 import { randomBytes } from "crypto";
 import { utils, sign, verify } from "@noble/secp256k1";
 import { Readable } from "stream";
+import nacl from "tweetnacl";
 
 export function genIntro(): Buffer {
     let t = Buffer.alloc(7)
     t.writeIntBE(0, 0, 2)
     t.write("P2PEM", 2, "utf8")
+    console.log("sending intro ")
     return t
 }
 
 export function parseIntro(b: Buffer): boolean {
+    console.log("received intro: ", b.toString("utf8", 2))
     return (b.readIntBE(0, 2) != 0 || b.toString("utf8", 2) !== "P2PEM")
 }
 
@@ -113,6 +116,45 @@ export async function receiveEncCheck(write: write, waitForData: waitForData, de
 }
 
 
+export async function sendEncCheckED25519(write: write, waitForData: waitForData, destroy: destroy, clientPubKey: Buffer) {
+    const byteCount = Crypto.randomInt(1_024, (65_536 - 97))
+
+    const bytes = Crypto.randomBytes(byteCount)
+    const message = setMessageID(Buffer.alloc(byteCount + 97 + 2), 3)
+    //const enc = encrypt(clientPubKey, bytes)
+    const enc = Buffer.from(nacl.box.after(message, clientPubKey.subarray(clientPubKey.length - 1, clientPubKey.length), clientPubKey.subarray(0, clientPubKey.length - 1)))
+    enc.copy(message, 2)
+
+    await write(message)
+
+    const data = await waitForData();
+
+    if (getMessageID(data) !== 4) destroy("Expected message ID 4")
+
+    if (Buffer.compare(bytes, data.subarray(2)) !== 0) destroy("Encryption ID check buffer content mismatch")
+
+    await sendConfirmation(write)
+
+}
+
+export async function receiveEncCheckED25519(write: write, waitForData: waitForData, destroy: destroy, privKey: Buffer) {
+    let d = await waitForData();
+    if (getMessageID(d) !== 3) destroy("Expected message ID 3")
+
+    const data = nacl.box.open.after(d.subarray(2), privKey.subarray(privKey.length - 1, privKey.length), privKey.subarray(0, privKey.length - 1))
+    if (!data) { destroy("enc check no data"); return }
+    let msg = setMessageID(Buffer.alloc(data.length + 2), 4)
+    Buffer.from(data).copy(msg, 2)
+
+    await write(msg)
+
+    d = await waitForData();
+
+    if (getMessageID(d) !== 1) destroy("Expected message ID 1")
+
+}
+
+
 export async function genAndSendSymKey(write: write, waitForData: waitForData, destroy: destroy, privKey: Buffer, clientPubKey: Buffer) {
 
 
diff --git a/cw/src/server.d.ts b/cw/src/server.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..80f8b8aa6920d454bb54845ae72b78755ce8dd7e
--- /dev/null
+++ b/cw/src/server.d.ts
@@ -0,0 +1,5 @@
+/// <reference types="node" />
+import { Socket } from "net";
+export declare function listen(port?: number): Promise<void>;
+export declare const capabilities: string[];
+export declare function handleConnection(conn: Socket): Promise<void>;
diff --git a/cw/src/server.js b/cw/src/server.js
new file mode 100644
index 0000000000000000000000000000000000000000..5543dac5ce65e78b66f0ba549bd821016676c299
--- /dev/null
+++ b/cw/src/server.js
@@ -0,0 +1,84 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.handleConnection = exports.capabilities = exports.listen = void 0;
+const messages_1 = require("./messages");
+const net_1 = require("net");
+const utils_1 = require("./utils");
+const secp256k1_1 = require("@noble/secp256k1");
+const crypto_1 = require("crypto");
+const stream_1 = require("stream");
+async function listen(port = 30522) {
+    const server = (0, net_1.createServer)();
+    server.listen(port, () => {
+        console.log(`Listening on ${port}`);
+    });
+    server.on("connection", handleConnection);
+}
+exports.listen = listen;
+exports.capabilities = [
+    "text:0.0.1",
+    "sha256:0.0.1",
+    "aes-256-ctr:0.0.1",
+    "secp256k1:0.0.1"
+];
+async function handleConnection(conn) {
+    (0, utils_1.registerRecvBuffer)(conn);
+    conn.on("data", (d) => {
+        console.log(d);
+    });
+    const rAddr = `${conn.remoteAddress}:${conn.remotePort}`;
+    console.log(`New connection from ${rAddr}`);
+    conn.setKeepAlive();
+    conn.setNoDelay(true);
+    conn.on("error", (err) => {
+        console.error(`${err} from ${rAddr}`);
+        return;
+    });
+    conn.on("end", () => {
+        console.error(`Stream from ${rAddr} closed`);
+        return;
+    });
+    // @ts-ignore
+    conn._writableState.highWaterMark = 1;
+    // @ts-ignore
+    conn._readableState.highWaterMark = 1;
+    const destroy = utils_1.destroyUB.bind(undefined, conn);
+    const write = utils_1.writeUB.bind(undefined, conn);
+    const waitForData = utils_1.waitForDataUB.bind(undefined, conn);
+    if ((0, messages_1.parseIntro)(await waitForData()))
+        destroy("Invalid intro");
+    await write((0, messages_1.genIntro)());
+    console.log("intro done");
+    const clientCaps = await (0, messages_1.receiveCapabilities)(waitForData, destroy);
+    // todo: capability negotiation logic
+    await (0, messages_1.sendCapabilities)(write, exports.capabilities);
+    console.log("client capabilities: ", clientCaps);
+    if ((0, utils_1.getMessageID)(await waitForData()) != 1)
+        destroy("No confirmation for capability negotations");
+    const privKey = Buffer.from(secp256k1_1.utils.randomPrivateKey());
+    const pubKey = Buffer.from((0, secp256k1_1.getPublicKey)(privKey));
+    // console.log("pubKey: ", pubKey)
+    await (0, messages_1.sendPubKey)(write, pubKey);
+    const clientPubKey = await (0, messages_1.receivePubKey)(waitForData, destroy);
+    // console.log("ClientPubKey: ", clientPubKey)
+    await (0, messages_1.sendEncCheck)(write, waitForData, destroy, clientPubKey);
+    await (0, messages_1.receiveEncCheck)(write, waitForData, destroy, privKey);
+    const { key, iv } = await (0, messages_1.genAndSendSymKey)(write, waitForData, destroy, privKey, clientPubKey);
+    const cipher = (0, crypto_1.createCipheriv)("aes-256-ctr", key, iv);
+    const decipher = (0, crypto_1.createDecipheriv)("aes-256-ctr", key, iv);
+    const writeEnc = utils_1.writeEncUB.bind(undefined, conn, cipher);
+    const waitForEncData = utils_1.waitForEncDataUB.bind(undefined, conn, decipher);
+    console.log("done");
+    // console.log("p2pem:", (await waitForEncData()).toString("utf8"))
+    // await writeEnc(genIntro())
+    let state = { s: "waiting", wb: new stream_1.PassThrough() };
+    //@ts-ignore
+    const msgListener = utils_1.msgListenerUB.bind(undefined, writeEnc, waitForEncData, destroy, decipher, state, exports.capabilities);
+    // now we event-emitter bind to the socket to allow for unprompted comms
+    conn.on("data", async (d) => {
+        //@ts-ignore
+        await msgListener(d);
+    });
+}
+exports.handleConnection = handleConnection;
+listen();
diff --git a/cw/src/server.ts b/cw/src/server.ts
index cfe1d294079542d189730b141efd1f66a3fffec5..a6f8153178a7551656ec0cf5f7f18e0dd4159052 100644
--- a/cw/src/server.ts
+++ b/cw/src/server.ts
@@ -5,6 +5,7 @@ import { destroyUB, getMessageID, waitForDataUB, waitForEncDataUB, writeEncUB, w
 import { getPublicKey, utils } from "@noble/secp256k1"
 
 import { createCipheriv, createDecipheriv } from "crypto"
+import { Duplex, PassThrough, Transform } from "stream"
 
 
 export async function listen(port = 30522) {
@@ -17,10 +18,13 @@ export async function listen(port = 30522) {
 
 export const capabilities = [
     "text:0.0.1",
-    "data:0.0.1"
+    "sha256:0.0.1",
+    "aes-256-ctr:0.0.1",
+    "secp256k1:0.0.1"
 ]
 
 
+
 export async function handleConnection(conn: Socket) {
     registerRecvBuffer(conn)
     conn.on("data", (d) => {
@@ -97,7 +101,7 @@ export async function handleConnection(conn: Socket) {
     // await writeEnc(genIntro())
 
 
-    let state = { s: "waiting" }
+    let state = { s: "waiting", wb: new PassThrough() }
     //@ts-ignore
     const msgListener = msgListenerUB.bind(undefined, writeEnc, waitForEncData, destroy, decipher, state, capabilities)
     // now we event-emitter bind to the socket to allow for unprompted comms
diff --git a/cw/src/utils.d.ts b/cw/src/utils.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ee356dd0fdf8ee35a27dea853c1d0c1f2461be4
--- /dev/null
+++ b/cw/src/utils.d.ts
@@ -0,0 +1,33 @@
+/// <reference types="node" />
+import { Socket } from "net";
+import { Cipher, Decipher } from "crypto";
+import internal, { Transform } from "stream";
+export declare const sleep: (ms: number) => Promise<void>;
+export declare type waitForData = () => Promise<Buffer>;
+export declare type write = (d: any) => Promise<unknown>;
+export declare type destroy = (message: any) => void;
+export declare const destroyUB: (conn: Socket, msg: string) => void;
+export declare const writeUB: (conn: Socket, d: any) => Promise<unknown>;
+export declare function registerRecvBuffer(conn: Socket): void;
+export declare const writeEncUB: (conn: Socket, cipher: Cipher, d: any) => Promise<void>;
+export declare function waitForDataUB(s: Socket): Promise<Buffer>;
+export declare function waitForEncDataUB(s: Socket, decipher: Decipher): Promise<Buffer>;
+export declare function getMessageID(b: Buffer): number;
+export declare function setMessageID(b: Buffer, id: number): Buffer;
+export declare function msgListenerUB(write: write, waitForData: waitForData, destroy: destroy, decipher: Decipher, state: any, capabilities: string[], d: Buffer): Promise<void>;
+export declare class SizeChunker extends Transform {
+    protected bytesPassed: number;
+    protected currentChunk: number;
+    protected lastEmittedChunk: undefined | number;
+    protected chunkSize: number;
+    protected flushTail: boolean;
+    constructor(options: internal.TransformOptions & {
+        chunkSize: number;
+        flushTail: boolean;
+    });
+    protected finishChunk(done: any): void;
+    protected startChunk(done: any): void;
+    protected pushData(buf: Buffer): void;
+    protected startIfNeededAndPushData(buf: Buffer): void;
+    _transform(chunk: any, _encoding: BufferEncoding, done: internal.TransformCallback): void;
+}
diff --git a/cw/src/utils.js b/cw/src/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..82887503f901ddd6000e0489a1291735e1ceb938
--- /dev/null
+++ b/cw/src/utils.js
@@ -0,0 +1,202 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.SizeChunker = exports.msgListenerUB = exports.setMessageID = exports.getMessageID = exports.waitForEncDataUB = exports.waitForDataUB = exports.writeEncUB = exports.registerRecvBuffer = exports.writeUB = exports.destroyUB = exports.sleep = void 0;
+const secp256k1_1 = require("@noble/secp256k1");
+const stream_1 = require("stream");
+const perf_hooks_1 = require("perf_hooks");
+const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
+exports.sleep = sleep;
+const destroyUB = (conn, msg) => { conn.destroy(new Error(msg)); };
+exports.destroyUB = destroyUB;
+let sendDelay = 50;
+const writeUB = async (conn, d) => {
+    const diff = sendDelay - perf_hooks_1.performance.now();
+    if (diff > 0) {
+        await (0, exports.sleep)(diff);
+    }
+    sendDelay = perf_hooks_1.performance.now() + 100;
+    return new Promise((res, rej) => {
+        conn.write(d, (err) => {
+            conn.emit("drain");
+            if (err)
+                rej(err);
+            setImmediate(() => res(true));
+            // res(true)
+        });
+    });
+};
+exports.writeUB = writeUB;
+let readBuffer = [];
+function registerRecvBuffer(conn) {
+    conn.on("data", d => readBuffer.push(d));
+}
+exports.registerRecvBuffer = registerRecvBuffer;
+// let writes = 0;
+// let reads = 0;
+const writeEncUB = async (conn, cipher, d) => {
+    // console.log("enc writes:", ++writes)
+    await (0, exports.writeUB)(conn, cipher.update(d));
+};
+exports.writeEncUB = writeEncUB;
+async function waitForDataUB(s) {
+    if (readBuffer.length > 0) {
+        return readBuffer.shift();
+    }
+    return new Promise(res => s.once("data", d => {
+        readBuffer.shift();
+        res(d);
+    }));
+}
+exports.waitForDataUB = waitForDataUB;
+async function waitForEncDataUB(s, decipher) {
+    // console.log("enc reads:", ++reads)
+    const eData = await waitForDataUB(s);
+    const decData = decipher.update(eData);
+    return decData;
+}
+exports.waitForEncDataUB = waitForEncDataUB;
+function getMessageID(b) {
+    return b.readIntBE(0, 2);
+}
+exports.getMessageID = getMessageID;
+function setMessageID(b, id) {
+    b.writeIntBE(id, 0, 2);
+    return b;
+}
+exports.setMessageID = setMessageID;
+async function msgListenerUB(write, waitForData, destroy, decipher, state, capabilities, d) {
+    // const { write, destroy, waitForData } = f
+    // let state = f.state;
+    // DO NOT DECRYPT UNLESS NEEDED
+    // WILL DESYNC COUNTERS
+    if (state.s === "waiting") {
+        const data = decipher.update(d);
+        if (getMessageID(data) !== 8)
+            destroy("Expected ID 8");
+        const capLen = data.readIntBE(2, 1);
+        const cap = data.toString("utf8", 3, capLen + 3);
+        if (!capabilities.includes(cap))
+            destroy(`Invalid capability ${cap}`);
+        const dataLen = data.readBigUInt64BE(capLen + 3);
+        console.log(`Request for ${cap} with size ${dataLen}`);
+        const res = setMessageID(Buffer.alloc(3), 9);
+        res.writeIntBE(1, 2, 1);
+        state.s = "receiving";
+        state.rec = 0;
+        state.size = dataLen;
+        state.wb = new stream_1.PassThrough();
+        console.log(res);
+        await write(res);
+        return;
+    }
+    else if (state.s === "receiving") {
+        const data = decipher.update(d);
+        if (getMessageID(data) !== 10)
+            destroy("Expected ID 10 (chunk)");
+        const chnkSize = data.readBigUInt64BE(2);
+        const chnkHash = data.subarray(10, 42);
+        const chnkData = data.subarray(42);
+        const tstHash = await secp256k1_1.utils.sha256(chnkData);
+        const res = setMessageID(Buffer.alloc(3), 11);
+        if (Buffer.compare(chnkHash, tstHash) !== 0) {
+            res.writeUIntBE(0, 2, 1);
+            await write(res);
+            return;
+        }
+        res.writeUIntBE(1, 2, 1);
+        state.wb.write(chnkData);
+        state.rec += chnkData.length;
+        if (state.rec >= state.size) {
+            console.log("Received message:");
+            console.log(state.wb.read().toString("utf8"));
+            state.s = "waiting";
+        }
+        console.log({ chnkSize, chnkHash: chnkHash.length, chnkData: chnkData.length });
+        await write(res);
+    }
+}
+exports.msgListenerUB = msgListenerUB;
+class SizeChunker extends stream_1.Transform {
+    bytesPassed = 0;
+    currentChunk = -1;
+    lastEmittedChunk = undefined;
+    chunkSize;
+    flushTail;
+    constructor(options) {
+        super({ ...options, readableObjectMode: true });
+        this.chunkSize = options.chunkSize ?? 10_000;
+        this.flushTail = options.flushTail ?? false;
+        this.readableObjectMode;
+        this.once("end", () => {
+            if (this.flushTail && (this.lastEmittedChunk !== undefined) && this.bytesPassed > 0) {
+                this.emit("chunkEnd", this.currentChunk, () => { return; });
+            }
+        });
+    }
+    finishChunk(done) {
+        if (this.listenerCount("chunkEnd") > 0) {
+            this.emit("chunkEnd", this.currentChunk, () => {
+                this.bytesPassed = 0;
+                this.lastEmittedChunk = undefined;
+                done();
+            });
+        }
+        else {
+            this.bytesPassed = 0;
+            this.lastEmittedChunk = undefined;
+            done();
+        }
+    }
+    startChunk(done) {
+        this.currentChunk++;
+        if (this.listenerCount("chunkStart") > 0) {
+            this.emit("chunkStart", this.currentChunk, done);
+        }
+        else {
+            done();
+        }
+    }
+    pushData(buf) {
+        this.push({
+            data: buf,
+            id: this.currentChunk
+        });
+        this.bytesPassed += buf.length;
+    }
+    ;
+    startIfNeededAndPushData(buf) {
+        if (this.lastEmittedChunk != this.currentChunk) {
+            this.startChunk(() => {
+                this.lastEmittedChunk = this.currentChunk;
+                this.pushData(buf);
+            });
+        }
+        else {
+            this.pushData(buf);
+        }
+    }
+    _transform(chunk, _encoding, done) {
+        const doTransform = () => {
+            const bytesLeave = Math.min(chunk.length, this.chunkSize - this.bytesPassed);
+            let remainder;
+            if (this.bytesPassed + chunk.length < this.chunkSize) {
+                this.startIfNeededAndPushData(chunk);
+                done();
+            }
+            else {
+                remainder = bytesLeave - chunk.length;
+                if (remainder === 0) {
+                    this.startIfNeededAndPushData(chunk);
+                    this.finishChunk(done);
+                }
+                else {
+                    this.startIfNeededAndPushData(chunk.slice(0, bytesLeave));
+                    chunk = chunk.slice(bytesLeave);
+                    this.finishChunk(doTransform);
+                }
+            }
+        };
+        doTransform();
+    }
+}
+exports.SizeChunker = SizeChunker;
diff --git a/cw/src/utils.ts b/cw/src/utils.ts
index 78a0af7080810a5290d8fbcd40a7ee114fc2a9f1..e6143bf3f440fb516afad650a40088a6108a5fcc 100644
--- a/cw/src/utils.ts
+++ b/cw/src/utils.ts
@@ -1,10 +1,13 @@
 
 import { Socket } from "net";
 
-
 import { Cipher, Decipher } from "crypto"
 import { utils } from "@noble/secp256k1";
 
+
+import internal, { Duplex, PassThrough, Transform } from "stream";
+import { performance } from "perf_hooks";
+
 export const sleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));
 
 export type waitForData = () => Promise<Buffer>
@@ -87,7 +90,7 @@ export async function msgListenerUB(write: write, waitForData: waitForData, dest
         if (getMessageID(data) !== 8) destroy("Expected ID 8")
         const capLen = data.readIntBE(2, 1)
         const cap = data.toString("utf8", 3, capLen + 3)
-        if (!capabilities.includes(cap)) destroy("Invalid capability")
+        if (!capabilities.includes(cap)) destroy(`Invalid capability ${cap}`)
         const dataLen = data.readBigUInt64BE(capLen + 3)
         console.log(`Request for ${cap} with size ${dataLen}`)
         const res = setMessageID(Buffer.alloc(3), 9)
@@ -95,7 +98,7 @@ export async function msgListenerUB(write: write, waitForData: waitForData, dest
         state.s = "receiving";
         state.rec = 0;
         state.size = dataLen;
-        state.ws = createWriteStream("./recv.data")
+        state.wb = new PassThrough()
         console.log(res)
         await write(res)
         return;
@@ -113,13 +116,13 @@ export async function msgListenerUB(write: write, waitForData: waitForData, dest
             return;
         }
         res.writeUIntBE(1, 2, 1)
+
+        state.wb.write(chnkData)
         state.rec += chnkData.length;
         if (state.rec >= state.size) {
-            console.log("done?")
-            state.ws.end(chnkData)
+            console.log("Received message:")
+            console.log(state.wb.read().toString("utf8"))
             state.s = "waiting"
-        } else {
-            state.ws.write(chnkData)
         }
         console.log({ chnkSize, chnkHash: chnkHash.length, chnkData: chnkData.length })
         await write(res)
@@ -129,9 +132,7 @@ export async function msgListenerUB(write: write, waitForData: waitForData, dest
 
 
 
-import internal, { Transform } from "stream";
-import { createWriteStream } from "fs";
-import { performance } from "perf_hooks";
+
 
 export class SizeChunker extends Transform {
     protected bytesPassed = 0
diff --git a/cw/tsconfig.json b/cw/tsconfig.json
index 9f41421b353535f941e33e6787eb8619aeef3bf0..3781650919d755a45dfd5ddc9ec7f25cb1aa28f4 100644
--- a/cw/tsconfig.json
+++ b/cw/tsconfig.json
@@ -18,7 +18,8 @@
         "skipLibCheck": true,
         "strict": true,
         "traceResolution": false,
-        "types": ["node"]
+        "types": ["node"],
+        "module": "CommonJS"
     },
     "compileOnSave": true,
     "exclude": ["node_modules"],
diff --git a/cw/yarn.lock b/cw/yarn.lock
index 9f75eec6179e34d46bdeaa0980575adc5b67cecf..6e5dcfca0899898f338c9eb6daac62026b330eda 100644
--- a/cw/yarn.lock
+++ b/cw/yarn.lock
@@ -330,6 +330,11 @@ ts-node@^10.7.0:
     v8-compile-cache-lib "^3.0.0"
     yn "3.1.1"
 
+tweetnacl@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
+  integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
+
 typescript@^4.6.4:
   version "4.6.4"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"
diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity
index 50631ceb20b906467ba367fbef77b23956f3c0b5..08fc19f54289c0fbfa67cc8545b71d20501bb2a3 100644
--- a/node_modules/.yarn-integrity
+++ b/node_modules/.yarn-integrity
@@ -6,6 +6,7 @@
   "flags": [],
   "linkedModules": [],
   "topLevelPatterns": [
+    "inquirer@^8.2.4",
     "ts-node@^10.7.0",
     "typescript@^4.6.4"
   ],
@@ -18,13 +19,69 @@
     "@tsconfig/node16@^1.0.2": "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e",
     "acorn-walk@^8.1.1": "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1",
     "acorn@^8.4.1": "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30",
+    "ansi-escapes@^4.2.1": "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e",
+    "ansi-regex@^5.0.1": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304",
+    "ansi-styles@^4.0.0": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937",
+    "ansi-styles@^4.1.0": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937",
     "arg@^4.1.0": "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089",
+    "base64-js@^1.3.1": "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a",
+    "bl@^4.1.0": "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a",
+    "buffer@^5.5.0": "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0",
+    "chalk@^4.1.0": "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01",
+    "chalk@^4.1.1": "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01",
+    "chardet@^0.7.0": "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e",
+    "cli-cursor@^3.1.0": "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307",
+    "cli-spinners@^2.5.0": "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d",
+    "cli-width@^3.0.0": "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6",
+    "clone@^1.0.2": "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e",
+    "color-convert@^2.0.1": "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3",
+    "color-name@~1.1.4": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2",
     "create-require@^1.1.0": "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333",
+    "defaults@^1.0.3": "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d",
     "diff@^4.0.1": "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d",
+    "emoji-regex@^8.0.0": "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37",
+    "escape-string-regexp@^1.0.5": "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4",
+    "external-editor@^3.0.3": "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495",
+    "figures@^3.0.0": "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af",
+    "has-flag@^4.0.0": "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b",
+    "iconv-lite@^0.4.24": "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b",
+    "ieee754@^1.1.13": "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352",
+    "inherits@^2.0.3": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c",
+    "inherits@^2.0.4": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c",
+    "inquirer@^8.2.4": "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4",
+    "is-fullwidth-code-point@^3.0.0": "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d",
+    "is-interactive@^1.0.0": "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e",
+    "is-unicode-supported@^0.1.0": "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7",
+    "lodash@^4.17.21": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c",
+    "log-symbols@^4.1.0": "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503",
     "make-error@^1.1.1": "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2",
+    "mimic-fn@^2.1.0": "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b",
+    "mute-stream@0.0.8": "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d",
+    "onetime@^5.1.0": "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e",
+    "ora@^5.4.1": "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18",
+    "os-tmpdir@~1.0.2": "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274",
+    "readable-stream@^3.4.0": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198",
+    "restore-cursor@^3.1.0": "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e",
+    "run-async@^2.4.0": "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455",
+    "rxjs@^7.5.5": "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f",
+    "safe-buffer@~5.2.0": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6",
+    "safer-buffer@>= 2.1.2 < 3": "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a",
+    "signal-exit@^3.0.2": "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9",
+    "string-width@^4.1.0": "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010",
+    "string_decoder@^1.1.1": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e",
+    "strip-ansi@^6.0.0": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9",
+    "strip-ansi@^6.0.1": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9",
+    "supports-color@^7.1.0": "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da",
+    "through@^2.3.6": "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5",
+    "tmp@^0.0.33": "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9",
     "ts-node@^10.7.0": "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5",
+    "tslib@^2.1.0": "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3",
+    "type-fest@^0.21.3": "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37",
     "typescript@^4.6.4": "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9",
+    "util-deprecate@^1.0.1": "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf",
     "v8-compile-cache-lib@^3.0.0": "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf",
+    "wcwidth@^1.0.1": "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8",
+    "wrap-ansi@^7.0.0": "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43",
     "yn@3.1.1": "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
   },
   "files": [],
diff --git a/package.json b/package.json
index 4273314adb9221ec1da4ef3b2330bce85c37a741..feb66a0b4146d43c49f5369b77665c4ff9cbe496 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
   "author": "JesseTheRobot <jesse.cruz.wright@gmail.com>",
   "license": "MIT",
   "dependencies": {
+    "inquirer": "^8.2.4",
     "ts-node": "^10.7.0",
     "typescript": "^4.6.4"
   }
diff --git a/cw/src/cli.ts b/recv.data
similarity index 100%
rename from cw/src/cli.ts
rename to recv.data
diff --git a/yarn.lock b/yarn.lock
index 29739427452cf6ca3c68ba6d0d8b828a0953b8cf..f4f000150d15d12dc4c456c59f069da42683ffee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -44,26 +44,341 @@ acorn@^8.4.1:
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
   integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
 
+ansi-escapes@^4.2.1:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+  integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+  dependencies:
+    type-fest "^0.21.3"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
 arg@^4.1.0:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
   integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
 
+base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+bl@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+  integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+  dependencies:
+    buffer "^5.5.0"
+    inherits "^2.0.4"
+    readable-stream "^3.4.0"
+
+buffer@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
+chalk@^4.1.0, chalk@^4.1.1:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chardet@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+  integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+  dependencies:
+    restore-cursor "^3.1.0"
+
+cli-spinners@^2.5.0:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
+  integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
+
+cli-width@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
+  integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+
+clone@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
 create-require@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
   integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
 
+defaults@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+  integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
+  dependencies:
+    clone "^1.0.2"
+
 diff@^4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
   integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
 
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+external-editor@^3.0.3:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+  integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+  dependencies:
+    chardet "^0.7.0"
+    iconv-lite "^0.4.24"
+    tmp "^0.0.33"
+
+figures@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+  integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+iconv-lite@^0.4.24:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+ieee754@^1.1.13:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+inherits@^2.0.3, inherits@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inquirer@^8.2.4:
+  version "8.2.4"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4"
+  integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==
+  dependencies:
+    ansi-escapes "^4.2.1"
+    chalk "^4.1.1"
+    cli-cursor "^3.1.0"
+    cli-width "^3.0.0"
+    external-editor "^3.0.3"
+    figures "^3.0.0"
+    lodash "^4.17.21"
+    mute-stream "0.0.8"
+    ora "^5.4.1"
+    run-async "^2.4.0"
+    rxjs "^7.5.5"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+    through "^2.3.6"
+    wrap-ansi "^7.0.0"
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-interactive@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
+  integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+
+is-unicode-supported@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+  integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+lodash@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+  integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+  dependencies:
+    chalk "^4.1.0"
+    is-unicode-supported "^0.1.0"
+
 make-error@^1.1.1:
   version "1.3.6"
   resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
   integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
 
+mimic-fn@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+mute-stream@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+  integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+
+onetime@^5.1.0:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+  dependencies:
+    mimic-fn "^2.1.0"
+
+ora@^5.4.1:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
+  integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
+  dependencies:
+    bl "^4.1.0"
+    chalk "^4.1.0"
+    cli-cursor "^3.1.0"
+    cli-spinners "^2.5.0"
+    is-interactive "^1.0.0"
+    is-unicode-supported "^0.1.0"
+    log-symbols "^4.1.0"
+    strip-ansi "^6.0.0"
+    wcwidth "^1.0.1"
+
+os-tmpdir@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+readable-stream@^3.4.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+  integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+
+run-async@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
+  integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
+
+rxjs@^7.5.5:
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f"
+  integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
+  dependencies:
+    tslib "^2.1.0"
+
+safe-buffer@~5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+signal-exit@^3.0.2:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+string-width@^4.1.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+through@^2.3.6:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
+
+tmp@^0.0.33:
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+  integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+  dependencies:
+    os-tmpdir "~1.0.2"
+
 ts-node@^10.7.0:
   version "10.7.0"
   resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5"
@@ -83,16 +398,47 @@ ts-node@^10.7.0:
     v8-compile-cache-lib "^3.0.0"
     yn "3.1.1"
 
+tslib@^2.1.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+  integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
+type-fest@^0.21.3:
+  version "0.21.3"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+  integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
 typescript@^4.6.4:
   version "4.6.4"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"
   integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==
 
+util-deprecate@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
 v8-compile-cache-lib@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
   integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
 
+wcwidth@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+  integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+  dependencies:
+    defaults "^1.0.3"
+
+wrap-ansi@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 yn@3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"