diff --git a/codex-cli/server.py b/codex-cli/server.py new file mode 100644 index 000000000..9a6f84032 --- /dev/null +++ b/codex-cli/server.py @@ -0,0 +1,72 @@ +from enum import Enum +from typing import Literal, Optional +import socketio +from fastapi import FastAPI +from pydantic import BaseModel, ValidationError +from rich.console import Console +from rich.prompt import Prompt +import asyncio + +console = Console() + + + +class Decision(str, Enum): + YES = "yes" + NO_CONTINUE = "no-continue" + NO_EXIT = "no-exit" + EXPLAIN = "explain" + +class PermissionRequest(BaseModel): + type: Literal["permission-request"] = "permission-request" + agentId: str + message: str + + +class PermissionResponse(BaseModel): + type: Literal["permission-response"] = "permission-response" + agentId: str + decision: Decision + customDenyMessage: Optional[str] = None + + class Config: + use_enum_values = True + + +# socket.io + FastAPI setup +sio = socketio.AsyncServer( + cors_allowed_origins="*", + async_mode="asgi", + # logger=True, + # engineio_logger=True, + ping_interval=25, + ping_timeout=60, +) +app = FastAPI() +app.mount("/", socketio.ASGIApp(sio, socketio_path="socket.io")) + + +@sio.event +async def connect(sid, environ): + console.log(f"[green]Client connected:[/green] {sid}") + + +@sio.on("permission_request") +async def on_permission_request(sid, data): + try: + req = PermissionRequest.parse_obj(data) + except ValidationError as exc: + console.log(f"[red]Invalid request from {sid}[/red]: {exc}") + return + + console.print(f"🟑 New request from [bold]{req.agentId}[/bold]: {req.message}") + + # Run Prompt.ask in a thread + choice = await asyncio.to_thread(Prompt.ask, "Reply", choices=[d.value for d in Decision], show_choices=True) + + custom_msg = None + if choice == Decision.NO_CONTINUE.value: + custom_msg = (await asyncio.to_thread(Prompt.ask, "Custom deny message", default="")).strip() or None + + resp = PermissionResponse(agentId=req.agentId, decision=Decision(choice), customDenyMessage=custom_msg) + await sio.emit("permission_response", resp.dict(exclude_none=True), room=sid) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index c7e5d9ff3..fe2b0093b 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -19,7 +19,7 @@ if (major < 22) { (process as any).noDeprecation = true; import type { AppRollout } from "./app"; -import type { ApprovalPolicy } from "./approvals"; +import type { ApplyPatchCommand, ApprovalPolicy } from "./approvals"; import type { CommandConfirmation } from "./utils/agent/agent-loop"; import type { AppConfig } from "./utils/config"; import type { ResponseItem } from "openai/resources/responses/responses"; @@ -28,7 +28,9 @@ import type { ReasoningEffort } from "openai/resources.mjs"; import App from "./app"; import { runSinglePass } from "./cli-singlepass"; import SessionsOverlay from "./components/sessions-overlay.js"; +import { requestRemotePermission } from "./permission-client"; import { AgentLoop } from "./utils/agent/agent-loop"; +import { generateCommandExplanation } from "./utils/agent/generate-explanation"; import { ReviewDecision } from "./utils/agent/review"; import { AutoApprovalMode } from "./utils/auto-approval-mode"; import { checkForUpdates } from "./utils/check-updates"; @@ -111,11 +113,17 @@ const cli = meow( -f, --full-context Launch in "full-context" mode which loads the entire repository into context and applies a batch of edits in one go. Incompatible with all other flags, except for --model. + -r, --remote-permission Send permission requests to a remote server. + Requires --permission-server-url and --agent-id flags. + --permission-server-url + URL of the remote server to send permission requests to. + --agent-id Agent ID to use for remote permission requests. Examples $ codex "Write and run a python program that prints ASCII art" $ codex -q "fix build issues" $ codex completion bash + $ codex -r --permission-server-url http://localhost:3000 "Create a CI workflow that runs ESLint on every PR" `, { importMeta: import.meta, @@ -213,6 +221,20 @@ const cli = meow( description: `Run in full-context editing approach. The model is given the whole code directory as context and performs changes in one go without acting.`, }, + + remotePermission: { + type: "boolean", + aliases: ["r"], + description: "Send permission requests to a remote server. ", + }, + permissionServerUrl: { + type: "string", + description: "URL of the remote server to send permission requests to.", + }, + agentId: { + type: "string", + description: "Agent ID to use for remote permission requests.", + }, }, }, ); @@ -572,6 +594,43 @@ const approvalPolicy: ApprovalPolicy = ? AutoApprovalMode.AUTO_EDIT : config.approvalMode || AutoApprovalMode.SUGGEST; +if (cli.flags.remotePermission) { + if (!cli.flags.permissionServerUrl) { + // eslint-disable-next-line no-console + console.error( + "The --remote-permission flag requires the --permission-server-url flag to be set.", + ); + process.exit(1); + } + if (!cli.flags.agentId) { + // We require an agent ID to send remote permission requests, since permission server + // might be dealing with multiple agents. + // eslint-disable-next-line no-console + console.error( + "The --remote-permission flag requires the --agent-id flag to be set.", + ); + process.exit(1); + } + if (!prompt || prompt.trim() === "") { + // eslint-disable-next-line no-console + console.error( + 'Remote permission mode requires a prompt string, e.g.,: codex -r http://localhost:8080 "Find and fix bugs"', + ); + process.exit(1); + } + await runRemoteMode({ + agentId: cli.flags.agentId, + url: cli.flags.permissionServerUrl, + prompt, + imagePaths: imagePaths || [], + approvalPolicy, + additionalWritableRoots, + config, + }); + onExit(); + process.exit(0); +} + const instance = render( ; + approvalPolicy: ApprovalPolicy; + additionalWritableRoots: ReadonlyArray; + config: AppConfig; +}): Promise { + const agent = new AgentLoop({ + model: config.model, + config: config, + instructions: config.instructions, + provider: config.provider, + approvalPolicy, + additionalWritableRoots, + disableResponseStorage: config.disableResponseStorage, + onItem: (item: ResponseItem) => { + /* Could also send over the wire */ + // eslint-disable-next-line no-console + console.log(formatResponseItemForQuietMode(item)); + }, + onLoading: () => { + /* intentionally ignored in socket mode */ + }, + onLastResponseId: () => { + /* intentionally ignored in socket mode */ + }, + getCommandConfirmation: async ( + command: Array, + applyPatch: ApplyPatchCommand | undefined, + ): Promise => { + // First request for confirmation + let { decision: review, customDenyMessage } = + await requestRemotePermission( + agentId, + url, + `command: ${command.join(" ")}`, + ); + + // If the user wants an explanation, generate one and ask again. + if (review === ReviewDecision.EXPLAIN) { + const explanation = await generateCommandExplanation( + command, + config.model, + Boolean(config.flexMode), + config, + ); + // Ask for confirmation again, but with the explanation. + const confirmResult = await requestRemotePermission( + url, + `command: ${command.join(" ")}}`, + explanation, + ); + + // Update the decision based on the second confirmation. + review = confirmResult.decision; + customDenyMessage = confirmResult.customDenyMessage; + + // Return the final decision with the explanation. + return { review, customDenyMessage, applyPatch, explanation }; + } + return { review, customDenyMessage, applyPatch }; + }, + }); + + const inputItem = await createInputItem(prompt, imagePaths); + await agent.run([inputItem]); +} + const exit = () => { onExit(); process.exit(0); diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index d41a94990..9e9901015 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -13,6 +13,7 @@ import { formatCommandForDisplay } from "../../format-command.js"; import { useConfirmation } from "../../hooks/use-confirmation.js"; import { useTerminalSize } from "../../hooks/use-terminal-size.js"; import { AgentLoop } from "../../utils/agent/agent-loop.js"; +import { generateCommandExplanation } from "../../utils/agent/generate-explanation.js"; import { ReviewDecision } from "../../utils/agent/review.js"; import { generateCompactSummary } from "../../utils/compact-summary.js"; import { saveConfig } from "../../utils/config.js"; @@ -25,7 +26,6 @@ import { calculateContextPercentRemaining, uniqueById, } from "../../utils/model-utils.js"; -import { createOpenAIClient } from "../../utils/openai-client.js"; import { shortCwd } from "../../utils/short-path.js"; import { saveRollout } from "../../utils/storage/save-rollout.js"; import { CLI_VERSION } from "../../version.js"; @@ -66,76 +66,6 @@ const colorsByPolicy: Record = { "full-auto": "green", }; -/** - * Generates an explanation for a shell command using the OpenAI API. - * - * @param command The command to explain - * @param model The model to use for generating the explanation - * @param flexMode Whether to use the flex-mode service tier - * @param config The configuration object - * @returns A human-readable explanation of what the command does - */ -async function generateCommandExplanation( - command: Array, - model: string, - flexMode: boolean, - config: AppConfig, -): Promise { - try { - // Create a temporary OpenAI client - const oai = createOpenAIClient(config); - - // Format the command for display - const commandForDisplay = formatCommandForDisplay(command); - - // Create a prompt that asks for an explanation with a more detailed system prompt - const response = await oai.chat.completions.create({ - model, - ...(flexMode ? { service_tier: "flex" } : {}), - messages: [ - { - role: "system", - content: - "You are an expert in shell commands and terminal operations. Your task is to provide detailed, accurate explanations of shell commands that users are considering executing. Break down each part of the command, explain what it does, identify any potential risks or side effects, and explain why someone might want to run it. Be specific about what files or systems will be affected. If the command could potentially be harmful, make sure to clearly highlight those risks.", - }, - { - role: "user", - content: `Please explain this shell command in detail: \`${commandForDisplay}\`\n\nProvide a structured explanation that includes:\n1. A brief overview of what the command does\n2. A breakdown of each part of the command (flags, arguments, etc.)\n3. What files, directories, or systems will be affected\n4. Any potential risks or side effects\n5. Why someone might want to run this command\n\nBe specific and technical - this explanation will help the user decide whether to approve or reject the command.`, - }, - ], - }); - - // Extract the explanation from the response - const explanation = - response.choices[0]?.message.content || "Unable to generate explanation."; - return explanation; - } catch (error) { - log(`Error generating command explanation: ${error}`); - - let errorMessage = "Unable to generate explanation due to an error."; - if (error instanceof Error) { - errorMessage = `Unable to generate explanation: ${error.message}`; - - // If it's an API error, check for more specific information - if ("status" in error && typeof error.status === "number") { - // Handle API-specific errors - if (error.status === 401) { - errorMessage = - "Unable to generate explanation: API key is invalid or expired."; - } else if (error.status === 429) { - errorMessage = - "Unable to generate explanation: Rate limit exceeded. Please try again later."; - } else if (error.status >= 500) { - errorMessage = - "Unable to generate explanation: OpenAI service is currently unavailable. Please try again later."; - } - } - } - - return errorMessage; - } -} - export default function TerminalChat({ config, prompt: _initialPrompt, diff --git a/codex-cli/src/hooks/use-confirmation.ts b/codex-cli/src/hooks/use-confirmation.ts index 07c9e75cd..50ee10619 100644 --- a/codex-cli/src/hooks/use-confirmation.ts +++ b/codex-cli/src/hooks/use-confirmation.ts @@ -3,7 +3,7 @@ import type React from "react"; import { useState, useCallback, useRef } from "react"; -type ConfirmationResult = { +export type ConfirmationResult = { decision: ReviewDecision; customDenyMessage?: string; }; diff --git a/codex-cli/src/permission-client.ts b/codex-cli/src/permission-client.ts new file mode 100644 index 000000000..4598d6ecb --- /dev/null +++ b/codex-cli/src/permission-client.ts @@ -0,0 +1,125 @@ +import type { ConfirmationResult } from "./hooks/use-confirmation"; +import type { Socket } from "socket.io-client"; + +import { ReviewDecision } from "./utils/agent/review"; +import { io } from "socket.io-client"; + +/** ---- internal singleton socket & helpers -------------------------------- */ + +type PermissionRequestPayload = { + type: "permission-request"; + agentId: string; + message: string; +}; + +type PermissionResponsePayload = ConfirmationResult & { + type: "permission-response"; + agentId: string; +}; + +let socket: Socket | null = null; +let activeRequest: { + agentId: string; + resolve: (value: ConfirmationResult) => void; + reject: (reason?: Error) => void; +} | null = null; + +function getSocket(serverUrl: string): Socket { + if (socket) { + return socket; + } + + socket = io(serverUrl, { + path: "/socket.io", + autoConnect: true, + reconnection: true, + reconnectionAttempts: Infinity, + reconnectionDelay: 1000, + reconnectionDelayMax: 30000, + }); + + socket.on("disconnect", () => { + if (activeRequest) { + activeRequest.reject( + new Error("Socket disconnected before permission response."), + ); + activeRequest = null; + } + }); + + socket.on("error", (err) => { + if (activeRequest) { + activeRequest.reject(new Error(`Socket error: ${err?.message ?? err}`)); + activeRequest = null; + } + }); + + socket.on("permission_response", (data: PermissionResponsePayload): void => { + if (!activeRequest || activeRequest.agentId !== data.agentId) { + return; + } + + if (!Object.values(ReviewDecision).includes(data.decision)) { + activeRequest.reject( + new Error( + `Unexpected decision value from permission server: ${data.decision}. Expected one of: ${Object.values(ReviewDecision).join(", ")}`, + ), + ); + } else { + activeRequest.resolve({ + decision: data.decision, + customDenyMessage: data.customDenyMessage, + }); + } + + activeRequest = null; // Clear active request + }); + + return socket; +} + +function waitUntilConnected(sock: Socket): Promise { + return sock.connected + ? Promise.resolve() + : new Promise((res) => sock.once("connect", () => res())); +} + +/** ---- public API --------------------------------------------------------- */ + +export async function requestRemotePermission( + agentId: string, + serverUrl: string, + prompt: string, + explanation?: string, +): Promise { + const fullMessage = explanation ? `${prompt}\n\n${explanation}` : prompt; + + if (activeRequest) { + return Promise.reject( + new Error( + "A permission request is already active. Wait for it to complete before making another.", + ), + ); + } + + const sock = getSocket(serverUrl); + await waitUntilConnected(sock); + + return new Promise((resolve, reject) => { + activeRequest = { agentId, resolve, reject }; + + try { + sock.emit("permission_request", { + agentId, + message: fullMessage, + } as PermissionRequestPayload); + } catch (err) { + activeRequest = null; + reject( + new Error( + `Failed to send permission request: ${(err as Error).message}`, + ), + ); + } + }); +} diff --git a/codex-cli/src/utils/agent/generate-explanation.ts b/codex-cli/src/utils/agent/generate-explanation.ts new file mode 100644 index 000000000..33e8041d8 --- /dev/null +++ b/codex-cli/src/utils/agent/generate-explanation.ts @@ -0,0 +1,75 @@ +import type { AppConfig } from "../config"; + +import { formatCommandForDisplay } from "../../format-command"; +import { log } from "../../utils/logger/log.js"; +import { createOpenAIClient } from "../openai-client"; + +/** + * Generates an explanation for a shell command using the OpenAI API. + * + * @param command The command to explain + * @param model The model to use for generating the explanation + * @param flexMode Whether to use the flex-mode service tier + * @param config The configuration object + * @returns A human-readable explanation of what the command does + */ +export async function generateCommandExplanation( + command: Array, + model: string, + flexMode: boolean, + config: AppConfig, +): Promise { + try { + // Create a temporary OpenAI client + const oai = createOpenAIClient(config); + + // Format the command for display + const commandForDisplay = formatCommandForDisplay(command); + + // Create a prompt that asks for an explanation with a more detailed system prompt + const response = await oai.chat.completions.create({ + model, + ...(flexMode ? { service_tier: "flex" } : {}), + messages: [ + { + role: "system", + content: + "You are an expert in shell commands and terminal operations. Your task is to provide detailed, accurate explanations of shell commands that users are considering executing. Break down each part of the command, explain what it does, identify any potential risks or side effects, and explain why someone might want to run it. Be specific about what files or systems will be affected. If the command could potentially be harmful, make sure to clearly highlight those risks.", + }, + { + role: "user", + content: `Please explain this shell command in detail: \`${commandForDisplay}\`\n\nProvide a structured explanation that includes:\n1. A brief overview of what the command does\n2. A breakdown of each part of the command (flags, arguments, etc.)\n3. What files, directories, or systems will be affected\n4. Any potential risks or side effects\n5. Why someone might want to run this command\n\nBe specific and technical - this explanation will help the user decide whether to approve or reject the command.`, + }, + ], + }); + + // Extract the explanation from the response + const explanation = + response.choices[0]?.message.content || "Unable to generate explanation."; + return explanation; + } catch (error) { + log(`Error generating command explanation: ${error}`); + + let errorMessage = "Unable to generate explanation due to an error."; + if (error instanceof Error) { + errorMessage = `Unable to generate explanation: ${error.message}`; + + // If it's an API error, check for more specific information + if ("status" in error && typeof error.status === "number") { + // Handle API-specific errors + if (error.status === 401) { + errorMessage = + "Unable to generate explanation: API key is invalid or expired."; + } else if (error.status === 429) { + errorMessage = + "Unable to generate explanation: Rate limit exceeded. Please try again later."; + } else if (error.status >= 500) { + errorMessage = + "Unable to generate explanation: OpenAI service is currently unavailable. Please try again later."; + } + } + } + + return errorMessage; + } +} diff --git a/codex-cli/tests/permission-client.test.ts b/codex-cli/tests/permission-client.test.ts new file mode 100644 index 000000000..24bab8163 --- /dev/null +++ b/codex-cli/tests/permission-client.test.ts @@ -0,0 +1,218 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { ReviewDecision } from "../src/utils/agent/review"; + +/* ---------------------------------------------------------------- *\ + ──── HELPER: minimal in-memory socket stub ─────────────────────── +\* ---------------------------------------------------------------- */ + +type Handler = (data?: unknown) => void; + +/** + * Creates a fresh mock socket that is β€œclose enough” to what + * socket.io-client gives us for the purposes of these tests. + */ +function createMockSocket(connected = true) { + const handlers: Record> = {}; + + const sock = { + /* state ------------------------------------------------------- */ + connected, + + /* event registration ----------------------------------------- */ + on: (event: string, cb: Handler) => { + (handlers[event] ||= []).push(cb); + return sock; + }, + once: (event: string, cb: Handler) => { + const wrapper: Handler = (d) => { + cb(d); + handlers[event] = handlers[event]!.filter((h) => h !== wrapper); + }; + return sock.on(event, wrapper); + }, + + /* outbound ---------------------------------------------------- */ + emit: vi.fn(), + + /* test-only helpers ------------------------------------------ */ + _fire(event: string, data?: unknown) { + handlers[event]?.forEach((h) => h(data)); + }, + _connect() { + sock.connected = true; + sock._fire("connect"); + }, + }; + + return sock; +} + +const flushMicrotasks = () => Promise.resolve(); + +/* ---------------------------------------------------------------- *\ + ──── MODULE Mocks (socket.io-client & ReviewDecision enum) ─────── +\* ---------------------------------------------------------------- */ +let socketInstance: ReturnType; +let ioMock: ReturnType; + +vi.mock("socket.io-client", () => { + ioMock = vi.fn(() => socketInstance); + return { io: ioMock }; +}); + +/* ---------------------------------------------------------------- *\ + ──── Tests ─────────────────────────────────────────────────────── +\* ---------------------------------------------------------------- */ + +describe("requestRemotePermission", () => { + // Module under test – imported *after* the mocks. + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + let requestRemotePermission: typeof import("../src/permission-client").requestRemotePermission; + beforeEach(async () => { + // Fresh socket for every test so listeners don’t bleed over. + socketInstance = createMockSocket(true); + vi.resetModules(); // make the SUT pick up the fresh mocks + + ({ requestRemotePermission } = await import("../src/permission-client")); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it("sends the correct payload and resolves on a valid server response", async () => { + const promise = requestRemotePermission( + "agent-1", + "http://perm-server", + "Let me do the thing", + ); + await flushMicrotasks(); + + // grab the payload we just emitted… + expect(socketInstance.emit).toHaveBeenCalledWith("permission_request", { + agentId: "agent-1", + message: "Let me do the thing", + }); + + // Simulate the server telling us ReviewDecision.YES,” + socketInstance._fire("permission_response", { + type: "permission-response", + agentId: "agent-1", + decision: ReviewDecision.YES, + customDenyMessage: "", + }); + + await expect(promise).resolves.toEqual({ + decision: ReviewDecision.YES, + customDenyMessage: "", + }); + }); + + it("waits for the socket to connect when it isn’t yet connected", async () => { + // Start disconnected β†’ requestRemotePermission must wait for "connect" + socketInstance = createMockSocket(false); + + const { requestRemotePermission: coldReq } = await import( + "../src/permission-client" + ); + + const p = coldReq("agent-2", "http://server", "hi"); + + await flushMicrotasks(); + + // Nothing should be emitted until we’re connected + expect(socketInstance.emit).not.toHaveBeenCalled(); + + // Simulate transport connection + socketInstance._connect(); + + await flushMicrotasks(); + + // Now emit + respond + expect(socketInstance.emit).toHaveBeenCalled(); + + socketInstance._fire("permission_response", { + type: "permission-response", + agentId: "agent-2", + decision: ReviewDecision.YES, + customDenyMessage: "", + }); + + await expect(p).resolves.toMatchObject({ decision: ReviewDecision.YES }); + }); + + it("rejects if a second request is made while one is already pending", async () => { + const first = requestRemotePermission("agent-1", "url", "prompt 1"); + await flushMicrotasks(); + + await expect( + requestRemotePermission("agent-2", "url", "prompt 2"), + ).rejects.toThrow(/already active/i); + + // Clean-up: finish the first request so leak checking in Vitest passes + socketInstance._fire("permission_response", { + type: "permission-response", + agentId: "agent-1", + decision: ReviewDecision.YES, + customDenyMessage: "", + }); + await first; + }); + + it("rejects when the server returns an unknown decision value", async () => { + const p = requestRemotePermission("agent-x", "url", "prompt"); + + // Wait until requestRemotePermission has passed the `await waitUntilConnected(...)` + // and actually set activeRequest: + await flushMicrotasks(); + + socketInstance._fire("permission_response", { + type: "permission-response", + agentId: "agent-x", + decision: "totally-unexpected", + customDenyMessage: "", + }); + + await expect(p).rejects.toThrow(/Unexpected decision value/i); + }); + + it("propagates an error when socket.emit throws", async () => { + socketInstance.emit.mockImplementation(() => { + throw new Error("socket down"); + }); + + await expect( + requestRemotePermission("agent-1", "url", "prompt"), + ).rejects.toThrow(/socket down/); + }); + + it("re-uses the same socket instance across multiple calls", async () => { + const p1 = requestRemotePermission("a1", "url", "p1"); + + // wait one microtask so that `activeRequest` is set before we fire the response: + await flushMicrotasks(); + + socketInstance._fire("permission_response", { + type: "permission-response", + agentId: "a1", + decision: ReviewDecision.YES, + customDenyMessage: "", + }); + await p1; + + // Second call should reuse the same socket instance (i.e. ioMock called only once) + const p2 = requestRemotePermission("a2", "url", "p2"); + expect(ioMock).toHaveBeenCalledTimes(1); + + // again wait one microtask so that activeRequest is set for the second request + await flushMicrotasks(); + + socketInstance._fire("permission_response", { + type: "permission-response", + agentId: "a2", + decision: ReviewDecision.YES, + customDenyMessage: "", + }); + await p2; + }); +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..5a87a4b0f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,861 @@ +{ + "name": "codex-monorepo", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codex-monorepo", + "dependencies": { + "socket.io-client": "^4.8.1" + }, + "devDependencies": { + "git-cliff": "^2.8.0", + "husky": "^9.1.7", + "lint-staged": "^15.5.1", + "prettier": "^3.5.3" + }, + "engines": { + "node": ">=22", + "pnpm": ">=9.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-cliff": { + "version": "2.8.0", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "execa": "^8.0.1" + }, + "bin": { + "git-cliff": "lib/cli/cli.js" + }, + "engines": { + "node": ">=18.19 || >=20.6 || >=21" + }, + "optionalDependencies": { + "git-cliff-darwin-arm64": "2.8.0", + "git-cliff-darwin-x64": "2.8.0", + "git-cliff-linux-arm64": "2.8.0", + "git-cliff-linux-x64": "2.8.0", + "git-cliff-windows-arm64": "2.8.0", + "git-cliff-windows-x64": "2.8.0" + } + }, + "node_modules/git-cliff-darwin-arm64": { + "version": "2.8.0", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "15.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/yaml": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json index 9d45c6e38..32e5656e3 100644 --- a/package.json +++ b/package.json @@ -48,5 +48,8 @@ "cd codex-cli && pnpm run typecheck" ] }, - "packageManager": "pnpm@10.8.1" + "packageManager": "pnpm@10.8.1", + "dependencies": { + "socket.io-client": "^4.8.1" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e4db6952..0628fa752 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,10 @@ patchedDependencies: importers: .: + dependencies: + socket.io-client: + specifier: ^4.8.1 + version: 4.8.1 devDependencies: git-cliff: specifier: ^2.8.0 @@ -525,6 +529,9 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} engines: {node: '>=18'} @@ -1006,6 +1013,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -1098,6 +1114,13 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -2299,6 +2322,14 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2678,6 +2709,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.1: resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} @@ -2690,6 +2733,10 @@ packages: utf-8-validate: optional: true + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2940,6 +2987,8 @@ snapshots: '@sindresorhus/is@4.6.0': {} + '@socket.io/component-emitter@3.1.2': {} + '@tokenizer/inflate@0.2.7': dependencies: debug: 4.4.0 @@ -3490,6 +3539,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.3.7: + dependencies: + ms: 2.1.3 + debug@4.4.0: dependencies: ms: 2.1.3 @@ -3559,6 +3612,20 @@ snapshots: encodeurl@2.0.0: {} + engine.io-client@6.6.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + environment@1.1.0: {} es-abstract@1.23.9: @@ -4985,6 +5052,24 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + socket.io-client@4.8.1: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + source-map-js@1.2.1: {} stack-utils@2.0.6: @@ -5399,8 +5484,12 @@ snapshots: wrappy@1.0.2: {} + ws@8.17.1: {} + ws@8.18.1: {} + xmlhttprequest-ssl@2.1.2: {} + y18n@5.0.8: {} yaml@2.7.1: {}