working chat and TTS

This commit is contained in:
Elijah R 2024-07-02 23:42:03 -04:00
parent c3c0d33e5b
commit c7963631cb
18 changed files with 564 additions and 36 deletions

1
.gitignore vendored
View file

@ -15,3 +15,4 @@
node_modules/
**/dist/
**/.parcel-cache/
server/config.toml

View file

@ -10,8 +10,14 @@
"scripts": {
"build": "parcel build"
},
<<<<<<< HEAD
"type": "module",
"source": "src/index.ts",
"module": "./dist/index.js",
"types": "./dist/index.d.ts"
=======
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module"
>>>>>>> 2414658 (working chat and TTS)
}

View file

@ -8,5 +8,6 @@
"build": "tsc"
},
"main": "./dist/protocol.js",
"types": "./dist/protocol.d.ts"
"types": "./dist/protocol.d.ts",
"type": "module"
}

View file

@ -6,7 +6,7 @@ export enum MSAgentProtocolMessageType {
Init = "init",
AddUser = "adduser",
RemoveUser = "remuser",
Message = "msg"
Chat = "chat"
}
export interface MSAgentProtocolMessage {
@ -19,6 +19,7 @@ export interface MSAgentJoinMessage extends MSAgentProtocolMessage {
op: MSAgentProtocolMessageType.Join,
data: {
username: string;
agent: string;
}
}
@ -34,7 +35,12 @@ export interface MSAgentTalkMessage extends MSAgentProtocolMessage {
export interface MSAgentInitMessage extends MSAgentProtocolMessage {
op: MSAgentProtocolMessageType.Init,
data: {
users: string[]
username: string
agent: string
users: {
username: string,
agent: string
}[]
}
}
@ -42,6 +48,7 @@ export interface MSAgentAddUserMessage extends MSAgentProtocolMessage {
op: MSAgentProtocolMessageType.AddUser,
data: {
username: string;
agent: string;
}
}
@ -51,3 +58,12 @@ export interface MSAgentRemoveUserMessage extends MSAgentProtocolMessage {
username: string;
}
}
export interface MSAgentChatMessage extends MSAgentProtocolMessage {
op: MSAgentProtocolMessageType.Chat,
data: {
username: string;
message: string;
audio? : string | undefined;
}
}

View file

@ -0,0 +1,11 @@
[http]
host = "127.0.0.1"
port = 3000
[tts]
enabled = true
# https://git.computernewb.com/computernewb/SAPIServer
server = "http://127.0.0.1:3001"
voice = "Microsoft Sam"
tempDir = "/tmp/msac-tts"
wavExpirySeconds = 60

View file

@ -11,8 +11,11 @@
"typescript": "^5.5.3"
},
"dependencies": {
"@fastify/static": "^7.0.4",
"@fastify/websocket": "^10.0.1",
"fastify": "^4.28.1",
"html-entities": "^2.5.2",
"toml": "^3.0.0",
"ws": "^8.17.1"
},
"type": "module"

View file

