diff --git a/desktop/main.mjs b/desktop/main.mjs index 2474295..8a79aa3 100644 --- a/desktop/main.mjs +++ b/desktop/main.mjs @@ -203,7 +203,7 @@ async function handleListPathContent(event, uri, options) { return await listPathContent(uri, options); } -async function handleHasFile(event, path) { +async function handleHasPath(event, path) { return fs.existsSync(path); } @@ -278,7 +278,7 @@ app.whenReady().then(() => { ipcMain.handle("rename", handleRename); ipcMain.handle("delete", handleDelete); - ipcMain.handle("has-file", handleHasFile); + ipcMain.handle("has-path", handleHasPath); ipcMain.handle("read-file", handleReadFile); ipcMain.handle("write-file", handleWriteFile); diff --git a/desktop/preload.mjs b/desktop/preload.mjs index 42c4b5b..22c411b 100644 --- a/desktop/preload.mjs +++ b/desktop/preload.mjs @@ -16,7 +16,7 @@ contextBridge.exposeInMainWorld("electronAPI", { rename: (oldUri, newUri) => ipcRenderer.invoke("rename", oldUri, newUri), delete: (uri) => ipcRenderer.invoke("delete", uri), - hasFile: (path) => ipcRenderer.invoke("has-file", path), + hasPath: (path) => ipcRenderer.invoke("has-path", path), readFile: (path) => ipcRenderer.invoke("read-file", path), writeFile: (data, path) => ipcRenderer.invoke("write-file", data, path), diff --git a/npm-packages/cli/package-lock.json b/npm-packages/cli/package-lock.json index 227c555..bd6b655 100644 --- a/npm-packages/cli/package-lock.json +++ b/npm-packages/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pulse-editor/cli", - "version": "0.1.0-beta.1", + "version": "0.1.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pulse-editor/cli", - "version": "0.1.0-beta.1", + "version": "0.1.0-beta.2", "license": "MIT", "dependencies": { "execa": "^9.6.0", diff --git a/npm-packages/cli/source/components/commands/create.tsx b/npm-packages/cli/source/components/commands/create.tsx index 9198468..4ff67f8 100644 --- a/npm-packages/cli/source/components/commands/create.tsx +++ b/npm-packages/cli/source/components/commands/create.tsx @@ -97,7 +97,7 @@ export default function Create({cli}: {cli: Result}) { , ); try { - await $`git clone --depth 1 https://github.com/ClayPulse/pulse-editor-extension-template.git ${name}`; + await $`git clone --depth 1 https://github.com/ClayPulse/pulse-app-template.git ${name}`; } catch (error) { setCreateMessage( diff --git a/npm-packages/react-api/src/hooks/agent/use-agents.ts b/npm-packages/react-api/src/hooks/agent/use-agents.ts index 4b589dd..d506399 100644 --- a/npm-packages/react-api/src/hooks/agent/use-agents.ts +++ b/npm-packages/react-api/src/hooks/agent/use-agents.ts @@ -27,7 +27,7 @@ export default function useAgents() { const result = await imc .sendMessage( - IMCMessageTypeEnum.RunAgentMethod, + IMCMessageTypeEnum.EditorRunAgentMethod, { agentName, methodName, diff --git a/npm-packages/react-api/src/hooks/modality/use-image-gen.ts b/npm-packages/react-api/src/hooks/ai-modality/use-image-gen.ts similarity index 96% rename from npm-packages/react-api/src/hooks/modality/use-image-gen.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-image-gen.ts index 14c1698..854264d 100644 --- a/npm-packages/react-api/src/hooks/modality/use-image-gen.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-image-gen.ts @@ -38,7 +38,7 @@ export default function useImageGen() { } const result = await imc - .sendMessage(IMCMessageTypeEnum.UseImageGen, { + .sendMessage(IMCMessageTypeEnum.ModalityImageGen, { textPrompt, imagePrompt, imageModelConfig, diff --git a/npm-packages/react-api/src/hooks/modality/use-llm.ts b/npm-packages/react-api/src/hooks/ai-modality/use-llm.ts similarity index 93% rename from npm-packages/react-api/src/hooks/modality/use-llm.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-llm.ts index fac4c51..17f9df3 100644 --- a/npm-packages/react-api/src/hooks/modality/use-llm.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-llm.ts @@ -23,7 +23,7 @@ export default function useLLM() { } const result = await imc - .sendMessage(IMCMessageTypeEnum.UseLLM, { + .sendMessage(IMCMessageTypeEnum.ModalityLLM, { prompt, llmConfig, }) diff --git a/npm-packages/react-api/src/hooks/modality/use-ocr.ts b/npm-packages/react-api/src/hooks/ai-modality/use-ocr.ts similarity index 89% rename from npm-packages/react-api/src/hooks/modality/use-ocr.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-ocr.ts index 36e4496..6c8f8a3 100644 --- a/npm-packages/react-api/src/hooks/modality/use-ocr.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-ocr.ts @@ -21,7 +21,7 @@ export default function useOCR() { } // Send the message to the extension - const result = await imc.sendMessage(IMCMessageTypeEnum.UseOCR, { image }); + const result = await imc.sendMessage(IMCMessageTypeEnum.ModalityOCR, { image }); return result.payload.text; } diff --git a/npm-packages/react-api/src/hooks/modality/use-speech2speech.ts b/npm-packages/react-api/src/hooks/ai-modality/use-speech2speech.ts similarity index 94% rename from npm-packages/react-api/src/hooks/modality/use-speech2speech.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-speech2speech.ts index 5a56d96..0281f5c 100644 --- a/npm-packages/react-api/src/hooks/modality/use-speech2speech.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-speech2speech.ts @@ -6,7 +6,7 @@ import { useState } from "react"; * Use speech-to-speech API to listen to user input and read the output * provided by you. */ -export default function UseSpeech2Speech() { +export default function useSpeech2Speech() { const receiverHandlerMap = new Map< IMCMessageTypeEnum, (senderWindow: Window, message: IMCMessage) => Promise diff --git a/npm-packages/react-api/src/hooks/modality/use-stt.ts b/npm-packages/react-api/src/hooks/ai-modality/use-stt.ts similarity index 93% rename from npm-packages/react-api/src/hooks/modality/use-stt.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-stt.ts index f76cf0e..7f8dd2a 100644 --- a/npm-packages/react-api/src/hooks/modality/use-stt.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-stt.ts @@ -23,7 +23,7 @@ export default function useSTT() { } const result = await imc - .sendMessage(IMCMessageTypeEnum.UseSTT, { + .sendMessage(IMCMessageTypeEnum.ModalitySTT, { audio, sttConfig, }) diff --git a/npm-packages/react-api/src/hooks/modality/use-tts.ts b/npm-packages/react-api/src/hooks/ai-modality/use-tts.ts similarity index 93% rename from npm-packages/react-api/src/hooks/modality/use-tts.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-tts.ts index b18932a..f9cdd20 100644 --- a/npm-packages/react-api/src/hooks/modality/use-tts.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-tts.ts @@ -23,7 +23,7 @@ export default function useTTS() { } const result = await imc - .sendMessage(IMCMessageTypeEnum.UseTTS, { + .sendMessage(IMCMessageTypeEnum.ModalityTTS, { text, ttsConfig, }) diff --git a/npm-packages/react-api/src/hooks/modality/use-video-gen.ts b/npm-packages/react-api/src/hooks/ai-modality/use-video-gen.ts similarity index 96% rename from npm-packages/react-api/src/hooks/modality/use-video-gen.ts rename to npm-packages/react-api/src/hooks/ai-modality/use-video-gen.ts index 70729eb..67a9f11 100644 --- a/npm-packages/react-api/src/hooks/modality/use-video-gen.ts +++ b/npm-packages/react-api/src/hooks/ai-modality/use-video-gen.ts @@ -39,7 +39,7 @@ export default function useVideoGen() { } const result = await imc - .sendMessage(IMCMessageTypeEnum.UseVideoGen, { + .sendMessage(IMCMessageTypeEnum.ModalityVideoGen, { duration, textPrompt, imagePrompt, diff --git a/npm-packages/react-api/src/hooks/editor/use-fetch.ts b/npm-packages/react-api/src/hooks/editor/use-fetch.ts deleted file mode 100644 index ce57bad..0000000 --- a/npm-packages/react-api/src/hooks/editor/use-fetch.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IMCMessage, IMCMessageTypeEnum } from "@pulse-editor/shared-utils"; - -import useIMC from "../../lib/use-imc"; - -export default function useFetch() { - const receiverHandlerMap = new Map< - IMCMessageTypeEnum, - (senderWindow: Window, message: IMCMessage) => Promise - >(); - - const { imc } = useIMC(receiverHandlerMap); - - function fetch(uri: string, options?: RequestInit): Promise { - if (!imc) { - throw new Error("IMC is not initialized."); - } - - return imc.sendMessage( - IMCMessageTypeEnum.Fetch, - JSON.stringify({ uri, options }) - ); - } - - return { fetch }; -} diff --git a/npm-packages/react-api/src/hooks/editor/use-file-view.ts b/npm-packages/react-api/src/hooks/editor/use-file-view.ts index 542d27f..99a9e22 100644 --- a/npm-packages/react-api/src/hooks/editor/use-file-view.ts +++ b/npm-packages/react-api/src/hooks/editor/use-file-view.ts @@ -18,15 +18,14 @@ export default function useFileView() { useEffect(() => { if (isReady) { - imc?.sendMessage(IMCMessageTypeEnum.RequestViewFile).then((model) => { + imc?.sendMessage(IMCMessageTypeEnum.PlatformReadFile).then((model) => { setViewModel(model); }); } }, [isReady]); function updateViewModel(viewModel: ViewModel) { - // sender.sendMessage(ViewBoxMessageTypeEnum.ViewFile, JSON.stringify(file)); - imc?.sendMessage(IMCMessageTypeEnum.WriteViewFile, viewModel); + imc?.sendMessage(IMCMessageTypeEnum.PlatformWriteFile, viewModel); } return { diff --git a/npm-packages/react-api/src/hooks/editor/use-loading.ts b/npm-packages/react-api/src/hooks/editor/use-loading.ts index 7e0256a..db13874 100644 --- a/npm-packages/react-api/src/hooks/editor/use-loading.ts +++ b/npm-packages/react-api/src/hooks/editor/use-loading.ts @@ -13,7 +13,7 @@ export default function useLoading() { useEffect(() => { if (isReady) { - imc?.sendMessage(IMCMessageTypeEnum.UseLoading, { + imc?.sendMessage(IMCMessageTypeEnum.EditorLoadingExt, { isLoading, }); } diff --git a/npm-packages/react-api/src/hooks/editor/use-notification.ts b/npm-packages/react-api/src/hooks/editor/use-notification.ts index 17e7b70..a352ac6 100644 --- a/npm-packages/react-api/src/hooks/editor/use-notification.ts +++ b/npm-packages/react-api/src/hooks/editor/use-notification.ts @@ -18,7 +18,7 @@ export default function useNotification() { if (!imc) { throw new Error("IMC is not initialized."); } - imc.sendMessage(IMCMessageTypeEnum.Notification, { + imc.sendMessage(IMCMessageTypeEnum.EditorShowNotification, { text, type, }); diff --git a/npm-packages/react-api/src/hooks/editor/use-theme.ts b/npm-packages/react-api/src/hooks/editor/use-theme.ts index 5458c06..2972bde 100644 --- a/npm-packages/react-api/src/hooks/editor/use-theme.ts +++ b/npm-packages/react-api/src/hooks/editor/use-theme.ts @@ -10,7 +10,7 @@ export default function useTheme() { >(); receiverHandlerMap.set( - IMCMessageTypeEnum.ThemeChange, + IMCMessageTypeEnum.EditorThemeUpdate, async (senderWindow: Window, message: IMCMessage) => { const theme = message.payload; setTheme((prev) => theme); diff --git a/npm-packages/react-api/src/hooks/extension/use-ext-command.ts b/npm-packages/react-api/src/hooks/extension/use-ext-command.ts index 08479d4..cdd0714 100644 --- a/npm-packages/react-api/src/hooks/extension/use-ext-command.ts +++ b/npm-packages/react-api/src/hooks/extension/use-ext-command.ts @@ -28,7 +28,7 @@ export default function useExtCommand( function getReceiverHandlerMap() { const receiverHandlerMap = new Map([ [ - IMCMessageTypeEnum.RunExtCommand, + IMCMessageTypeEnum.EditorRunExtCommand, async (senderWindow: Window, message: IMCMessage) => { if (!commandInfo) { throw new Error("Extension command is not available"); diff --git a/npm-packages/react-api/src/hooks/terminal/use-terminal.ts b/npm-packages/react-api/src/hooks/terminal/use-terminal.ts index 6dff2f9..7c624e3 100644 --- a/npm-packages/react-api/src/hooks/terminal/use-terminal.ts +++ b/npm-packages/react-api/src/hooks/terminal/use-terminal.ts @@ -18,7 +18,7 @@ export default function useTerminal() { useEffect(() => { if (isReady) { - imc?.sendMessage(IMCMessageTypeEnum.RequestTerminal).then((response) => { + imc?.sendMessage(IMCMessageTypeEnum.PlatformCreateTerminal).then((response) => { const { websocketUrl, projectHomePath, diff --git a/npm-packages/react-api/src/main.ts b/npm-packages/react-api/src/main.ts index 5f6d7f0..1bcbca3 100644 --- a/npm-packages/react-api/src/main.ts +++ b/npm-packages/react-api/src/main.ts @@ -1,6 +1,5 @@ import useAgentTools from "./hooks/agent/use-agent-tools"; import useAgents from "./hooks/agent/use-agents"; -import useFetch from "./hooks/editor/use-fetch"; import useFileView from "./hooks/editor/use-file-view"; import useLoading from "./hooks/editor/use-loading"; import useNotification from "./hooks/editor/use-notification"; @@ -8,28 +7,27 @@ import useTheme from "./hooks/editor/use-theme"; import useToolbar from "./hooks/editor/use-toolbar"; import useExtCommand from "./hooks/extension/use-ext-command"; -import useImageGen from "./hooks/modality/use-image-gen"; -import useLLM from "./hooks/modality/use-llm"; -import useOCR from "./hooks/modality/use-ocr"; -import useSTT from "./hooks/modality/use-stt"; -import useTTS from "./hooks/modality/use-tts"; -import useVideoGen from "./hooks/modality/use-video-gen"; +import useImageGen from "./hooks/ai-modality/use-image-gen"; +import useLLM from "./hooks/ai-modality/use-llm"; +import useOCR from "./hooks/ai-modality/use-ocr"; +import useSTT from "./hooks/ai-modality/use-stt"; +import useTTS from "./hooks/ai-modality/use-tts"; +import useVideoGen from "./hooks/ai-modality/use-video-gen"; import useTerminal from "./hooks/terminal/use-terminal"; export { useAgentTools, useAgents, - useFetch, useFileView, useNotification, useTheme, useToolbar, - useImageGen, - useVideoGen, - useLLM, - useOCR, - useSTT, - useTTS, + useImageGen as ModalityImageGen, + useVideoGen as ModalityVideoGen, + useLLM as ModalityLLM, + useOCR as ModalityOCR, + useSTT as ModalitySTT, + useTTS as ModalityTTS, useExtCommand, useTerminal, useLoading, diff --git a/npm-packages/shared-utils/src/imc/inter-module-communication.ts b/npm-packages/shared-utils/src/imc/inter-module-communication.ts index f2b1288..0ec1d17 100644 --- a/npm-packages/shared-utils/src/imc/inter-module-communication.ts +++ b/npm-packages/shared-utils/src/imc/inter-module-communication.ts @@ -54,7 +54,7 @@ export class InterModuleCommunication { if ( this.messageRecords?.has(messageId) && - type !== IMCMessageTypeEnum.GetWindowId + type !== IMCMessageTypeEnum.SignalGetWindowId ) { console.warn( `Duplicate message received with ID: ${messageId}. Ignoring this message. Message: ${JSON.stringify( @@ -131,7 +131,7 @@ export class InterModuleCommunication { this.otherWindow = window; this.otherWindow.postMessage( { - type: IMCMessageTypeEnum.GetWindowId, + type: IMCMessageTypeEnum.SignalGetWindowId, from: this.thisWindowId, }, "*" @@ -193,7 +193,7 @@ export class InterModuleCommunication { // window has received the message and finished processing it. // The current window must be initialized first. i.e. call initThisWindow() before initOtherWindow(). this.receiverHandlerMap?.set( - IMCMessageTypeEnum.Acknowledge, + IMCMessageTypeEnum.SignalAcknowledge, async (senderWindow: Window, message: IMCMessage) => { const pendingMessage = this.sender?.getPendingMessage(message.id); if (pendingMessage) { @@ -205,7 +205,7 @@ export class InterModuleCommunication { // Set get window ID handler in the receiver handler map. this.receiverHandlerMap?.set( - IMCMessageTypeEnum.GetWindowId, + IMCMessageTypeEnum.SignalGetWindowId, async (senderWindow: Window, message: IMCMessage) => { console.log( "Received window ID request. Sending window ID to other window: " @@ -216,7 +216,7 @@ export class InterModuleCommunication { } const msg: IMCMessage = { id: message.id, - type: IMCMessageTypeEnum.ReturnWindowId, + type: IMCMessageTypeEnum.SignalReturnWindowId, payload: { windowId: id, }, diff --git a/npm-packages/shared-utils/src/imc/message-receiver.ts b/npm-packages/shared-utils/src/imc/message-receiver.ts index 75c8623..db10bd0 100644 --- a/npm-packages/shared-utils/src/imc/message-receiver.ts +++ b/npm-packages/shared-utils/src/imc/message-receiver.ts @@ -25,7 +25,7 @@ export class MessageReceiver { if (this.windowId === message.from) return; // Abort the task if the message type is Abort - if (message.type === IMCMessageTypeEnum.Abort) { + if (message.type === IMCMessageTypeEnum.SignalAbort) { const id = message.id; const pendingTask = this.pendingTasks.get(id); @@ -55,7 +55,7 @@ export class MessageReceiver { if (signal.aborted) return; // Acknowledge the sender with the result if the message type is not Acknowledge - if (message.type !== IMCMessageTypeEnum.Acknowledge) { + if (message.type !== IMCMessageTypeEnum.SignalAcknowledge) { this.acknowledgeSender(senderWindow, message.id, result); } }) @@ -63,7 +63,7 @@ export class MessageReceiver { // Send the error message to the sender const errMsg: IMCMessage = { id: message.id, - type: IMCMessageTypeEnum.Error, + type: IMCMessageTypeEnum.SignalError, payload: error.message, from: this.windowId, }; @@ -83,7 +83,7 @@ export class MessageReceiver { ): void { const message: IMCMessage = { id, - type: IMCMessageTypeEnum.Acknowledge, + type: IMCMessageTypeEnum.SignalAcknowledge, payload: payload, from: this.windowId, }; diff --git a/npm-packages/shared-utils/src/imc/message-sender.ts b/npm-packages/shared-utils/src/imc/message-sender.ts index fe76f99..f754d30 100644 --- a/npm-packages/shared-utils/src/imc/message-sender.ts +++ b/npm-packages/shared-utils/src/imc/message-sender.ts @@ -45,7 +45,7 @@ export class MessageSender { this.targetWindow.postMessage( { id, - type: IMCMessageTypeEnum.Abort, + type: IMCMessageTypeEnum.SignalAbort, payload: JSON.stringify({ status: "Task aborted", data: null, diff --git a/npm-packages/shared-utils/src/types/types.ts b/npm-packages/shared-utils/src/types/types.ts index 3a53783..a2dabc7 100644 --- a/npm-packages/shared-utils/src/types/types.ts +++ b/npm-packages/shared-utils/src/types/types.ts @@ -1,60 +1,60 @@ // #region Inter-Module Communication /* Inter Module Communication messages */ export enum IMCMessageTypeEnum { - GetWindowId = "get-window-id", - ReturnWindowId = "return-window-id", - - // Update view file - WriteViewFile = "write-view-file", - // Request view file - RequestViewFile = "request-view-file", - - // Network fetch request - Fetch = "fetch", - // Send notification - Notification = "notification", - // Get theme - ThemeChange = "theme-change", - - /* Agents */ - // Execute agent method - RunAgentMethod = "run-agent-method", - - /* Modality tools */ - UseVAD = "use-vad", - UseSTT = "use-stt", - UseLLM = "use-llm", - UseTTS = "use-tts", - UseSpeech2Speech = "use-speech-to-speech", + // #region AI modality tools + ModalityVAD = "modality-vad", + ModalitySTT = "modality-stt", + ModalityLLM = "modality-llm", + ModalityTTS = "modality-tts", + ModalitySpeech2Speech = "modality-speech-to-speech", // TODO: Do not use UseX2Y or Use__Gen in the future. // Instead, use a common AI IO adapter. - UseImageGen = "use-image-gen", - UseVideoGen = "use-video-gen", - UseOCR = "use-ocr", - UseMusicGen = "use-music-gen", + ModalityImageGen = "modality-image-gen", + ModalityVideoGen = "modality-video-gen", + ModalityOCR = "modality-ocr", + ModalityMusicGen = "modality-music-gen", + // #endregion - /* Extension commands*/ - RunExtCommand = "run-ext-command", - - /* Terminal */ - RequestTerminal = "request-terminal", - - /* Extension statuses */ + // #region Extension states // Notify Pulse that extension window is available ExtReady = "ext-ready", // Notify Pulse that extension is closing ExtClose = "ext-close", + // #endregion + + // #region Editor states + // Notify editor that extension is loading or loaded + EditorLoadingExt = "editor-loading-ext", + /* Extension commands*/ + EditorRunExtCommand = "editor-run-ext-command", + // Execute agent method + EditorRunAgentMethod = "editor-run-agent-method", + // Get theme + EditorThemeUpdate = "editor-theme-update", + // Send notification + EditorShowNotification = "editor-show-notification", + // #endregion - // Notify Pulse that extension is loading or loaded - UseLoading = "use-loading", + // #region Platform API interaction messages (require OS-like environment) + /* Terminal */ + PlatformCreateTerminal = "platform-create-terminal", + // Update view file + PlatformWriteFile = "platform-write-file", + // Request view file + PlatformReadFile = "platform-read-file", + // #endregion + // #region Signal messages + SignalGetWindowId = "signal-get-window-id", + SignalReturnWindowId = "signal-return-window-id", // A message to notify sender that the message // has been received and finished processing - Acknowledge = "acknowledge", + SignalAcknowledge = "signal-acknowledge", // Notify abort - Abort = "abort", + SignalAbort = "signal-abort", // Error - Error = "error", + SignalError = "signal-error", + // #endregion } export type IMCMessage = { diff --git a/package-lock.json b/package-lock.json index 20f5406..541fcad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5058,6 +5058,17 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -5872,39 +5883,39 @@ } }, "node_modules/@module-federation/error-codes": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.18.0.tgz", - "integrity": "sha512-Woonm8ehyVIUPXChmbu80Zj6uJkC0dD9SJUZ/wOPtO8iiz/m+dkrOugAuKgoiR6qH4F+yorWila954tBz4uKsQ==", + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.18.3.tgz", + "integrity": "sha512-ZSSOFvi5iwJdveRQrCIQJHv+clAXKR6APyf+yJq3oLm4EiV70OjVUC8JAG6o5oEwJT4L38U29HbziqZCBA55Yg==", "dev": true, "license": "MIT" }, "node_modules/@module-federation/runtime": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.18.0.tgz", - "integrity": "sha512-+C4YtoSztM7nHwNyZl6dQKGUVJdsPrUdaf3HIKReg/GQbrt9uvOlUWo2NXMZ8vDAnf/QRrpSYAwXHmWDn9Obaw==", + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.18.3.tgz", + "integrity": "sha512-zuPvCs51CFu3efSl7hl8MIEhc1nwYQyJlENWM7qaeWK85yfftLIvYA7iy4+y9CZORTmtEg6RwwlsUmhv62YlLA==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.18.0", - "@module-federation/runtime-core": "0.18.0", - "@module-federation/sdk": "0.18.0" + "@module-federation/error-codes": "0.18.3", + "@module-federation/runtime-core": "0.18.3", + "@module-federation/sdk": "0.18.3" } }, "node_modules/@module-federation/runtime-core": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.18.0.tgz", - "integrity": "sha512-ZyYhrDyVAhUzriOsVfgL6vwd+5ebYm595Y13KeMf6TKDRoUHBMTLGQ8WM4TDj8JNsy7LigncK8C03fn97of0QQ==", + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.18.3.tgz", + "integrity": "sha512-Xk5w+Z+r8f19p/4xLMJTxUxOF0aE/0VEV2yV77dAb4CZ2zPCs2xPqa9Su43+LYlVAkIvcpOgxFCMLQEaxajLPg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.18.0", - "@module-federation/sdk": "0.18.0" + "@module-federation/error-codes": "0.18.3", + "@module-federation/sdk": "0.18.3" } }, "node_modules/@module-federation/sdk": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.18.0.tgz", - "integrity": "sha512-Lo/Feq73tO2unjmpRfyyoUkTVoejhItXOk/h5C+4cistnHbTV8XHrW/13fD5e1Iu60heVdAhhelJd6F898Ve9A==", + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.18.3.tgz", + "integrity": "sha512-tlBgF5pKXoiZ5hGRgafOpsktt0iafdjoH2O85ywPqvDGVK0DzfP8hs4qdUBJlKulP5PZoBtgTe7UiqyTbKJ7YQ==", "dev": true, "license": "MIT" }, @@ -5922,15 +5933,15 @@ } }, "node_modules/@next/env": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.6.tgz", - "integrity": "sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.0.tgz", + "integrity": "sha512-sDaprBAfzCQiOgo2pO+LhnV0Wt2wBgartjrr+dpcTORYVnnXD0gwhHhiiyIih9hQbq+JnbqH4odgcFWhqCGidw==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.4.6.tgz", - "integrity": "sha512-2NOu3ln+BTcpnbIDuxx6MNq+pRrCyey4WSXGaJIyt0D2TYicHeO9QrUENNjcf673n3B1s7hsiV5xBYRCK1Q8kA==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.0.tgz", + "integrity": "sha512-+k83U/fST66eQBjTltX2T9qUYd43ntAe+NZ5qeZVTQyTiFiHvTLtkpLKug4AnZAtuI/lwz5tl/4QDJymjVkybg==", "dev": true, "license": "MIT", "dependencies": { @@ -5938,9 +5949,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz", - "integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.0.tgz", + "integrity": "sha512-v7Jj9iqC6enxIRBIScD/o0lH7QKvSxq2LM8UTyqJi+S2w2QzhMYjven4vgu/RzgsdtdbpkyCxBTzHl/gN5rTRg==", "cpu": [ "arm64" ], @@ -5954,9 +5965,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.6.tgz", - "integrity": "sha512-KMSFoistFkaiQYVQQnaU9MPWtp/3m0kn2Xed1Ces5ll+ag1+rlac20sxG+MqhH2qYWX1O2GFOATQXEyxKiIscg==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.0.tgz", + "integrity": "sha512-s2Nk6ec+pmYmAb/utawuURy7uvyYKDk+TRE5aqLRsdnj3AhwC9IKUBmhfnLmY/+P+DnwqpeXEFIKe9tlG0p6CA==", "cpu": [ "x64" ], @@ -5970,9 +5981,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.6.tgz", - "integrity": "sha512-PnOx1YdO0W7m/HWFeYd2A6JtBO8O8Eb9h6nfJia2Dw1sRHoHpNf6lN1U4GKFRzRDBi9Nq2GrHk9PF3Vmwf7XVw==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.0.tgz", + "integrity": "sha512-mGlPJMZReU4yP5fSHjOxiTYvZmwPSWn/eF/dcg21pwfmiUCKS1amFvf1F1RkLHPIMPfocxLViNWFvkvDB14Isg==", "cpu": [ "arm64" ], @@ -5986,9 +5997,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.6.tgz", - "integrity": "sha512-XBbuQddtY1p5FGPc2naMO0kqs4YYtLYK/8aPausI5lyOjr4J77KTG9mtlU4P3NwkLI1+OjsPzKVvSJdMs3cFaw==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.0.tgz", + "integrity": "sha512-biWqIOE17OW/6S34t1X8K/3vb1+svp5ji5QQT/IKR+VfM3B7GvlCwmz5XtlEan2ukOUf9tj2vJJBffaGH4fGRw==", "cpu": [ "arm64" ], @@ -6002,9 +6013,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.6.tgz", - "integrity": "sha512-+WTeK7Qdw82ez3U9JgD+igBAP75gqZ1vbK6R8PlEEuY0OIe5FuYXA4aTjL811kWPf7hNeslD4hHK2WoM9W0IgA==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.0.tgz", + "integrity": "sha512-zPisT+obYypM/l6EZ0yRkK3LEuoZqHaSoYKj+5jiD9ESHwdr6QhnabnNxYkdy34uCigNlWIaCbjFmQ8FY5AlxA==", "cpu": [ "x64" ], @@ -6018,9 +6029,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.6.tgz", - "integrity": "sha512-XP824mCbgQsK20jlXKrUpZoh/iO3vUWhMpxCz8oYeagoiZ4V0TQiKy0ASji1KK6IAe3DYGfj5RfKP6+L2020OQ==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.0.tgz", + "integrity": "sha512-+t3+7GoU9IYmk+N+FHKBNFdahaReoAktdOpXHFIPOU1ixxtdge26NgQEEkJkCw2dHT9UwwK5zw4mAsURw4E8jA==", "cpu": [ "x64" ], @@ -6034,9 +6045,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.6.tgz", - "integrity": "sha512-FxrsenhUz0LbgRkNWx6FRRJIPe/MI1JRA4W4EPd5leXO00AZ6YU8v5vfx4MDXTvN77lM/EqsE3+6d2CIeF5NYg==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.0.tgz", + "integrity": "sha512-d8MrXKh0A+c9DLiy1BUFwtg3Hu90Lucj3k6iKTUdPOv42Ve2UiIG8HYi3UAb8kFVluXxEfdpCoPPCSODk5fDcw==", "cpu": [ "arm64" ], @@ -6050,9 +6061,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.6.tgz", - "integrity": "sha512-T4ufqnZ4u88ZheczkBTtOF+eKaM14V8kbjud/XrAakoM5DKQWjW09vD6B9fsdsWS2T7D5EY31hRHdta7QKWOng==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.0.tgz", + "integrity": "sha512-Fe1tGHxOWEyQjmygWkkXSwhFcTJuimrNu52JEuwItrKJVV4iRjbWp9I7zZjwqtiNnQmxoEvoisn8wueFLrNpvQ==", "cpu": [ "x64" ], @@ -8092,25 +8103,25 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", + "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" + "tailwindcss": "4.1.12" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", + "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -8122,24 +8133,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + "@tailwindcss/oxide-android-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-arm64": "4.1.12", + "@tailwindcss/oxide-darwin-x64": "4.1.12", + "@tailwindcss/oxide-freebsd-x64": "4.1.12", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", + "@tailwindcss/oxide-linux-x64-musl": "4.1.12", + "@tailwindcss/oxide-wasm32-wasi": "4.1.12", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", + "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", "cpu": [ "arm64" ], @@ -8154,9 +8165,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", + "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", "cpu": [ "arm64" ], @@ -8171,9 +8182,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", + "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", "cpu": [ "x64" ], @@ -8188,9 +8199,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", + "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", "cpu": [ "x64" ], @@ -8205,9 +8216,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", + "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", "cpu": [ "arm" ], @@ -8222,9 +8233,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", + "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", "cpu": [ "arm64" ], @@ -8239,9 +8250,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", + "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", "cpu": [ "arm64" ], @@ -8256,9 +8267,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", + "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", "cpu": [ "x64" ], @@ -8273,9 +8284,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", + "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", "cpu": [ "x64" ], @@ -8290,9 +8301,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", + "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -8308,11 +8319,11 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", "tslib": "^2.8.0" }, "engines": { @@ -8320,9 +8331,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", + "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", "cpu": [ "arm64" ], @@ -8337,9 +8348,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", + "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", "cpu": [ "x64" ], @@ -8421,17 +8432,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", - "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.12.tgz", + "integrity": "sha512-5PpLYhCAwf9SJEeIsSmCDLgyVfdBhdBpzX1OJ87anT9IVR0Z9pjM0FNixCAUAHGnMBGB8K99SwAheXrT0Kh6QQ==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", + "@tailwindcss/node": "4.1.12", + "@tailwindcss/oxide": "4.1.12", "postcss": "^8.4.41", - "tailwindcss": "4.1.11" + "tailwindcss": "4.1.12" } }, "node_modules/@tanstack/react-virtual": { @@ -11892,13 +11903,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.4.6.tgz", - "integrity": "sha512-4uznvw5DlTTjrZgYZjMciSdDDMO2SWIuQgUNaFyC2O3Zw3Z91XeIejeVa439yRq2CnJb/KEvE4U2AeN/66FpUA==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.0.tgz", + "integrity": "sha512-Yl4hlOdBqstAuHnlBfx2RimBzWQwysM2SJNu5EzYVa2qS2ItPs7lgxL0sJJDudEx5ZZHfWPZ/6U8+FtDFWs7/w==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.4.6", + "@next/eslint-plugin-next": "15.5.0", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -14787,9 +14798,9 @@ } }, "node_modules/langchain": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.30.tgz", - "integrity": "sha512-UyVsfwHDpHbrnWrjWuhJHqi8Non+Zcsf2kdpDTqyJF8NXrHBOpjdHT5LvPuW9fnE7miDTWf5mLcrWAGZgcrznQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.31.tgz", + "integrity": "sha512-C7n7WGa44RytsuxEtGcArVcXidRqzjl6UWQxaG3NdIw4gIqErWoOlNC1qADAa04H5JAOARxuE6S99+WNXB/rzA==", "license": "MIT", "dependencies": { "@langchain/openai": ">=0.1.0 <0.7.0", @@ -14797,7 +14808,7 @@ "js-tiktoken": "^1.0.12", "js-yaml": "^4.1.0", "jsonpointer": "^5.0.1", - "langsmith": "^0.3.33", + "langsmith": "^0.3.46", "openapi-types": "^12.1.3", "p-retry": "4", "uuid": "^10.0.0", @@ -15981,12 +15992,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.4.6", - "resolved": "https://registry.npmjs.org/next/-/next-15.4.6.tgz", - "integrity": "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.0.tgz", + "integrity": "sha512-N1lp9Hatw3a9XLt0307lGB4uTKsXDhyOKQo7uYMzX4i0nF/c27grcGXkLdb7VcT8QPYLBa8ouIyEoUQJ2OyeNQ==", "license": "MIT", "dependencies": { - "@next/env": "15.4.6", + "@next/env": "15.5.0", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -15999,14 +16010,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.4.6", - "@next/swc-darwin-x64": "15.4.6", - "@next/swc-linux-arm64-gnu": "15.4.6", - "@next/swc-linux-arm64-musl": "15.4.6", - "@next/swc-linux-x64-gnu": "15.4.6", - "@next/swc-linux-x64-musl": "15.4.6", - "@next/swc-win32-arm64-msvc": "15.4.6", - "@next/swc-win32-x64-msvc": "15.4.6", + "@next/swc-darwin-arm64": "15.5.0", + "@next/swc-darwin-x64": "15.5.0", + "@next/swc-linux-arm64-gnu": "15.5.0", + "@next/swc-linux-arm64-musl": "15.5.0", + "@next/swc-linux-x64-gnu": "15.5.0", + "@next/swc-linux-x64-musl": "15.5.0", + "@next/swc-win32-arm64-msvc": "15.5.0", + "@next/swc-win32-x64-msvc": "15.5.0", "sharp": "^0.34.3" }, "peerDependencies": { @@ -20149,9 +20160,9 @@ } }, "node_modules/react-hot-toast": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", - "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", "license": "MIT", "dependencies": { "csstype": "^3.1.3", @@ -21750,9 +21761,9 @@ } }, "node_modules/swr": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.5.tgz", - "integrity": "sha512-4e7pjTVulZTIL+b/S0RYFsgDcTcXPLUOvBPqyh9YdD+PkHeEMoaPwDmF9Kv6I1nnPg1OFKhiiEYpsYaaE2W2jA==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz", + "integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==", "license": "MIT", "dependencies": { "dequal": "^2.0.3", @@ -21792,9 +21803,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", + "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", "license": "MIT" }, "node_modules/tapable": { @@ -25039,34 +25050,34 @@ "ignore": "^7.0.5", "is-electron": "^2.2.2", "jszip": "^3.10.1", - "langchain": "^0.3.30", + "langchain": "^0.3.31", "material-icons": "^1.13.14", - "next": "^15.4.6", + "next": "^15.5.0", "next-auth": "^4.24.11", "next-themes": "^0.4.6", "qr-code-styling": "^1.9.2", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-hot-toast": "^2.5.2", + "react-hot-toast": "^2.6.0", "react-responsive": "^10.0.1", "react-spinners": "^0.17.0", - "swr": "^2.3.5", + "swr": "^2.3.6", "tesseract.js": "^6.0.1", "uuid": "^11.1.0" }, "devDependencies": { - "@module-federation/runtime": "^0.18.0", - "@tailwindcss/postcss": "^4.1.11", + "@module-federation/runtime": "^0.18.3", + "@tailwindcss/postcss": "^4.1.12", "@types/node": "^24", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "15.4.6", + "eslint-config-next": "15.5.0", "postcss": "^8", "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", "serve": "^14.2.4", - "tailwindcss": "^4.1.11", + "tailwindcss": "^4.1.12", "typescript": "^5", "workbox-webpack-plugin": "^7.3.0" } diff --git a/remote-instance/README.md b/remote-instance/README.md index 83e59a9..f41a1d8 100644 --- a/remote-instance/README.md +++ b/remote-instance/README.md @@ -26,5 +26,5 @@ Create `.env` file in current folder, and set `SSL_CERT_PATH` and `SSL_KEY_PATH` Generate self-signed SSL certificates: ```bash -bash ./generate-self-signed.sh +bash ./utils/generate-self-signed.sh ``` diff --git a/remote-instance/package-lock.json b/remote-instance/package-lock.json index 4afe234..002ef76 100644 --- a/remote-instance/package-lock.json +++ b/remote-instance/package-lock.json @@ -9,8 +9,10 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@pulse-editor/shared-utils": "^0.1.1-alpha.24", "dotenv": "^17.2.0", "express": "^5.1.0", + "ignore": "^7.0.5", "node-pty": "^1.1.0-beta34", "ws": "^8.18.3" }, @@ -464,6 +466,11 @@ "node": ">=18" } }, + "node_modules/@pulse-editor/shared-utils": { + "version": "0.1.1-alpha.24", + "resolved": "https://registry.npmjs.org/@pulse-editor/shared-utils/-/shared-utils-0.1.1-alpha.24.tgz", + "integrity": "sha512-aHSrc1Ntpvvs8npeSa543v0imjd/20UdXiJtLS5g2CsJAWAayzMjEU/nL6aWaE2rn6xThq8xgfy2Iu1Wr/SclQ==" + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -1069,6 +1076,15 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", diff --git a/remote-instance/package.json b/remote-instance/package.json index 9d30b9d..2ddffcd 100644 --- a/remote-instance/package.json +++ b/remote-instance/package.json @@ -11,8 +11,10 @@ "start": "node dist/index.js" }, "dependencies": { + "@pulse-editor/shared-utils": "^0.1.1-alpha.24", "dotenv": "^17.2.0", "express": "^5.1.0", + "ignore": "^7.0.5", "node-pty": "^1.1.0-beta34", "ws": "^8.18.3" }, diff --git a/remote-instance/src/index.ts b/remote-instance/src/index.ts index 36d4158..e4cce2e 100644 --- a/remote-instance/src/index.ts +++ b/remote-instance/src/index.ts @@ -1,68 +1,8 @@ -import express from "express"; -import { createTerminalServer } from "./node-pty/node-pty-server"; -import https from "https"; -import http from "http"; -import fs from "fs"; -import dotenv from "dotenv"; +import { createTerminalServer } from "./servers/node-pty"; +import { createAPIServer } from "./servers/api-server"; -// Load environment variables from .env file -dotenv.config(); - -const app = express(); -const HOST = "0.0.0.0"; -const HTTP_SERVER_PORT = 6080; -const HTTPS_SERVER_PORT = 6443; - -app.get("/:appname/", (_req, res) => { - // Get the requested URL - const serverUrl = _req.protocol + "://" + _req.get("host") + _req.originalUrl; - - // Redirect to https://editor.pulse-editor.com and append - // this instance's URL as a query parameter - const url = new URL( - process.env.FRONTEND_URL ?? "https://editor.pulse-editor.com" - ); - url.searchParams.append("instance", serverUrl); - res.redirect(url.toString()); -}); - -app.get("/:appname/test", (_req, res) => { - res.send("Remote instance is running!"); -}); - -const certPath = process.env.SSL_CERT_PATH; -const keyPath = process.env.SSL_KEY_PATH; - -// Check if the certs directory exists -if ( - !certPath || - !keyPath || - !fs.existsSync(certPath) || - !fs.existsSync(keyPath) -) { - console.log("SSL certificates not found. Using HTTP instead of HTTPS. "); - - const server = http.createServer(app); - - // Start the terminal websocket server +/* Create servers */ +createAPIServer().then((server) => { + // After API server is created, the terminal server can use it createTerminalServer(server); - - server.listen(HTTP_SERVER_PORT, HOST, () => { - console.log(`HTTP server is running on port ${HTTP_SERVER_PORT}`); - }); -} else { - const server = https.createServer( - { - key: fs.readFileSync(keyPath), - cert: fs.readFileSync(certPath), - }, - app - ); - - // Start the terminal websocket server - createTerminalServer(server); - - server.listen(HTTPS_SERVER_PORT, HOST, () => { - console.log(`HTTPS server is running on port ${HTTPS_SERVER_PORT}`); - }); -} +}); diff --git a/remote-instance/src/servers/api-server/index.ts b/remote-instance/src/servers/api-server/index.ts new file mode 100644 index 0000000..6b03be5 --- /dev/null +++ b/remote-instance/src/servers/api-server/index.ts @@ -0,0 +1,100 @@ +import express from "express"; +import https from "https"; +import http from "http"; +import fs from "fs"; +import dotenv from "dotenv"; +import { handlePlatformAPIRequest } from "./platform-api/handler"; + +dotenv.config(); + +const app = express(); +const HOST = "0.0.0.0"; +const HTTP_SERVER_PORT = 6080; +const HTTPS_SERVER_PORT = 6443; +const certPath = process.env.SSL_CERT_PATH; +const keyPath = process.env.SSL_KEY_PATH; + +export async function createAPIServer() { + await createEndpoints(app); + + if ( + certPath && + keyPath && + fs.existsSync(certPath) && + fs.existsSync(keyPath) + ) { + const server = https.createServer( + { + key: fs.readFileSync(keyPath), + cert: fs.readFileSync(certPath), + }, + app + ); + server.listen(HTTPS_SERVER_PORT, HOST, () => { + console.log(`HTTPS server is running on port ${HTTPS_SERVER_PORT}`); + }); + return server; + } else { + const server = http.createServer(app); + server.listen(HTTP_SERVER_PORT, HOST, () => { + console.log(`HTTP server is running on port ${HTTP_SERVER_PORT}`); + }); + return server; + } +} + +async function createEndpoints(app: express.Express) { + app.use(express.json()); + + app.get("/:instanceId/", (req, res) => { + const instanceId = req.params.instanceId; + if (instanceId !== process.env.INSTANCE_ID) { + return res.status(400).send("Invalid instance ID"); + } + // Get the requested URL + const serverUrl = req.protocol + "://" + req.get("host") + req.originalUrl; + + // Redirect to https://editor.pulse-editor.com and append + // this instance's URL as a query parameter + const url = new URL( + process.env.FRONTEND_URL ?? "https://editor.pulse-editor.com" + ); + url.searchParams.append("instance", serverUrl); + res.redirect(url.toString()); + }); + + app.get("/:instanceId/test", (req, res) => { + const instanceId = req.params.instanceId; + if (instanceId !== process.env.INSTANCE_ID) { + return res.status(400).send("Invalid instance ID"); + } + res.send("Remote instance is running!"); + }); + + app.post("/:instanceId/platform-api", async (req, res) => { + const instanceId = req.params.instanceId; + if (instanceId !== process.env.INSTANCE_ID) { + return res.status(400).send("Invalid instance ID"); + } + + // Get json body + const body = req.body; + + console.log("Received platform API request:", body); + + const host = req.host; + + const result = await handlePlatformAPIRequest( + body, + host, + instanceId + ); + + // Process the request and send a response + if (result && result.error) { + res.status(400).json(result); + } else { + res.send(result); + } + }); +} diff --git a/remote-instance/src/servers/api-server/platform-api/handler.ts b/remote-instance/src/servers/api-server/platform-api/handler.ts new file mode 100644 index 0000000..4aae2ea --- /dev/null +++ b/remote-instance/src/servers/api-server/platform-api/handler.ts @@ -0,0 +1,232 @@ +import fs from "fs"; +import ignore from "ignore"; +import path from "path"; + +// Define a safe root directory for projects. Can be overridden by env or configured as needed. +const PROJECTS_ROOT = process.env.PROJECTS_ROOT ?? "/srv/projects"; + +// Utility to resolve and validate user-supplied uri inside PROJECTS_ROOT +function getSafePath(uri: string): string { + // Prevent empty/undefined input + if (!uri || typeof uri !== 'string') { + throw new Error("Invalid project path"); + } + // Resolve against the root directory + const resolved = path.resolve(PROJECTS_ROOT, uri); + // Use fs.realpathSync to follow symlinks + let normalized; + try { + normalized = fs.realpathSync(resolved); + } catch { + // If path does not exist yet (e.g., on creation), just use resolved. + normalized = resolved; + } + // Ensure the normalized path is inside the root + if (!normalized.startsWith(PROJECTS_ROOT)) { + throw new Error("Access to paths outside projects root denied"); + } + return normalized; +} +// List all folders in a path +async function handleListProjects(uri: string) { + const rootPath = getSafePath(uri); + const files = await fs.promises.readdir(rootPath, { withFileTypes: true }); + const folders = files + .filter((file) => file.isDirectory()) + .map((file) => file.name) + .map((projectName) => ({ + name: projectName, + ctime: fs.statSync(path.join(rootPath, projectName)).ctime, + })); + + return folders; +} + +async function listPathContent( + uri: string, + options: any, + baseUri: string | undefined = undefined +) { + const rootPath = getSafePath(uri); + const files = await fs.promises.readdir(rootPath, { withFileTypes: true }); + + const promise: Promise[] = files + // Filter by file type + .filter( + (file) => + (options?.include === "folders" && file.isDirectory()) || + (options?.include === "files" && file.isFile()) || + options?.include === "all" + ) + // Filter by gitignore + .filter((file) => { + if (!options?.gitignore) { + return true; + } + const ig = ignore().add(options.gitignore); + + const filePath = baseUri + ? path.relative(baseUri, path.join(uri, file.name)) + : file.name; + + const isIgnored = ig.ignores(filePath); + + return !isIgnored; + }) + .map(async (file) => { + const name = file.name; + const absoluteUri = path.join(rootPath, name); + if (file.isDirectory()) { + return { + name: name, + isFolder: true, + subDirItems: options.isRecursive + ? await listPathContent(absoluteUri, options, baseUri ?? uri) + : [], + uri: absoluteUri.replace(/\\/g, "/"), + }; + } + + return { + name, + isFolder: false, + uri: absoluteUri.replace(/\\/g, "/"), + }; + }); + + return Promise.all(promise); +} + +// Discover the content of a project +async function handleListPathContent(uri: string, options: any) { + return await listPathContent(uri, options); +} + +async function handleCreateProject(uri: string) { + // Create a folder at the validated path + await fs.promises.mkdir(getSafePath(uri)); +} + +async function handleCreateFolder(uri: string) { + // Create a folder at the validated path + await fs.promises.mkdir(getSafePath(uri)); +} + +async function handleCreateFile(uri: string) { + // Create a file at the validated path + await fs.promises.writeFile(getSafePath(uri), ""); +} + +async function handleRename(oldUri: string, newUri: string) { + await fs.promises.rename(getSafePath(oldUri), getSafePath(newUri)); +} + +async function handleDelete(uri: string) { + await fs.promises.rm(getSafePath(uri), { recursive: true, force: true }); +} + +async function handleHasPath(uri: string) { + return fs.existsSync(getSafePath(uri)); +} + +async function handleReadFile(uri: string) { + // Read the file at validated path + const data = await fs.promises.readFile(getSafePath(uri), "utf-8"); + + return data; +} + +async function handleWriteFile(data: any, uri: string) { + // Write the data at validated path + const safePath = getSafePath(uri); + // create parent directory if it doesn't exist + const dir = path.dirname(safePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + fs.writeFileSync(safePath, data); +} + +async function handleCopyFiles(from: string, to: string) { + // Copy the files from the validated from path to the validated to path + await fs.promises.cp(getSafePath(from), getSafePath(to), { recursive: true }); +} + +async function handleLoadSettings() { + if (fs.existsSync(settingsPath)) { + const data = fs.readFileSync(settingsPath, "utf-8"); + return JSON.parse(data); + } + return {}; +} + +async function handleSaveSettings(settings: any) { + fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2)); +} + +async function handleGetInstallationPath() { + const uri = "~/pulse-editor"; + return uri; +} + +export async function handlePlatformAPIRequest( + data: any, + host: string, + instanceId: string +): Promise { + const { operation, args } = data; + + if (operation === "select-dir") { + throw new Error("Method not implemented."); + } else if (operation === "select-file") { + throw new Error("Method not implemented."); + } else if (operation === "list-projects") { + const { uri }: { uri: string } = args; + return await handleListProjects(uri); + } else if (operation === "list-path-content") { + const { uri, options }: { uri: string; options?: any } = args; + + return await handleListPathContent(uri, options); + } else if (operation === "create-project") { + const { uri }: { uri: string } = args; + await handleCreateProject(uri); + } else if (operation === "create-folder") { + const { uri }: { uri: string } = args; + await handleCreateFolder(uri); + } else if (operation === "create-file") { + const { uri }: { uri: string } = args; + await handleCreateFile(uri); + } else if (operation === "rename") { + const { oldUri, newUri }: { oldUri: string; newUri: string } = args; + await handleRename(oldUri, newUri); + } else if (operation === "delete") { + const { uri }: { uri: string } = args; + await handleDelete(uri); + } else if (operation === "has-path") { + const { uri }: { uri: string } = args; + return await handleHasPath(uri); + } else if (operation === "read-file") { + const { uri }: { uri: string } = args; + return handleReadFile(uri); + } else if (operation === "write-file") { + const { data, uri }: { data: any; uri: string } = args; + await handleWriteFile(data, uri); + } else if (operation === "copy-files") { + const { from, to }: { from: string; to: string } = args; + await handleCopyFiles(from, to); + } else if (operation === "get-persistent-settings") { + return handleLoadSettings(); + } else if (operation === "set-persistent-settings") { + const { settings }: { settings: any } = args; + await handleSaveSettings(settings); + } else if (operation === "reset-persistent-settings") { + await handleSaveSettings({}); + } else if (operation === "get-installation-path") { + return await handleGetInstallationPath(); + } else if (operation === "create-terminal") { + return `${host}/${instanceId}/terminal/ws`; + } + // Do not reflect input data back to the client, return an explicit error message. + return { error: "Unknown operation" }; +} diff --git a/remote-instance/src/node-pty/node-pty-server.ts b/remote-instance/src/servers/node-pty/index.ts similarity index 75% rename from remote-instance/src/node-pty/node-pty-server.ts rename to remote-instance/src/servers/node-pty/index.ts index 9345629..d2446ed 100644 --- a/remote-instance/src/node-pty/node-pty-server.ts +++ b/remote-instance/src/servers/node-pty/index.ts @@ -64,18 +64,32 @@ export function createTerminalServer(server: http.Server | https.Server) { server.on("upgrade", (request, socket, head) => { const { url } = request; + if (!url) { + socket.write("HTTP/1.1 400 Bad Request: Missing WebSocket URL\r\n\r\n"); + socket.destroy(); + return; + } + // Only match URLs like /anything/terminal/ws const wsPathRegex = /^\/([^/]+)\/terminal\/ws$/; - - if (url && wsPathRegex.test(url)) { - wss.handleUpgrade(request, socket, head, (ws) => { - wss.emit("connection", ws, request); - }); - } else { - // Reject unknown upgrade paths - socket.write("HTTP/1.1 404 Not Found\r\n\r\n"); + const match = url.match(wsPathRegex); + + if (!match) { + socket.write("HTTP/1.1 400 Bad Request: Invalid WebSocket path\r\n\r\n"); socket.destroy(); + return; } + + const instanceId = match?.[1]; + if (instanceId !== process.env.INSTANCE_ID) { + socket.write("HTTP/1.1 400 Bad Request: Invalid instance ID\r\n\r\n"); + socket.destroy(); + return; + } + + wss.handleUpgrade(request, socket, head, (ws) => { + wss.emit("connection", ws, request); + }); }); return server; diff --git a/web/components/providers/imc-provider.tsx b/web/components/providers/imc-provider.tsx index af47019..c5d7106 100644 --- a/web/components/providers/imc-provider.tsx +++ b/web/components/providers/imc-provider.tsx @@ -1,6 +1,6 @@ "use client"; -import { IMCContextType, PlatformEnum } from "@/lib/types"; +import { IMCContextType } from "@/lib/types"; import { ImageModelConfig, IMCMessage, @@ -13,8 +13,6 @@ import { } from "@pulse-editor/shared-utils"; import { createContext, useContext, useEffect, useState } from "react"; import { EditorContext } from "./editor-context-provider"; -import { getPlatform } from "@/lib/platform-api/platform-checker"; -import { usePlatformApi } from "@/lib/hooks/use-platform-api"; import { getAPIKey } from "@/lib/settings/api-manager-utils"; import { runAgentMethod } from "@/lib/agent/agent-runner"; import { getLLMModel } from "@/lib/modalities/llm/llm"; @@ -30,7 +28,6 @@ import { } from "@/lib/modalities/utils"; import { getImageGenModel } from "@/lib/modalities/image-gen/image-gen"; import { getVideoGenModel } from "@/lib/modalities/video-gen/video-gen"; -import { getMusicGenModel } from "@/lib/modalities/music-gen/music-gen"; export const IMCContext = createContext(undefined); @@ -42,7 +39,6 @@ export default function InterModuleCommunicationProvider({ const [polyIMC, setPolyIMC] = useState(undefined); const editorContext = useContext(EditorContext); - const { platformApi } = usePlatformApi(); useEffect(() => { // @ts-expect-error set window viewId @@ -77,7 +73,7 @@ export default function InterModuleCommunicationProvider({ function getHandlerMap() { const newMap = new Map([ [ - IMCMessageTypeEnum.RunAgentMethod, + IMCMessageTypeEnum.EditorRunAgentMethod, async ( senderWindow: Window, message: IMCMessage, @@ -148,30 +144,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.RequestTerminal, - async ( - senderWindow: Window, - message: IMCMessage, - abortSignal?: AbortSignal, - ) => { - const platform = getPlatform(); - // Get a shell terminal from native platform APIs - if (platform === PlatformEnum.Capacitor) { - return { - websocketUrl: editorContext?.persistSettings?.mobileHost, - projectHomePath: `~/storage/shared/${editorContext?.persistSettings?.projectHomePath}`, - }; - } else { - const wsUrl = await platformApi?.createTerminal(); - return { - websocketUrl: wsUrl, - projectHomePath: editorContext?.persistSettings?.projectHomePath, - }; - } - }, - ], - [ - IMCMessageTypeEnum.UseVAD, + IMCMessageTypeEnum.ModalityVAD, async ( senderWindow: Window, message: IMCMessage, @@ -183,7 +156,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseSTT, + IMCMessageTypeEnum.ModalitySTT, async ( senderWindow: Window, message: IMCMessage, @@ -222,7 +195,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseLLM, + IMCMessageTypeEnum.ModalityLLM, async ( senderWindow: Window, message: IMCMessage, @@ -269,7 +242,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseTTS, + IMCMessageTypeEnum.ModalityTTS, async ( senderWindow: Window, message: IMCMessage, @@ -311,7 +284,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseImageGen, + IMCMessageTypeEnum.ModalityImageGen, async ( senderWindow: Window, message: IMCMessage, @@ -319,7 +292,7 @@ export default function InterModuleCommunicationProvider({ ) => { // Handle the use diffusion message console.log( - `Received ${IMCMessageTypeEnum.UseImageGen.toString()} message from extension:`, + `Received ${IMCMessageTypeEnum.ModalityImageGen.toString()} message from extension:`, message, ); @@ -356,7 +329,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseVideoGen, + IMCMessageTypeEnum.ModalityVideoGen, async ( senderWindow: Window, message: IMCMessage, @@ -364,7 +337,7 @@ export default function InterModuleCommunicationProvider({ ) => { // Handle the use video generation message console.log( - `Received ${IMCMessageTypeEnum.UseVideoGen.toString()} message from extension:`, + `Received ${IMCMessageTypeEnum.ModalityVideoGen.toString()} message from extension:`, message, ); @@ -403,7 +376,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseOCR, + IMCMessageTypeEnum.ModalityOCR, async ( senderWindow: Window, message: IMCMessage, @@ -420,7 +393,7 @@ export default function InterModuleCommunicationProvider({ }, ], [ - IMCMessageTypeEnum.UseMusicGen, + IMCMessageTypeEnum.ModalityMusicGen, async ( senderWindow: Window, message: IMCMessage, diff --git a/web/components/views/loaders/view-loader.tsx b/web/components/views/loaders/view-loader.tsx index 08f85cb..8e89cfc 100644 --- a/web/components/views/loaders/view-loader.tsx +++ b/web/components/views/loaders/view-loader.tsx @@ -2,7 +2,9 @@ import ExtensionLoader from "@/components/extension/extension-loader"; import Loading from "@/components/interface/loading"; import { EditorContext } from "@/components/providers/editor-context-provider"; import { IMCContext } from "@/components/providers/imc-provider"; -import { Extension } from "@/lib/types"; +import { usePlatformApi } from "@/lib/hooks/use-platform-api"; +import { getPlatform } from "@/lib/platform-api/platform-checker"; +import { Extension, PlatformEnum } from "@/lib/types"; import { ConnectionListener, ExtensionTypeEnum, @@ -45,6 +47,7 @@ export default function ViewLoader({ const [isConnected, setIsConnected] = useState(false); const { resolvedTheme } = useTheme(); + const { platformApi } = usePlatformApi(); // Update view Id when the view model changes useEffect(() => { @@ -148,7 +151,7 @@ export default function ViewLoader({ if (currentViewId && imcContext?.polyIMC?.hasChannel(currentViewId)) { imcContext?.polyIMC?.sendMessage( currentViewId, - IMCMessageTypeEnum.ThemeChange, + IMCMessageTypeEnum.EditorThemeUpdate, resolvedTheme, ); } @@ -175,7 +178,7 @@ export default function ViewLoader({ // Add loaded handler newMap.set( - IMCMessageTypeEnum.UseLoading, + IMCMessageTypeEnum.EditorLoadingExt, async ( senderWindow: Window, message: IMCMessage, @@ -190,9 +193,11 @@ export default function ViewLoader({ }, ); + // The following message handlers require OS-like environment. + // This can be either local environment or remote instance. if (model.extensionConfig?.extensionType === ExtensionTypeEnum.FileView) { newMap.set( - IMCMessageTypeEnum.WriteViewFile, + IMCMessageTypeEnum.PlatformWriteFile, async ( senderWindow: Window, message: IMCMessage, @@ -205,7 +210,7 @@ export default function ViewLoader({ }, ); newMap.set( - IMCMessageTypeEnum.RequestViewFile, + IMCMessageTypeEnum.PlatformReadFile, async ( senderWindow: Window, message: IMCMessage, @@ -217,7 +222,29 @@ export default function ViewLoader({ } else if ( model.extensionConfig?.extensionType === ExtensionTypeEnum.ConsoleView ) { - // Add console view handlers here + newMap.set( + IMCMessageTypeEnum.PlatformCreateTerminal, + async ( + senderWindow: Window, + message: IMCMessage, + abortSignal?: AbortSignal, + ) => { + const platform = getPlatform(); + // Get a shell terminal from native platform APIs + if (platform === PlatformEnum.Capacitor) { + return { + websocketUrl: editorContext?.persistSettings?.mobileHost, + projectHomePath: `~/storage/shared/${editorContext?.persistSettings?.projectHomePath}`, + }; + } else { + const wsUrl = await platformApi?.createTerminal(); + return { + websocketUrl: wsUrl, + projectHomePath: editorContext?.persistSettings?.projectHomePath, + }; + } + }, + ); } return newMap; diff --git a/web/lib/hooks/use-extension-commands.ts b/web/lib/hooks/use-extension-commands.ts index b1195da..1e8be0b 100644 --- a/web/lib/hooks/use-extension-commands.ts +++ b/web/lib/hooks/use-extension-commands.ts @@ -8,7 +8,7 @@ export default function useExtensionCommands() { async function runCommand(windowId: string, commandName: string, args: any) { const result = await imcContext?.polyIMC?.sendMessage( windowId, - IMCMessageTypeEnum.RunExtCommand, + IMCMessageTypeEnum.EditorRunExtCommand, { name: commandName, args: args, diff --git a/web/lib/platform-api/electron/electron-api.ts b/web/lib/platform-api/electron/electron-api.ts index fcaf792..e8d9e84 100644 --- a/web/lib/platform-api/electron/electron-api.ts +++ b/web/lib/platform-api/electron/electron-api.ts @@ -56,7 +56,7 @@ export class ElectronAPI extends AbstractPlatformAPI { } async hasPath(uri: string): Promise { - return await this.electronAPI?.hasFile(uri); + return await this.electronAPI?.hasPath(uri); } async readFile(uri: string): Promise { diff --git a/web/package.json b/web/package.json index 20f22d3..6315cb7 100644 --- a/web/package.json +++ b/web/package.json @@ -36,35 +36,35 @@ "ignore": "^7.0.5", "is-electron": "^2.2.2", "jszip": "^3.10.1", - "langchain": "^0.3.30", + "langchain": "^0.3.31", "material-icons": "^1.13.14", - "next": "^15.4.6", + "next": "^15.5.0", "next-auth": "^4.24.11", "next-themes": "^0.4.6", "qr-code-styling": "^1.9.2", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-hot-toast": "^2.5.2", + "react-hot-toast": "^2.6.0", "react-responsive": "^10.0.1", "react-spinners": "^0.17.0", - "swr": "^2.3.5", + "swr": "^2.3.6", "tesseract.js": "^6.0.1", "uuid": "^11.1.0" }, "devDependencies": { - "@module-federation/runtime": "^0.18.0", - "@tailwindcss/postcss": "^4.1.11", + "@module-federation/runtime": "^0.18.3", + "@tailwindcss/postcss": "^4.1.12", "@types/node": "^24", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "15.4.6", + "eslint-config-next": "15.5.0", "postcss": "^8", "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", "serve": "^14.2.4", - "tailwindcss": "^4.1.11", + "tailwindcss": "^4.1.12", "typescript": "^5", "workbox-webpack-plugin": "^7.3.0" } -} +} \ No newline at end of file