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"