@ -1,10 +1,23 @@
import EventEmitter from "events";
import { WebSocket } from "ws";
import { MSAgentProtocolMessage, MSAgentProtocolMessageType } from '@msagent-chat/protocol';
import { MSAgentJoinMessage, MSAgentProtocolMessage, MSAgentProtocolMessageType, MSAgentTalkMessage } from '@msagent-chat/protocol';
import { MSAgentChatRoom } from "./room.js";
import * as htmlentities from 'html-entities';
// Event types
export interface Client {
on(event: 'join', listener: () => void): this;
on(event: 'close', listener: () => void): this;
on(event: 'talk', listener: (msg: string) => void): this;
on(event: string, listener: Function): this;
}
export class Client extends EventEmitter {
username: string | null;
agent: string | null;
room: MSAgentChatRoom;
socket: WebSocket;
constructor(socket: WebSocket, room: MSAgentChatRoom) {
@ -12,6 +25,7 @@ export class Client extends EventEmitter {
this.socket = socket;
this.room = room;
this.username = null;
this.agent = null;
this.socket.on('message', (msg, isBinary) => {
if (isBinary) {
this.socket.close();
@ -47,11 +61,22 @@ export class Client extends EventEmitter {
}
switch (msg.op) {
case MSAgentProtocolMessageType.Join: {
let joinMsg = msg as MSAgentJoinMessage;
if (!joinMsg.data || !joinMsg.data.username || !joinMsg.data.username) {
this.socket.close();
return;
}
this.username = htmlentities.encode(joinMsg.data.username);
this.agent = htmlentities.encode(joinMsg.data.agent);
this.emit('join');
break;
}
case MSAgentProtocolMessageType.Talk: {
let talkMsg = msg as MSAgentTalkMessage;
if (!talkMsg.data || !talkMsg.data.msg) {
return;
}
this.emit('talk', htmlentities.encode(talkMsg.data.msg));
break;
}
}

15
server/src/config.ts Normal file
View file

@ -0,0 +1,15 @@
export interface IConfig {
http: {
host: string;
port: number;
}
tts: TTSConfig
}
export interface TTSConfig {
enabled: boolean;
server: string;
voice: string;
tempDir: string;
wavExpirySeconds: number;
}

View file

@ -1,7 +1,32 @@
import Fastify from 'fastify';
import FastifyWS from '@fastify/websocket';
import FastifyStatic from '@fastify/static';
import { Client } from './client.js';
import { MSAgentChatRoom } from './room.js';
import * as toml from 'toml';
import { IConfig } from './config.js';
import * as fs from 'fs';
import { TTSClient } from './tts.js';
let config: IConfig;
let configPath: string;
if (process.argv.length < 3)
configPath = "./config.toml";
else
configPath = process.argv[2];
if (!fs.existsSync(configPath)) {
console.error(`${configPath} not found. Please copy config.example.toml and fill out fields.`);
process.exit(1);
}
try {
let configRaw = fs.readFileSync(configPath, "utf-8");
config = toml.parse(configRaw);
} catch (e) {
console.error(`Failed to read or parse ${configPath}: ${(e as Error).message}`);
process.exit(1);
}
const app = Fastify({
logger: true,
@ -9,16 +34,24 @@ const app = Fastify({
app.register(FastifyWS);
let room = new MSAgentChatRoom();
let tts = null;
if (config.tts.enabled) {
tts = new TTSClient(config.tts);
app.register(FastifyStatic, {
root: config.tts.tempDir,
prefix: "/api/tts/"
});
}
let room = new MSAgentChatRoom(tts);
app.register(async app => {
app.get("/socket", {websocket: true}, (socket, req) => {
let client = new Client(socket, room);
room.addClient(client);
});
});
let port;
if (process.argv.length < 3 || isNaN(port = parseInt(process.argv[2]))) {
console.error("Usage: index.js [port]");
process.exit(1);
}
app.listen({host: "127.0.0.1", port});
app.listen({host: config.http.host, port: config.http.port});

View file

@ -1,11 +1,14 @@
import { MSAgentAddUserMessage, MSAgentInitMessage, MSAgentProtocolMessage, MSAgentProtocolMessageType, MSAgentRemoveUserMessage } from "@msagent-chat/protocol";
import { MSAgentAddUserMessage, MSAgentChatMessage, MSAgentInitMessage, MSAgentProtocolMessage, MSAgentProtocolMessageType, MSAgentRemoveUserMessage } from "@msagent-chat/protocol";
import { Client } from "./client.js";
import { TTSClient } from "./tts.js";
export class MSAgentChatRoom {
clients: Client[];
constructor() {
tts: TTSClient | null;
msgId : number = 0;
constructor(tts: TTSClient | null) {
this.clients = [];
this.tts = tts;
}
addClient(client: Client) {
@ -24,26 +27,47 @@ export class MSAgentChatRoom {
}
});
client.on('join', () => {
client.send(this.getInitMsg());
let initmsg : MSAgentInitMessage = {
op: MSAgentProtocolMessageType.Init,
data: {
username: client.username!,
agent: client.agent!,
users: this.clients.filter(c => c.username !== null).map(c => {
return {
username: c.username!,
agent: c.agent!
}
})
}
};
client.send(initmsg);
let msg: MSAgentAddUserMessage = {
op: MSAgentProtocolMessageType.AddUser,
data: {
username: client.username!
username: client.username!,
agent: client.agent!
}
}
for (const _client of this.getActiveClients().filter(c => c !== client)) {
_client.send(msg);
}
});
}
private getInitMsg(): MSAgentInitMessage {
return {
op: MSAgentProtocolMessageType.Init,
client.on('talk', async message => {
let msg: MSAgentChatMessage = {
op: MSAgentProtocolMessageType.Chat,
data: {
users: this.clients.filter(c => c.username !== null).map(c => c.username!)
username: client.username!,
message
}
};
if (this.tts !== null) {
let filename = await this.tts.synthesizeToFile(message, (++this.msgId).toString(10));
msg.data.audio = "/api/tts/" + filename;
}
for (const _client of this.getActiveClients()) {
_client.send(msg);
}
});
}
private getActiveClients() {

70
server/src/tts.ts Normal file
View file

@ -0,0 +1,70 @@
import path from "path";
import * as fs from 'fs/promises';
import { TTSConfig } from "./config.js";
import { Readable } from "stream";
import { ReadableStream } from 'node:stream/web';
import { finished } from "node:stream/promises";
export class TTSClient {
private config: TTSConfig;
private deleteOps: Map<string, NodeJS.Timeout>
constructor(config: TTSConfig) {
this.config = config;
if (!this.config.tempDir.endsWith('/')) this.config.tempDir += '/';
this.deleteOps = new Map();
}
async ensureDirectoryExists() {
let stat;
try {
stat = await fs.stat(this.config.tempDir);
} catch (e) {
let error = e as NodeJS.ErrnoException;
switch (error.code) {
case "ENOTDIR": {
console.warn("File exists at TTS temp directory path. Unlinking...");
await fs.unlink(this.config.tempDir.substring(0, this.config.tempDir.length - 1));
// intentional fall-through
}
case "ENOENT": {
await fs.mkdir(this.config.tempDir, {recursive: true});
break;
}
default: {
console.error(`Cannot access TTS Temp dir: ${error.message}`);
process.exit(1);
break;
}
}
}
}
async synthesizeToFile(text: string, id: string) : Promise<string> {
this.ensureDirectoryExists();
let wavFilename = id + ".wav"
let wavPath = path.join(this.config.tempDir, wavFilename);
try {
await fs.unlink(wavPath);
} catch {}
let file = await fs.open(wavPath, fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_WRONLY);
let stream = file.createWriteStream();
let res = await fetch(this.config.server + "/api/synthesize", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
text,
voice: this.config.voice
})
});
await finished(Readable.fromWeb(res.body as ReadableStream<any>).pipe(stream));
await file.close();
this.deleteOps.set(wavPath, setTimeout(async () => {
await fs.unlink(wavPath);
this.deleteOps.delete(wavPath);
}, this.config.wavExpirySeconds * 1000));
return wavFilename;
}
}

Binary file not shown.

View file

@ -15,5 +15,8 @@
"run-script-os": "^1.1.6",
"typescript": "^5.5.3"
},
"type": "module"
"type": "module",
"dependencies": {
"nanoevents": "^9.0.0"
}
}

View file

@ -73,6 +73,7 @@ body {
flex-grow: 1;
margin-right: 4px;
margin-left: 4px;
height: 100%;
}
#chatSentBtn {

144
webapp/src/ts/client.ts Normal file
View file

@ -0,0 +1,144 @@
import { createNanoEvents, Emitter, Unsubscribe } from 'nanoevents';
import { MSAgentAddUserMessage, MSAgentChatMessage, MSAgentInitMessage, MSAgentJoinMessage, MSAgentProtocolMessage, MSAgentProtocolMessageType, MSAgentRemoveUserMessage, MSAgentTalkMessage } from '@msagent-chat/protocol';
import { User } from './user';
export interface MSAgentClientEvents {
close: () => void;
join: () => void;
adduser: (user: User) => void;
remuser: (user: User) => void;
chat: (user: User, msg: string) => void;
}
export class MSAgentClient {
private url: string;
private socket: WebSocket | null;
private events: Emitter;
private users: User[];
private username: string | null = null;
private agent: string | null = null;
constructor(url: string) {
this.url = url;
this.socket = null;
this.events = createNanoEvents();
this.users = [];
}
on<E extends keyof MSAgentClientEvents>(event: E, callback: MSAgentClientEvents[E]): Unsubscribe {
return this.events.on(event, callback);
}
connect(): Promise<void> {
return new Promise(res => {
let url = new URL(this.url);
switch (url.protocol) {
case "http:":
url.protocol = "ws:";
break;
case "https":
url.protocol = "wss:";
break;
default:
throw new Error(`Unknown protocol ${url.protocol}`);
}
url.pathname = "/socket"
this.socket = new WebSocket(url);
this.socket.addEventListener('open', () => res());
this.socket.addEventListener('message', e => {
if (e.data instanceof ArrayBuffer) {
// server should not send binary
return;
}
this.handleMessage(e.data);
});
this.socket.addEventListener('close', () => {
this.events.emit('close');
})
});
}
send(msg: MSAgentProtocolMessage) {
if (this.socket === null || this.socket.readyState !== this.socket.OPEN) {
console.error("Tried to send data on a closed or uninitialized socket");
return;
}
let data = JSON.stringify(msg);
this.socket!.send(data);
}
join(username: string, agent: string) {
if (this.socket === null || this.socket.readyState !== this.socket.OPEN) {
throw new Error("Tried to join() on a closed or uninitialized socket");
}
return new Promise<void>(res => {
let msg: MSAgentJoinMessage = {
op: MSAgentProtocolMessageType.Join,
data: {
username,
agent
}
};
let u = this.on('join', () => {
u();
res();
});
this.send(msg);
});
}
talk(msg: string) {
let talkMsg: MSAgentTalkMessage = {
op: MSAgentProtocolMessageType.Talk,
data: {
msg
}
};
this.send(talkMsg);
}
private handleMessage(data: string) {
let msg: MSAgentProtocolMessage;
try {
msg = JSON.parse(data);
} catch (e) {
console.error(`Failed to parse message from server: ${(e as Error).message}`);
return;
}
switch (msg.op) {
case MSAgentProtocolMessageType.Init: {
let initMsg = msg as MSAgentInitMessage;
this.username = initMsg.data.username;
this.agent = initMsg.data.agent;
this.users.push(...initMsg.data.users.map(u => new User(u.username, u.agent)));
this.events.emit('join');
break;
}
case MSAgentProtocolMessageType.AddUser: {
let addUserMsg = msg as MSAgentAddUserMessage
let user = new User(addUserMsg.data.username, addUserMsg.data.agent);
this.events.emit('adduser', user);
break;
}
case MSAgentProtocolMessageType.RemoveUser: {
let remUserMsg = msg as MSAgentRemoveUserMessage;
let user = this.users.find(u => u.username === remUserMsg.data.username);
if (!user) return;
this.users.splice(this.users.indexOf(user), 1);
this.events.emit('remuser', user);
break;
}
case MSAgentProtocolMessageType.Chat: {
let chatMsg = msg as MSAgentChatMessage;
let user = this.users.find(u => u.username === chatMsg.data.username);
this.events.emit('chat', user, chatMsg.data.message);
if (chatMsg.data.audio !== undefined) {
let audio = new Audio(this.url + chatMsg.data.audio);
audio.play();
}
break;
}
}
}
}

View file

@ -1,13 +1,18 @@
import { MSWindow, MSWindowStartPosition } from "./MSWindow.js";
import { agentInit } from "@msagent-chat/msagent.js";
import { MSAgentClient } from "./client.js";
let Room : MSAgentClient | null = null;
const elements = {
logonView: document.getElementById("logonView") as HTMLDivElement,
logonWindow: document.getElementById("logonWindow") as HTMLDivElement,
logonForm: document.getElementById("logonForm") as HTMLFormElement,
logonUsername: document.getElementById("logonUsername") as HTMLInputElement,
chatView: document.getElementById("chatView") as HTMLDivElement,
chatInput: document.getElementById("chatInput") as HTMLInputElement,
chatSendBtn: document.getElementById("chatSendBtn") as HTMLButtonElement
}
let logonWindow = new MSWindow(elements.logonWindow, {
@ -21,7 +26,22 @@ logonWindow.show();
elements.logonForm.addEventListener('submit', e => {
e.preventDefault();
connectToRoom();
});
elements.chatInput.addEventListener('keypress', e => {
// enter
if (e.key === "Enter") talk();
});
elements.chatSendBtn.addEventListener('click', () => {
talk();
});
async function connectToRoom() {
Room = new MSAgentClient("http://127.0.0.1:3000");
await Room.connect();
await Room.join(elements.logonUsername.value, "test");
logonWindow.hide();
elements.logonView.style.display = "none";
elements.chatView.style.display = "block";
@ -30,3 +50,10 @@ elements.logonForm.addEventListener('submit', e => {
document.addEventListener('DOMContentLoaded', async () => {
await agentInit();
});
}
function talk() {
if (Room === null) return;
Room.talk(elements.chatInput.value);
elements.chatInput.value = "";
}

9
webapp/src/ts/user.ts Normal file
View file

@ -0,0 +1,9 @@
export class User {
username: string;
agent: string;
constructor(username: string, agent: string) {
this.username = username;
this.agent = agent;
}
}

147
yarn.lock
View file

@ -34,6 +34,13 @@ __metadata:
languageName: node
linkType: hard
"@fastify/accept-negotiator@npm:^1.0.0":
version: 1.1.0
resolution: "@fastify/accept-negotiator@npm:1.1.0"
checksum: 10c0/1cb9a298c992b812869158ddc6093557a877b30e5f77618a7afea985a0667c50bc7113593bf0f7f9dc9b82b94c16e8ab127a0afc3efde6677fd645539f6d08e5
languageName: node
linkType: hard
"@fastify/ajv-compiler@npm:^3.5.0":
version: 3.6.0
resolution: "@fastify/ajv-compiler@npm:3.6.0"
@ -70,6 +77,33 @@ __metadata:
languageName: node
linkType: hard
"@fastify/send@npm:^2.0.0":
version: 2.1.0
resolution: "@fastify/send@npm:2.1.0"
dependencies:
"@lukeed/ms": "npm:^2.0.1"
escape-html: "npm:~1.0.3"
fast-decode-uri-component: "npm:^1.0.1"
http-errors: "npm:2.0.0"
mime: "npm:^3.0.0"
checksum: 10c0/0e1c10022660fa1f1959b7c414d1be2c47ab42be1da8e21cd72a4df3104c516fdf7b590ee67f897037dd4c85b716fac63929e894d7699623549646604f6db14b
languageName: node
linkType: hard
"@fastify/static@npm:^7.0.4":
version: 7.0.4
resolution: "@fastify/static@npm:7.0.4"
dependencies:
"@fastify/accept-negotiator": "npm:^1.0.0"
"@fastify/send": "npm:^2.0.0"
content-disposition: "npm:^0.5.3"
fastify-plugin: "npm:^4.0.0"
fastq: "npm:^1.17.0"
glob: "npm:^10.3.4"
checksum: 10c0/4ca29a37226880fcc8e711b4a3b93858d4e23dd0458a9f65373b63b3df4768610c8cfbd9e51b749ab52d65b9139142a5c69571740265c9ace1660e268ebb38f6
languageName: node
linkType: hard
"@fastify/websocket@npm:^10.0.1":
version: 10.0.1
resolution: "@fastify/websocket@npm:10.0.1"
@ -153,6 +187,13 @@ __metadata:
languageName: node
linkType: hard
"@lukeed/ms@npm:^2.0.1":
version: 2.0.2
resolution: "@lukeed/ms@npm:2.0.2"
checksum: 10c0/843b922717313bcde4943f478145d8bc13115b9b91d83bbaed53739b5644122984412310aed574792f4e6b492f2cbda178175f601856d310f67e14834c3f17a0
languageName: node
linkType: hard
"@mischnic/json-sourcemap@npm:^0.1.0":
version: 0.1.1
resolution: "@mischnic/json-sourcemap@npm:0.1.1"
@ -164,12 +205,19 @@ __metadata:
languageName: node
linkType: hard
<<<<<<< HEAD
"@msagent-chat/msagent.js@npm:*, @msagent-chat/msagent.js@workspace:msagent.js":
version: 0.0.0-use.local
resolution: "@msagent-chat/msagent.js@workspace:msagent.js"
dependencies:
"@parcel/core": "npm:^2.12.0"
parcel: "npm:^2.12.0"
=======
"@msagent-chat/msagent.js@workspace:msagent.js":
version: 0.0.0-use.local
resolution: "@msagent-chat/msagent.js@workspace:msagent.js"
dependencies:
>>>>>>> 2414658 (working chat and TTS)
typescript: "npm:^5.5.3"
languageName: unknown
linkType: soft
@ -186,10 +234,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@msagent-chat/server@workspace:server"
dependencies:
"@fastify/static": "npm:^7.0.4"
"@fastify/websocket": "npm:^10.0.1"
"@types/node": "npm:^20.14.9"
"@types/ws": "npm:^8.5.10"
fastify: "npm:^4.28.1"
html-entities: "npm:^2.5.2"
toml: "npm:^3.0.0"
typescript: "npm:^5.5.3"
ws: "npm:^8.17.1"
languageName: unknown
@ -201,6 +252,7 @@ __metadata:
dependencies:
"@msagent-chat/msagent.js": "npm:*"
"@parcel/core": "npm:^2.12.0"
nanoevents: "npm:^9.0.0"
parcel: "npm:^2.12.0"
run-script-os: "npm:^1.1.6"
typescript: "npm:^5.5.3"
@ -1660,6 +1712,15 @@ __metadata:
languageName: node
linkType: hard
"content-disposition@npm:^0.5.3":
version: 0.5.4
resolution: "content-disposition@npm:0.5.4"
dependencies:
safe-buffer: "npm:5.2.1"
checksum: 10c0/bac0316ebfeacb8f381b38285dc691c9939bf0a78b0b7c2d5758acadad242d04783cee5337ba7d12a565a19075af1b3c11c728e1e4946de73c6ff7ce45f3f1bb
languageName: node
linkType: hard
"cookie@npm:^0.6.0":
version: 0.6.0
resolution: "cookie@npm:0.6.0"
@ -1746,6 +1807,13 @@ __metadata:
languageName: node
linkType: hard
"depd@npm:2.0.0":
version: 2.0.0
resolution: "depd@npm:2.0.0"
checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c
languageName: node
linkType: hard
"detect-libc@npm:^1.0.3":
version: 1.0.3
resolution: "detect-libc@npm:1.0.3"
@ -1916,6 +1984,13 @@ __metadata:
languageName: node
linkType: hard
"escape-html@npm:~1.0.3":
version: 1.0.3
resolution: "escape-html@npm:1.0.3"
checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3
languageName: node
linkType: hard
"escape-string-regexp@npm:^1.0.5":
version: 1.0.5
resolution: "escape-string-regexp@npm:1.0.5"
@ -2034,7 +2109,7 @@ __metadata:
languageName: node
linkType: hard
"fastq@npm:^1.17.1":
"fastq@npm:^1.17.0, fastq@npm:^1.17.1":
version: 1.17.1
resolution: "fastq@npm:1.17.1"
dependencies:
@ -2105,7 +2180,7 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^10.2.2, glob@npm:^10.3.10":
"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.4":
version: 10.4.2
resolution: "glob@npm:10.4.2"
dependencies:
@ -2151,6 +2226,13 @@ __metadata:
languageName: node
linkType: hard
"html-entities@npm:^2.5.2":
version: 2.5.2
resolution: "html-entities@npm:2.5.2"
checksum: 10c0/f20ffb4326606245c439c231de40a7c560607f639bf40ffbfb36b4c70729fd95d7964209045f1a4e62fe17f2364cef3d6e49b02ea09016f207fde51c2211e481
languageName: node
linkType: hard
"htmlnano@npm:^2.0.0":
version: 2.1.1
resolution: "htmlnano@npm:2.1.1"
@ -2207,6 +2289,19 @@ __metadata:
languageName: node
linkType: hard
"http-errors@npm:2.0.0":
version: 2.0.0
resolution: "http-errors@npm:2.0.0"
dependencies:
depd: "npm:2.0.0"
inherits: "npm:2.0.4"
setprototypeof: "npm:1.2.0"
statuses: "npm:2.0.1"
toidentifier: "npm:1.0.1"
checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19
languageName: node
linkType: hard
"http-proxy-agent@npm:^7.0.0":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
@ -2267,7 +2362,7 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:^2.0.3":
"inherits@npm:2.0.4, inherits@npm:^2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
@ -2623,6 +2718,15 @@ __metadata:
languageName: node
linkType: hard
"mime@npm:^3.0.0":
version: 3.0.0
resolution: "mime@npm:3.0.0"
bin:
mime: cli.js
checksum: 10c0/402e792a8df1b2cc41cb77f0dcc46472b7944b7ec29cb5bbcd398624b6b97096728f1239766d3fdeb20551dd8d94738344c195a6ea10c4f906eb0356323b0531
languageName: node
linkType: hard
"minimatch@npm:^9.0.4":
version: 9.0.5
resolution: "minimatch@npm:9.0.5"
@ -2785,6 +2889,13 @@ __metadata:
languageName: node
linkType: hard
"nanoevents@npm:^9.0.0":
version: 9.0.0
resolution: "nanoevents@npm:9.0.0"
checksum: 10c0/af74c96d0d54ef0ddf6fd163d961b23ec4219ac51d0b70135a2cae49fac1107ddccdcbf7a120601e0bfdd38b15de811f3ce43da5bab967fdee9684e52e4cb5b7
languageName: node
linkType: hard
"negotiator@npm:^0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
@ -3253,7 +3364,7 @@ __metadata:
languageName: node
linkType: hard
"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
@ -3306,6 +3417,13 @@ __metadata:
languageName: node
linkType: hard
"setprototypeof@npm:1.2.0":
version: 1.2.0
resolution: "setprototypeof@npm:1.2.0"
checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc
languageName: node
linkType: hard
"shebang-command@npm:^2.0.0":
version: 2.0.0
resolution: "shebang-command@npm:2.0.0"
@ -3410,6 +3528,13 @@ __metadata:
languageName: node
linkType: hard
"statuses@npm:2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0
languageName: node
linkType: hard
"stream-shift@npm:^1.0.2":
version: 1.0.3
resolution: "stream-shift@npm:1.0.3"
@ -3554,6 +3679,20 @@ __metadata:
languageName: node
linkType: hard
"toidentifier@npm:1.0.1":
version: 1.0.1
resolution: "toidentifier@npm:1.0.1"
checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1
languageName: node
linkType: hard
"toml@npm:^3.0.0":
version: 3.0.0
resolution: "toml@npm:3.0.0"
checksum: 10c0/8d7ed3e700ca602e5419fca343e1c595eb7aa177745141f0761a5b20874b58ee5c878cd045c408da9d130cb2b611c639912210ba96ce2f78e443569aa8060c18
languageName: node
linkType: hard
"tslib@npm:^2.4.0":
version: 2.6.3
resolution: "tslib@npm:2.6.3"