From 6e9c5a88b466234022fb995de54b8ad04f9d894b Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Wed, 21 Jan 2026 13:26:37 +0000 Subject: [PATCH 01/10] fix(ui): allow out-of-order permission clicks Show permission action buttons for queued tool calls while keeping keyboard shortcuts bound to the first active request. Prevent permission center list clicks from overriding keyboard-active ordering. --- .../components/permission-approval-modal.tsx | 11 --- packages/ui/src/components/tool-call.tsx | 84 +++++++++---------- 2 files changed, 42 insertions(+), 53 deletions(-) diff --git a/packages/ui/src/components/permission-approval-modal.tsx b/packages/ui/src/components/permission-approval-modal.tsx index 3a0105038..37250b449 100644 --- a/packages/ui/src/components/permission-approval-modal.tsx +++ b/packages/ui/src/components/permission-approval-modal.tsx @@ -7,8 +7,6 @@ import { getPermissionQueue, getQuestionQueue, getQuestionEnqueuedAtForInstance, - setActivePermissionIdForInstance, - setActiveQuestionIdForInstance, } from "../stores/instances" import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions" import { messageStoreBus } from "../stores/message-v2/bus" @@ -265,19 +263,10 @@ const PermissionApprovalModal: Component = (props) return count === 1 ? "1 question" : `${count} questions` } - const handleActivate = () => { - if (item.kind === "permission") { - setActivePermissionIdForInstance(props.instanceId, item.id) - } else { - setActiveQuestionIdForInstance(props.instanceId, item.id) - } - } - return (
diff --git a/packages/ui/src/components/tool-call.tsx b/packages/ui/src/components/tool-call.tsx index dd60a79a9..b995d05af 100644 --- a/packages/ui/src/components/tool-call.tsx +++ b/packages/ui/src/components/tool-call.tsx @@ -7,6 +7,7 @@ import { useGlobalCache } from "../lib/hooks/use-global-cache" import { useConfig } from "../stores/preferences" import type { DiffViewMode } from "../stores/preferences" import { activeInterruption, sendPermissionResponse, sendQuestionReject, sendQuestionReply } from "../stores/instances" +import type { PermissionRequestLike } from "../types/permission" import { getPermissionDisplayTitle, getPermissionKind, getPermissionSessionId } from "../types/permission" import type { QuestionRequest } from "@opencode-ai/sdk/v2" import type { TextPart, RenderCache } from "../types/message" @@ -859,15 +860,17 @@ export default function ToolCall(props: ToolCallProps) { const activeKey = activePermissionKey() if (!activeKey) return const handler = (event: KeyboardEvent) => { + const permission = permissionDetails() + if (!permission || !isPermissionActive()) return if (event.key === "Enter") { event.preventDefault() - handlePermissionResponse("once") + void handlePermissionResponse(permission, "once") } else if (event.key === "a" || event.key === "A") { event.preventDefault() - handlePermissionResponse("always") + void handlePermissionResponse(permission, "always") } else if (event.key === "d" || event.key === "D") { event.preventDefault() - handlePermissionResponse("reject") + void handlePermissionResponse(permission, "reject") } } document.addEventListener("keydown", handler) @@ -1240,11 +1243,8 @@ export default function ToolCall(props: ToolCallProps) { return renderer().renderBody(rendererContext) } - async function handlePermissionResponse(response: "once" | "always" | "reject") { - const permission = permissionDetails() - if (!permission || !isPermissionActive()) { - return - } + async function handlePermissionResponse(permission: PermissionRequestLike, response: "once" | "always" | "reject") { + if (!permission) return setPermissionSubmitting(true) setPermissionError(null) try { @@ -1310,37 +1310,37 @@ export default function ToolCall(props: ToolCallProps) {
)} - Waiting for earlier permission responses.

} - > -
-
- - - -
+ +

Waiting for earlier permission responses.

+
+
+
+ + + +
+
Enter Allow once @@ -1349,10 +1349,10 @@ export default function ToolCall(props: ToolCallProps) { D Deny
-
- -
{permissionError()}
+
+ +
{permissionError()}
From 7c0bf382ba529602195d49ea2ea78df86483b285 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Wed, 21 Jan 2026 14:17:08 +0000 Subject: [PATCH 02/10] fix(ui): add permission actions for unresolved requests Render Allow/Deny buttons in the permissions control center fallback when a permission request cannot be linked to a tool-call, enabling responses for global permissions like doom_loop. --- .../components/permission-approval-modal.tsx | 95 +++++++++++++++++-- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/components/permission-approval-modal.tsx b/packages/ui/src/components/permission-approval-modal.tsx index 37250b449..60bd512a6 100644 --- a/packages/ui/src/components/permission-approval-modal.tsx +++ b/packages/ui/src/components/permission-approval-modal.tsx @@ -7,6 +7,7 @@ import { getPermissionQueue, getQuestionQueue, getQuestionEnqueuedAtForInstance, + sendPermissionResponse, } from "../stores/instances" import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions" import { messageStoreBus } from "../stores/message-v2/bus" @@ -130,6 +131,45 @@ function resolveToolCallFromQuestion(instanceId: string, request: QuestionReques const PermissionApprovalModal: Component = (props) => { const [loadingSession, setLoadingSession] = createSignal(null) + const [permissionSubmitting, setPermissionSubmitting] = createSignal>(new Set()) + const [permissionError, setPermissionError] = createSignal>(new Map()) + + const setPermissionBusy = (permissionId: string, busy: boolean) => { + setPermissionSubmitting((prev) => { + const next = new Set(prev) + if (busy) next.add(permissionId) + else next.delete(permissionId) + return next + }) + } + + const setPermissionItemError = (permissionId: string, message: string | null) => { + setPermissionError((prev) => { + const next = new Map(prev) + if (!message) next.delete(permissionId) + else next.set(permissionId, message) + return next + }) + } + + async function handlePermissionDecision(permission: PermissionRequestLike, response: "once" | "always" | "reject") { + const permissionId = permission?.id + if (!permissionId) return + + if (permissionSubmitting().has(permissionId)) return + + setPermissionBusy(permissionId, true) + setPermissionItemError(permissionId, null) + + try { + const sessionId = getPermissionSessionId(permission) || "" + await sendPermissionResponse(props.instanceId, sessionId, permissionId, response) + } catch (error) { + setPermissionItemError(permissionId, error instanceof Error ? error.message : "Unable to update permission") + } finally { + setPermissionBusy(permissionId, false) + } + } const permissionQueue = createMemo(() => getPermissionQueue(props.instanceId)) const questionQueue = createMemo(() => getQuestionQueue(props.instanceId)) @@ -304,17 +344,52 @@ const PermissionApprovalModal: Component = (props) - -
- {primaryTitle()} + +
+ {primaryTitle()} +
+ +
+
+ + + +
+
+ + {(err) =>
{err()}
} +
+
+ +
Load session for more information.
+
-
Load session for more information.
- - } - > + } + > {(data) => ( Date: Thu, 22 Jan 2026 09:38:38 +0000 Subject: [PATCH 03/10] fix(ui): allow spaces in question custom answers Stop trimming custom answer input on each keystroke and instead normalize answers on submit so multi-word custom responses work. --- packages/ui/src/components/tool-call.tsx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/components/tool-call.tsx b/packages/ui/src/components/tool-call.tsx index b995d05af..b122f3b8d 100644 --- a/packages/ui/src/components/tool-call.tsx +++ b/packages/ui/src/components/tool-call.tsx @@ -206,8 +206,9 @@ function QuestionToolBlock(props: QuestionToolBlockProps) { const toggleFromCustomInput = (questionIndex: number, input: HTMLInputElement | null) => { if (!props.active()) return - const value = input?.value?.trim() ?? "" - if (!value) return + const rawValue = input?.value ?? "" + const value = rawValue + if (value.trim().length === 0) return const info = questions()[questionIndex] const multi = info?.multiple === true @@ -230,12 +231,13 @@ function QuestionToolBlock(props: QuestionToolBlockProps) { const handleCustomTyping = (questionIndex: number, input: HTMLInputElement) => { if (!props.active()) return - const value = input.value.trim() + const value = input.value + const trimmed = value.trim() const info = questions()[questionIndex] const multi = info?.multiple === true if (!multi) { - updateAnswer(questionIndex, value ? [value] : []) + updateAnswer(questionIndex, trimmed.length > 0 ? [value] : []) return } @@ -245,10 +247,11 @@ function QuestionToolBlock(props: QuestionToolBlockProps) { let next = existing.filter((item) => item !== last) - if (value) { - if (!optionLabels.has(value) && !next.includes(value)) { + if (trimmed.length > 0) { + // Only treat it as custom if it doesn't match an existing option label. + if (!optionLabels.has(trimmed) && !next.includes(value)) { next = [...next, value] - } else if (optionLabels.has(value)) { + } else if (optionLabels.has(trimmed)) { // If they typed an existing option label, don't treat it as custom. } else if (!next.includes(value)) { next = [...next, value] @@ -897,7 +900,10 @@ export default function ToolCall(props: ToolCallProps) { return } const answers = (questionDraftAnswers()[request.id] ?? []).map((x) => (Array.isArray(x) ? x : [])) - const normalized = request.questions.map((_, index) => answers[index] ?? []) + const normalized = request.questions.map((_, index) => { + const row = answers[index] ?? [] + return row.map((value) => value.trim()).filter((value) => value.length > 0) + }) if (normalized.some((item) => (item?.length ?? 0) === 0)) { setQuestionError("Please answer all questions before submitting.") return From a014ce555a26c847d324c1facd0096f6bb95a905 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 15:12:32 +0000 Subject: [PATCH 04/10] feat(server): auto-update UI via remote manifest --- packages/server/package-lock.json | 770 ++++++++++++++++++ packages/server/package.json | 2 + packages/server/src/api-types.ts | 20 +- packages/server/src/events/bus.ts | 2 - packages/server/src/index.ts | 66 +- packages/server/src/ui/remote-ui.ts | 535 ++++++++++++ .../src/components/folder-selection-view.tsx | 4 + packages/ui/src/components/version-pill.tsx | 38 + packages/ui/src/stores/releases.ts | 60 +- 9 files changed, 1441 insertions(+), 56 deletions(-) create mode 100644 packages/server/src/ui/remote-ui.ts create mode 100644 packages/ui/src/components/version-pill.tsx diff --git a/packages/server/package-lock.json b/packages/server/package-lock.json index b7b5b2b0b..e7e9d36b8 100644 --- a/packages/server/package-lock.json +++ b/packages/server/package-lock.json @@ -9,12 +9,22 @@ "version": "0.7.5", "dependencies": { "@fastify/cors": "^8.5.0", + "@fastify/reply-from": "^9.8.0", + "@fastify/static": "^7.0.4", "commander": "^12.1.0", "fastify": "^4.28.1", + "fuzzysort": "^2.0.4", "pino": "^9.4.0", + "undici": "^6.19.8", + "yauzl": "^2.10.0", "zod": "^3.23.8" }, + "bin": { + "codenomad": "dist/bin.js" + }, "devDependencies": { + "@types/yauzl": "^2.10.0", + "cross-env": "^7.0.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", "typescript": "^5.6.3" @@ -475,6 +485,15 @@ "node": ">=18" } }, + "node_modules/@fastify/accept-negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", + "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@fastify/ajv-compiler": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", @@ -486,6 +505,15 @@ "fast-uri": "^2.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@fastify/cors": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.5.0.tgz", @@ -520,6 +548,77 @@ "fast-deep-equal": "^3.1.3" } }, + "node_modules/@fastify/reply-from": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@fastify/reply-from/-/reply-from-9.8.0.tgz", + "integrity": "sha512-bPNVaFhEeNI0Lyl6404YZaPFokudCplidE3QoOcr78yOy6H9sYw97p5KPYvY/NJNUHfFtvxOaSAHnK+YSiv/Mg==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^3.0.0", + "end-of-stream": "^1.4.4", + "fast-content-type-parse": "^1.1.0", + "fast-querystring": "^1.0.0", + "fastify-plugin": "^4.0.0", + "toad-cache": "^3.7.0", + "undici": "^5.19.1" + } + }, + "node_modules/@fastify/reply-from/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/@fastify/send": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", + "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.1", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "2.0.0", + "mime": "^3.0.0" + } + }, + "node_modules/@fastify/static": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.4.tgz", + "integrity": "sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==", + "license": "MIT", + "dependencies": { + "@fastify/accept-negotiator": "^1.0.0", + "@fastify/send": "^2.0.0", + "content-disposition": "^0.5.3", + "fastify-plugin": "^4.0.0", + "fastq": "^1.17.0", + "glob": "^10.3.4" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -548,12 +647,31 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT" }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -593,6 +711,16 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -674,6 +802,30 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -700,6 +852,48 @@ "fastq": "^1.17.1" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -709,6 +903,18 @@ "node": ">=18" } }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -725,6 +931,48 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -735,6 +983,27 @@ "node": ">=0.3.1" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -777,6 +1046,12 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/fast-content-type-parse": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", @@ -891,6 +1166,15 @@ "reusify": "^1.0.4" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/find-my-way": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", @@ -905,6 +1189,22 @@ "node": ">=14" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -929,6 +1229,12 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fuzzysort": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-2.0.4.tgz", + "integrity": "sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==", + "license": "MIT" + }, "node_modules/get-tsconfig": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", @@ -942,6 +1248,48 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -951,6 +1299,36 @@ "node": ">= 0.10" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/json-schema-ref-resolver": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", @@ -977,6 +1355,12 @@ "set-cookie-parser": "^2.4.1" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -984,6 +1368,42 @@ "dev": true, "license": "ISC" }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mnemonist": { "version": "0.39.6", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", @@ -1008,6 +1428,52 @@ "node": ">=14.0.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, "node_modules/pino": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", @@ -1139,6 +1605,26 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-regex2": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", @@ -1181,6 +1667,45 @@ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -1199,6 +1724,111 @@ "node": ">= 10.x" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/thread-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", @@ -1217,6 +1847,15 @@ "node": ">=12" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -1296,6 +1935,15 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -1310,6 +1958,128 @@ "dev": true, "license": "MIT" }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/packages/server/package.json b/packages/server/package.json index 0ee91437f..694335a1b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -32,9 +32,11 @@ "fuzzysort": "^2.0.4", "pino": "^9.4.0", "undici": "^6.19.8", + "yauzl": "^2.10.0", "zod": "^3.23.8" }, "devDependencies": { + "@types/yauzl": "^2.10.0", "cross-env": "^7.0.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", diff --git a/packages/server/src/api-types.ts b/packages/server/src/api-types.ts index b889cac37..38cc1992c 100644 --- a/packages/server/src/api-types.ts +++ b/packages/server/src/api-types.ts @@ -167,7 +167,6 @@ export type WorkspaceEventType = | "instance.dataChanged" | "instance.event" | "instance.eventStatus" - | "app.releaseAvailable" export type WorkspaceEventPayload = | { type: "workspace.created"; workspace: WorkspaceDescriptor } @@ -180,7 +179,6 @@ export type WorkspaceEventPayload = | { type: "instance.dataChanged"; instanceId: string; data: InstanceData } | { type: "instance.event"; instanceId: string; event: InstanceStreamEvent } | { type: "instance.eventStatus"; instanceId: string; status: InstanceStreamStatus; reason?: string } - | { type: "app.releaseAvailable"; release: LatestReleaseInfo } export interface NetworkAddress { ip: string @@ -198,6 +196,19 @@ export interface LatestReleaseInfo { notes?: string } +export interface UiMeta { + version?: string + source: "bundled" | "downloaded" | "previous" | "override" | "dev-proxy" | "missing" +} + +export interface SupportMeta { + supported: boolean + message?: string + minServerVersion?: string + latestServerVersion?: string + latestServerUrl?: string +} + export interface ServerMeta { /** Base URL clients should target for REST calls (useful for Electron embedding). */ httpBaseUrl: string @@ -215,8 +226,9 @@ export interface ServerMeta { workspaceRoot: string /** Reachable addresses for this server, external first. */ addresses: NetworkAddress[] - /** Optional metadata about the most recent public release. */ - latestRelease?: LatestReleaseInfo + serverVersion?: string + ui?: UiMeta + support?: SupportMeta } export type BackgroundProcessStatus = "running" | "stopped" | "error" diff --git a/packages/server/src/events/bus.ts b/packages/server/src/events/bus.ts index 3d417ce89..614530246 100644 --- a/packages/server/src/events/bus.ts +++ b/packages/server/src/events/bus.ts @@ -29,7 +29,6 @@ export class EventBus extends EventEmitter { this.on("instance.dataChanged", handler) this.on("instance.event", handler) this.on("instance.eventStatus", handler) - this.on("app.releaseAvailable", handler) return () => { this.off("workspace.created", handler) this.off("workspace.started", handler) @@ -41,7 +40,6 @@ export class EventBus extends EventEmitter { this.off("instance.dataChanged", handler) this.off("instance.event", handler) this.off("instance.eventStatus", handler) - this.off("app.releaseAvailable", handler) } } } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index c2085a0a7..ac0c34e0e 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -17,7 +17,7 @@ import { InstanceStore } from "./storage/instance-store" import { InstanceEventBridge } from "./workspaces/instance-events" import { createLogger } from "./logger" import { launchInBrowser } from "./launcher" -import { startReleaseMonitor } from "./releases/release-monitor" +import { resolveUi } from "./ui/remote-ui" import { AuthManager, BOOTSTRAP_TOKEN_STDOUT_PREFIX, DEFAULT_AUTH_USERNAME } from "./auth/manager" const require = createRequire(import.meta.url) @@ -37,6 +37,9 @@ interface CliOptions { logDestination?: string uiStaticDir: string uiDevServer?: string + uiAutoUpdate: boolean + uiNoUpdate: boolean + uiManifestUrl?: string launch: boolean authUsername: string authPassword?: string @@ -66,6 +69,9 @@ function parseCliOptions(argv: string[]): CliOptions { new Option("--ui-dir ", "Directory containing the built UI bundle").env("CLI_UI_DIR").default(DEFAULT_UI_STATIC_DIR), ) .addOption(new Option("--ui-dev-server ", "Proxy UI requests to a running dev server").env("CLI_UI_DEV_SERVER")) + .addOption(new Option("--ui-no-update", "Disable remote UI updates").env("CLI_UI_NO_UPDATE").default(false)) + .addOption(new Option("--ui-auto-update ", "Enable remote UI updates (true|false)").env("CLI_UI_AUTO_UPDATE").default("true")) + .addOption(new Option("--ui-manifest-url ", "Remote UI manifest URL").env("CLI_UI_MANIFEST_URL")) .addOption(new Option("--launch", "Launch the UI in a browser after start").env("CLI_LAUNCH").default(false)) .addOption( new Option("--username ", "Username for server authentication") @@ -91,6 +97,9 @@ function parseCliOptions(argv: string[]): CliOptions { logDestination?: string uiDir: string uiDevServer?: string + uiNoUpdate?: boolean + uiAutoUpdate?: string + uiManifestUrl?: string launch?: boolean username: string password?: string @@ -101,6 +110,9 @@ function parseCliOptions(argv: string[]): CliOptions { const normalizedHost = resolveHost(parsed.host) + const autoUpdateString = (parsed.uiAutoUpdate ?? "true").trim().toLowerCase() + const uiAutoUpdate = autoUpdateString === "1" || autoUpdateString === "true" || autoUpdateString === "yes" + return { port: parsed.port, host: normalizedHost, @@ -111,6 +123,9 @@ function parseCliOptions(argv: string[]): CliOptions { logDestination: parsed.logDestination, uiStaticDir: parsed.uiDir, uiDevServer: parsed.uiDevServer, + uiAutoUpdate, + uiNoUpdate: Boolean(parsed.uiNoUpdate), + uiManifestUrl: parsed.uiManifestUrl, launch: Boolean(parsed.launch), authUsername: parsed.username, authPassword: parsed.password, @@ -141,6 +156,10 @@ function resolveHost(input: string | undefined): string { return trimmed } +function programHasArg(argv: string[], flag: string): boolean { + return argv.includes(flag) +} + async function main() { const options = parseCliOptions(process.argv.slice(2)) const logger = createLogger({ level: options.logLevel, destination: options.logDestination, component: "app" }) @@ -205,19 +224,36 @@ async function main() { logger: logger.child({ component: "instance-events" }), }) - const releaseMonitor = startReleaseMonitor({ - currentVersion: packageJson.version, - logger: logger.child({ component: "release-monitor" }), - onUpdate: (release) => { - if (release) { - serverMeta.latestRelease = release - eventBus.publish({ type: "app.releaseAvailable", release }) - } else { - delete serverMeta.latestRelease - } - }, + const uiDirEnvOverride = Boolean(process.env.CLI_UI_DIR) + const uiDirCliOverride = programHasArg(process.argv.slice(2), "--ui-dir") + const uiOverrideIsExplicit = uiDirEnvOverride || uiDirCliOverride + const uiDirOverride = uiOverrideIsExplicit ? options.uiStaticDir : undefined + + const autoUpdateEnabled = options.uiAutoUpdate && !options.uiNoUpdate + + const uiResolution = await resolveUi({ + serverVersion: packageJson.version, + bundledUiDir: DEFAULT_UI_STATIC_DIR, + autoUpdate: autoUpdateEnabled, + overrideUiDir: uiDirOverride, + uiDevServerUrl: options.uiDevServer, + manifestUrl: options.uiManifestUrl, + logger: logger.child({ component: "ui" }), }) + serverMeta.serverVersion = packageJson.version + serverMeta.ui = { + version: uiResolution.uiVersion, + source: uiResolution.source, + } + serverMeta.support = { + supported: uiResolution.supported, + message: uiResolution.message, + latestServerVersion: uiResolution.latestServerVersion, + latestServerUrl: uiResolution.latestServerUrl, + minServerVersion: uiResolution.minServerVersion, + } + const server = createHttpServer({ host: options.host, port: options.port, @@ -229,8 +265,8 @@ async function main() { serverMeta, instanceStore, authManager, - uiStaticDir: options.uiStaticDir, - uiDevServerUrl: options.uiDevServer, + uiStaticDir: uiResolution.uiStaticDir ?? DEFAULT_UI_STATIC_DIR, + uiDevServerUrl: uiResolution.uiDevServerUrl, logger, }) @@ -266,7 +302,7 @@ async function main() { logger.error({ err: error }, "Workspace manager shutdown failed") } - releaseMonitor.stop() + // no-op: remote UI manifest replaces GitHub release monitor logger.info("Exiting process") process.exit(0) diff --git a/packages/server/src/ui/remote-ui.ts b/packages/server/src/ui/remote-ui.ts new file mode 100644 index 000000000..45e0e544e --- /dev/null +++ b/packages/server/src/ui/remote-ui.ts @@ -0,0 +1,535 @@ +import { createHash } from "crypto" +import fs from "fs" +import { promises as fsp } from "fs" +import os from "os" +import path from "path" +import { Readable } from "stream" +import { fetch } from "undici" +import yauzl from "yauzl" +import type { Logger } from "../logger" + +export interface RemoteUiManifest { + minServerVersion: string + latestUIVersion: string + uiPackageURL: string + sha256: string + latestServerVersion?: string + latestServerUrl?: string +} + +export type UiSource = "bundled" | "downloaded" | "previous" | "override" | "dev-proxy" | "missing" + +export interface UiResolution { + uiStaticDir?: string + uiDevServerUrl?: string + source: UiSource + uiVersion?: string + supported: boolean + message?: string + latestServerVersion?: string + latestServerUrl?: string + minServerVersion?: string +} + +export interface RemoteUiOptions { + serverVersion: string + bundledUiDir: string + autoUpdate: boolean + overrideUiDir?: string + uiDevServerUrl?: string + manifestUrl?: string + configDir?: string + logger: Logger +} + +const DEFAULT_MANIFEST_URL = "https://ui.codenomad.neuralnomads.ai/version.json" + +const MANIFEST_TIMEOUT_MS = 5_000 +const ZIP_TIMEOUT_MS = 30_000 + +export async function resolveUi(options: RemoteUiOptions): Promise { + const manifestUrl = options.manifestUrl ?? DEFAULT_MANIFEST_URL + + if (options.uiDevServerUrl) { + return { + uiDevServerUrl: options.uiDevServerUrl, + source: "dev-proxy", + supported: true, + } + } + + if (options.overrideUiDir) { + const resolved = await resolveStaticUiDir(options.overrideUiDir) + return { + uiStaticDir: resolved ?? options.overrideUiDir, + source: "override", + uiVersion: await readUiVersion(resolved ?? options.overrideUiDir), + supported: true, + } + } + + const uiRoot = resolveUiCacheRoot(options.configDir) + const currentDir = path.join(uiRoot, "current") + const previousDir = path.join(uiRoot, "previous") + + if (!options.autoUpdate) { + const local = await resolveStaticUiDir(currentDir) + if (local) { + return { + uiStaticDir: local, + source: "downloaded", + uiVersion: await readUiVersion(local), + supported: true, + } + } + + const bundled = await resolveStaticUiDir(options.bundledUiDir) + return { + uiStaticDir: bundled ?? options.bundledUiDir, + source: bundled ? "bundled" : "missing", + uiVersion: bundled ? await readUiVersion(bundled) : undefined, + supported: true, + } + } + + let manifest: RemoteUiManifest | null = null + try { + manifest = await fetchManifest(manifestUrl, options.logger) + } catch (error) { + options.logger.debug({ err: error }, "Remote UI manifest unavailable; using cached/bundled UI") + } + + if (!manifest) { + return await resolveFromCacheOrBundled({ + logger: options.logger, + bundledUiDir: options.bundledUiDir, + currentDir, + previousDir, + supported: true, + }) + } + + const supported = compareSemverCore(options.serverVersion, manifest.minServerVersion) >= 0 + if (!supported) { + const message = "Upgrade App to use latest features" + return await resolveFromCacheOrBundled({ + logger: options.logger, + bundledUiDir: options.bundledUiDir, + currentDir, + previousDir, + supported: false, + message, + latestServerVersion: manifest.latestServerVersion, + latestServerUrl: manifest.latestServerUrl, + minServerVersion: manifest.minServerVersion, + }) + } + + const currentVersion = await readUiVersion(currentDir) + if (currentVersion && currentVersion === manifest.latestUIVersion) { + const currentResolved = await resolveStaticUiDir(currentDir) + if (currentResolved) { + return { + uiStaticDir: currentResolved, + source: "downloaded", + uiVersion: currentVersion, + supported: true, + latestServerVersion: manifest.latestServerVersion, + latestServerUrl: manifest.latestServerUrl, + minServerVersion: manifest.minServerVersion, + } + } + } + + try { + await installRemoteUi({ + manifest, + uiRoot, + currentDir, + previousDir, + logger: options.logger, + }) + } catch (error) { + options.logger.warn({ err: error }, "Failed to install remote UI; falling back") + return await resolveFromCacheOrBundled({ + logger: options.logger, + bundledUiDir: options.bundledUiDir, + currentDir, + previousDir, + supported: true, + latestServerVersion: manifest.latestServerVersion, + latestServerUrl: manifest.latestServerUrl, + minServerVersion: manifest.minServerVersion, + }) + } + + const installed = await resolveStaticUiDir(currentDir) + if (installed) { + return { + uiStaticDir: installed, + source: "downloaded", + uiVersion: await readUiVersion(installed), + supported: true, + latestServerVersion: manifest.latestServerVersion, + latestServerUrl: manifest.latestServerUrl, + minServerVersion: manifest.minServerVersion, + } + } + + return await resolveFromCacheOrBundled({ + logger: options.logger, + bundledUiDir: options.bundledUiDir, + currentDir, + previousDir, + supported: true, + latestServerVersion: manifest.latestServerVersion, + latestServerUrl: manifest.latestServerUrl, + minServerVersion: manifest.minServerVersion, + }) +} + +function resolveUiCacheRoot(configDir?: string): string { + if (configDir) { + return path.join(configDir, "ui") + } + return path.join(os.homedir(), ".config", "codenomad", "ui") +} + +async function resolveFromCacheOrBundled(args: { + logger: Logger + bundledUiDir: string + currentDir: string + previousDir: string + supported: boolean + message?: string + latestServerVersion?: string + latestServerUrl?: string + minServerVersion?: string +}): Promise { + const currentResolved = await resolveStaticUiDir(args.currentDir) + if (currentResolved) { + return { + uiStaticDir: currentResolved, + source: "downloaded", + uiVersion: await readUiVersion(currentResolved), + supported: args.supported, + message: args.message, + latestServerVersion: args.latestServerVersion, + latestServerUrl: args.latestServerUrl, + minServerVersion: args.minServerVersion, + } + } + + const previousResolved = await resolveStaticUiDir(args.previousDir) + if (previousResolved) { + return { + uiStaticDir: previousResolved, + source: "previous", + uiVersion: await readUiVersion(previousResolved), + supported: args.supported, + message: args.message, + latestServerVersion: args.latestServerVersion, + latestServerUrl: args.latestServerUrl, + minServerVersion: args.minServerVersion, + } + } + + const bundledResolved = await resolveStaticUiDir(args.bundledUiDir) + if (bundledResolved) { + return { + uiStaticDir: bundledResolved, + source: "bundled", + uiVersion: await readUiVersion(bundledResolved), + supported: args.supported, + message: args.message, + latestServerVersion: args.latestServerVersion, + latestServerUrl: args.latestServerUrl, + minServerVersion: args.minServerVersion, + } + } + + args.logger.warn({ bundledUiDir: args.bundledUiDir }, "No UI assets found") + return { + uiStaticDir: args.bundledUiDir, + source: "missing", + supported: args.supported, + message: args.message, + latestServerVersion: args.latestServerVersion, + latestServerUrl: args.latestServerUrl, + minServerVersion: args.minServerVersion, + } +} + +async function resolveStaticUiDir(uiDir: string): Promise { + try { + const indexPath = path.join(uiDir, "index.html") + await fsp.access(indexPath, fs.constants.R_OK) + return uiDir + } catch { + return null + } +} + +interface UiVersionFile { + uiVersion?: string + version?: string +} + +async function readUiVersion(uiDir: string): Promise { + try { + const content = await fsp.readFile(path.join(uiDir, "ui-version.json"), "utf-8") + const parsed = JSON.parse(content) as UiVersionFile + return parsed.uiVersion ?? parsed.version + } catch { + return undefined + } +} + +async function fetchManifest(url: string, logger: Logger): Promise { + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), MANIFEST_TIMEOUT_MS) + try { + const response = await fetch(url, { + signal: controller.signal, + headers: { + Accept: "application/json", + "User-Agent": "CodeNomad-CLI", + }, + }) + if (!response.ok) { + throw new Error(`Manifest responded with ${response.status}`) + } + const json = (await response.json()) as RemoteUiManifest + validateManifest(json) + return json + } catch (error) { + logger.debug({ err: error, url }, "Failed to fetch remote UI manifest") + throw error + } finally { + clearTimeout(timeout) + } +} + +function validateManifest(manifest: RemoteUiManifest) { + const required: Array = ["minServerVersion", "latestUIVersion", "uiPackageURL", "sha256"] + for (const key of required) { + const value = manifest[key] + if (typeof value !== "string" || value.trim().length === 0) { + throw new Error(`Manifest missing ${key}`) + } + } + if (!/^https:\/\//i.test(manifest.uiPackageURL)) { + throw new Error("uiPackageURL must be https") + } + if (!/^[a-f0-9]{64}$/i.test(manifest.sha256.trim())) { + throw new Error("sha256 must be 64 hex chars") + } +} + +async function installRemoteUi(args: { + manifest: RemoteUiManifest + uiRoot: string + currentDir: string + previousDir: string + logger: Logger +}) { + await fsp.mkdir(args.uiRoot, { recursive: true }) + + const tmpDir = path.join(args.uiRoot, `tmp-${Date.now()}`) + const zipPath = path.join(args.uiRoot, `ui-${args.manifest.latestUIVersion}.zip`) + + try { + await downloadFile(args.manifest.uiPackageURL, zipPath, args.logger) + const digest = await sha256File(zipPath) + if (digest.toLowerCase() !== args.manifest.sha256.toLowerCase()) { + throw new Error(`sha256 mismatch for UI zip (expected ${args.manifest.sha256}, got ${digest})`) + } + + await extractZip(zipPath, tmpDir) + + const indexPath = path.join(tmpDir, "index.html") + if (!fs.existsSync(indexPath)) { + throw new Error("Extracted UI missing index.html") + } + + await rotateDirs({ currentDir: args.currentDir, previousDir: args.previousDir, logger: args.logger }) + + fs.rmSync(args.currentDir, { recursive: true, force: true }) + fs.renameSync(tmpDir, args.currentDir) + } finally { + fs.rmSync(tmpDir, { recursive: true, force: true }) + fs.rmSync(zipPath, { force: true }) + } +} + +async function rotateDirs(args: { currentDir: string; previousDir: string; logger: Logger }) { + try { + if (fs.existsSync(args.previousDir)) { + fs.rmSync(args.previousDir, { recursive: true, force: true }) + } + if (fs.existsSync(args.currentDir)) { + fs.renameSync(args.currentDir, args.previousDir) + } + } catch (error) { + args.logger.warn({ err: error }, "Failed to rotate UI cache directories") + } +} + +async function downloadFile(url: string, targetPath: string, logger: Logger) { + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), ZIP_TIMEOUT_MS) + try { + const response = await fetch(url, { + signal: controller.signal, + headers: { + Accept: "application/octet-stream", + "User-Agent": "CodeNomad-CLI", + }, + }) + if (!response.ok || !response.body) { + throw new Error(`UI zip download failed with ${response.status}`) + } + + await fsp.mkdir(path.dirname(targetPath), { recursive: true }) + const fileStream = fs.createWriteStream(targetPath) + + const body = response.body + if (!body) { + throw new Error("UI zip response missing body") + } + + const nodeStream = Readable.fromWeb(body as any) + + await new Promise((resolve, reject) => { + nodeStream.pipe(fileStream) + nodeStream.on("error", reject) + fileStream.on("error", reject) + fileStream.on("finish", () => resolve()) + }) + + logger.debug({ url, targetPath }, "Downloaded remote UI bundle") + } finally { + clearTimeout(timeout) + } +} + +async function sha256File(filePath: string): Promise { + const hash = createHash("sha256") + const stream = fs.createReadStream(filePath) + await new Promise((resolve, reject) => { + stream.on("data", (chunk) => hash.update(chunk)) + stream.on("error", reject) + stream.on("end", () => resolve()) + }) + return hash.digest("hex") +} + +async function extractZip(zipPath: string, targetDir: string): Promise { + await fsp.mkdir(targetDir, { recursive: true }) + + await new Promise((resolve, reject) => { + yauzl.open(zipPath, { lazyEntries: true }, (openErr, zipfile) => { + if (openErr || !zipfile) { + reject(openErr ?? new Error("Unable to open zip")) + return + } + + const root = path.resolve(targetDir) + + const closeWithError = (error: unknown) => { + try { + zipfile.close() + } catch { + // ignore + } + reject(error) + } + + zipfile.readEntry() + + zipfile.on("entry", (entry) => { + // Normalize and guard against zip-slip. + const entryPath = entry.fileName.replace(/\\/g, "/") + + const segments = entryPath.split("/").filter(Boolean) + if (segments.some((segment: string) => segment === "..") || path.isAbsolute(entryPath)) { + closeWithError(new Error(`Invalid zip entry path: ${entry.fileName}`)) + return + } + + const destination = path.resolve(targetDir, entryPath) + if (!destination.startsWith(root + path.sep) && destination !== root) { + closeWithError(new Error(`Zip entry escapes target dir: ${entry.fileName}`)) + return + } + + const isDirectory = entry.fileName.endsWith("/") + + if (isDirectory) { + fsp + .mkdir(destination, { recursive: true }) + .then(() => zipfile.readEntry()) + .catch((error) => closeWithError(error)) + return + } + + fsp + .mkdir(path.dirname(destination), { recursive: true }) + .then(() => { + zipfile.openReadStream(entry, (streamErr, readStream) => { + if (streamErr || !readStream) { + closeWithError(streamErr ?? new Error("Unable to read zip entry")) + return + } + + const writeStream = fs.createWriteStream(destination) + const cleanup = (error?: unknown) => { + readStream.destroy() + writeStream.destroy() + if (error) { + closeWithError(error) + } + } + + readStream.on("error", cleanup) + writeStream.on("error", cleanup) + writeStream.on("finish", () => zipfile.readEntry()) + + readStream.pipe(writeStream) + }) + }) + .catch((error) => closeWithError(error)) + }) + + zipfile.on("end", () => { + zipfile.close() + resolve() + }) + + zipfile.on("error", (error) => closeWithError(error)) + }) + }) +} + +function compareSemverCore(a: string, b: string): number { + const pa = parseSemverCore(a) + const pb = parseSemverCore(b) + if (pa.major !== pb.major) return pa.major > pb.major ? 1 : -1 + if (pa.minor !== pb.minor) return pa.minor > pb.minor ? 1 : -1 + if (pa.patch !== pb.patch) return pa.patch > pb.patch ? 1 : -1 + return 0 +} + +function parseSemverCore(value: string): { major: number; minor: number; patch: number } { + const core = value.trim().replace(/^v/i, "").split("-", 1)[0] ?? "0.0.0" + const parts = core.split(".") + const parsePart = (input: string | undefined) => { + const n = Number.parseInt((input ?? "0").replace(/[^0-9]/g, ""), 10) + return Number.isFinite(n) ? n : 0 + } + return { + major: parsePart(parts[0]), + minor: parsePart(parts[1]), + patch: parsePart(parts[2]), + } +} diff --git a/packages/ui/src/components/folder-selection-view.tsx b/packages/ui/src/components/folder-selection-view.tsx index c495457a2..4391d8458 100644 --- a/packages/ui/src/components/folder-selection-view.tsx +++ b/packages/ui/src/components/folder-selection-view.tsx @@ -5,6 +5,7 @@ import AdvancedSettingsModal from "./advanced-settings-modal" import DirectoryBrowserDialog from "./directory-browser-dialog" import Kbd from "./kbd" import { openNativeFolderDialog, supportsNativeDialogs } from "../lib/native/native-functions" +import VersionPill from "./version-pill" const codeNomadLogo = new URL("../images/CodeNomad-Icon.png", import.meta.url).href @@ -248,6 +249,9 @@ const FolderSelectionView: Component = (props) => {

CodeNomad

Select a folder to start coding with AI

+
+ +
diff --git a/packages/ui/src/components/version-pill.tsx b/packages/ui/src/components/version-pill.tsx new file mode 100644 index 000000000..2f75fd3fe --- /dev/null +++ b/packages/ui/src/components/version-pill.tsx @@ -0,0 +1,38 @@ +import { Show, createEffect, createSignal } from "solid-js" +import type { ServerMeta } from "../../../server/src/api-types" +import { getServerMeta } from "../lib/server-meta" + +export default function VersionPill() { + const [meta, setMeta] = createSignal(null) + + createEffect(() => { + void getServerMeta() + .then((result) => setMeta(result)) + .catch(() => setMeta(null)) + }) + + const serverVersion = () => meta()?.serverVersion + const uiVersion = () => meta()?.ui?.version + const uiSource = () => meta()?.ui?.source + + return ( + +
+ + {(v) => App {v()}} + + + {(v) => ( + <> + · + + UI {v()} + {(s) => ({s()})} + + + )} + +
+
+ ) +} diff --git a/packages/ui/src/stores/releases.ts b/packages/ui/src/stores/releases.ts index f55bae709..e0566ede4 100644 --- a/packages/ui/src/stores/releases.ts +++ b/packages/ui/src/stores/releases.ts @@ -1,25 +1,24 @@ import { createEffect, createSignal } from "solid-js" -import type { LatestReleaseInfo, WorkspaceEventPayload } from "../../../server/src/api-types" +import type { SupportMeta } from "../../../server/src/api-types" import { getServerMeta } from "../lib/server-meta" -import { serverEvents } from "../lib/server-events" import { showToastNotification, ToastHandle } from "../lib/notifications" import { getLogger } from "../lib/logger" import { hasInstances, showFolderSelection } from "./ui" const log = getLogger("actions") -const [availableRelease, setAvailableRelease] = createSignal(null) +const [supportInfo, setSupportInfo] = createSignal(null) let initialized = false let visibilityEffectInitialized = false let activeToast: ToastHandle | null = null -let activeToastVersion: string | null = null +let activeToastKey: string | null = null function dismissActiveToast() { if (activeToast) { activeToast.dismiss() activeToast = null - activeToastVersion = null + activeToastKey = null } } @@ -30,28 +29,34 @@ function ensureVisibilityEffect() { visibilityEffectInitialized = true createEffect(() => { - const release = availableRelease() - const shouldShow = Boolean(release) && (!hasInstances() || showFolderSelection()) + const support = supportInfo() + const shouldShow = Boolean(support && support.supported === false) && (!hasInstances() || showFolderSelection()) - if (!shouldShow || !release) { + if (!shouldShow || !support || support.supported !== false) { dismissActiveToast() return } - if (!activeToast || activeToastVersion !== release.version) { + const key = `${support.minServerVersion ?? "unknown"}:${support.latestServerVersion ?? "unknown"}` + + if (!activeToast || activeToastKey !== key) { dismissActiveToast() activeToast = showToastNotification({ - title: `CodeNomad ${release.version}`, - message: release.channel === "dev" ? "Dev release build available." : "New stable build on GitHub.", + title: support.message ?? "Upgrade required", + message: support.latestServerVersion + ? `Update to CodeNomad ${support.latestServerVersion} to use the latest UI.` + : "Update CodeNomad to use the latest UI.", variant: "info", duration: Number.POSITIVE_INFINITY, position: "bottom-right", - action: { - label: "View release", - href: release.url, - }, + action: support.latestServerUrl + ? { + label: "Get update", + href: support.latestServerUrl, + } + : undefined, }) - activeToastVersion = release.version + activeToastKey = key } }) } @@ -64,32 +69,17 @@ export function initReleaseNotifications() { ensureVisibilityEffect() void refreshFromMeta() - - serverEvents.on("app.releaseAvailable", (event) => { - const typedEvent = event as Extract - applyRelease(typedEvent.release) - }) } async function refreshFromMeta() { try { const meta = await getServerMeta(true) - if (meta.latestRelease) { - applyRelease(meta.latestRelease) - } + setSupportInfo(meta.support ?? null) } catch (error) { - log.warn("Unable to load server metadata for release info", error) - } -} - -function applyRelease(release: LatestReleaseInfo | null | undefined) { - if (!release) { - setAvailableRelease(null) - return + log.warn("Unable to load server metadata for support info", error) } - setAvailableRelease(release) } -export function useAvailableRelease() { - return availableRelease +export function useSupportInfo() { + return supportInfo } From 02f1284f7fd075689db787cf869bda691bdf8d47 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 15:17:09 +0000 Subject: [PATCH 05/10] fix(ui): emit ui-version.json and show UI source --- packages/ui/src/components/version-pill.tsx | 20 ++++++++++---------- packages/ui/vite.config.ts | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/components/version-pill.tsx b/packages/ui/src/components/version-pill.tsx index 2f75fd3fe..a3347f0bd 100644 --- a/packages/ui/src/components/version-pill.tsx +++ b/packages/ui/src/components/version-pill.tsx @@ -16,21 +16,21 @@ export default function VersionPill() { const uiSource = () => meta()?.ui?.source return ( - +
{(v) => App {v()}} - - {(v) => ( - <> + + <> + · - - UI {v()} - {(s) => ({s()})} - - - )} + + + UI{uiVersion() ? ` ${uiVersion()}` : ""} + {(s) => ({s()})} + +
diff --git a/packages/ui/vite.config.ts b/packages/ui/vite.config.ts index b0a441bdb..4b1b3178c 100644 --- a/packages/ui/vite.config.ts +++ b/packages/ui/vite.config.ts @@ -1,10 +1,26 @@ +import fs from "fs" import { defineConfig } from "vite" import solid from "vite-plugin-solid" import { resolve } from "path" +const uiPackageJson = JSON.parse(fs.readFileSync(resolve(__dirname, "package.json"), "utf-8")) as { version?: string } +const uiVersion = uiPackageJson.version ?? "0.0.0" + export default defineConfig({ root: "./src/renderer", - plugins: [solid()], + plugins: [ + solid(), + { + name: "emit-ui-version", + generateBundle() { + this.emitFile({ + type: "asset", + fileName: "ui-version.json", + source: JSON.stringify({ uiVersion }, null, 2), + }) + }, + }, + ], css: { postcss: "./postcss.config.js", }, From adbfab5c2596259fb5d641e5d582aba461fc2286 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 16:16:36 +0000 Subject: [PATCH 06/10] feat(cloudflare): worker-hosted version.json for UI updates --- packages/cloudflare/.gitignore | 1 + packages/cloudflare/package.json | 14 ++++ packages/cloudflare/release-config.json | 4 + .../cloudflare/scripts/build-manifest.mjs | 75 +++++++++++++++++++ packages/cloudflare/scripts/release-ui.mjs | 73 ++++++++++++++++++ packages/cloudflare/src/index.ts | 29 +++++++ packages/cloudflare/wrangler.toml | 9 +++ 7 files changed, 205 insertions(+) create mode 100644 packages/cloudflare/.gitignore create mode 100644 packages/cloudflare/package.json create mode 100644 packages/cloudflare/release-config.json create mode 100644 packages/cloudflare/scripts/build-manifest.mjs create mode 100644 packages/cloudflare/scripts/release-ui.mjs create mode 100644 packages/cloudflare/src/index.ts create mode 100644 packages/cloudflare/wrangler.toml diff --git a/packages/cloudflare/.gitignore b/packages/cloudflare/.gitignore new file mode 100644 index 000000000..849ddff3b --- /dev/null +++ b/packages/cloudflare/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json new file mode 100644 index 000000000..e678bc94e --- /dev/null +++ b/packages/cloudflare/package.json @@ -0,0 +1,14 @@ +{ + "name": "@codenomad/ui-host-worker", + "private": true, + "type": "module", + "scripts": { + "build:manifest": "node ./scripts/build-manifest.mjs", + "release:ui": "node ./scripts/release-ui.mjs", + "dev": "wrangler dev", + "deploy": "wrangler deploy" + }, + "devDependencies": { + "wrangler": "^4.0.0" + } +} diff --git a/packages/cloudflare/release-config.json b/packages/cloudflare/release-config.json new file mode 100644 index 000000000..4e3b324a2 --- /dev/null +++ b/packages/cloudflare/release-config.json @@ -0,0 +1,4 @@ +{ + "minServerVersion": "0.7.5", + "latestServerUrl": "https://github.com/NeuralNomadsAI/CodeNomad/releases/latest" +} diff --git a/packages/cloudflare/scripts/build-manifest.mjs b/packages/cloudflare/scripts/build-manifest.mjs new file mode 100644 index 000000000..582bc34c0 --- /dev/null +++ b/packages/cloudflare/scripts/build-manifest.mjs @@ -0,0 +1,75 @@ +import { createHash } from "crypto" +import fs from "fs" +import path from "path" +import { fileURLToPath } from "url" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +const root = path.resolve(__dirname, "..") +const repoRoot = path.resolve(root, "..", "..") + +const releaseConfigPath = path.join(root, "release-config.json") +const uiPackageJsonPath = path.join(repoRoot, "packages/ui/package.json") +const serverPackageJsonPath = path.join(repoRoot, "packages/server/package.json") + +const distDir = path.join(root, "dist") +const manifestPath = path.join(distDir, "version.json") + +const args = new Set(process.argv.slice(2)) + +function getArgValue(flag) { + const idx = process.argv.indexOf(flag) + if (idx === -1) return null + return process.argv[idx + 1] ?? null +} + +const zipPath = getArgValue("--zip") + +if (!zipPath) { + console.error("Usage: node scripts/build-manifest.mjs --zip ") + process.exit(1) +} + +const resolvedZipPath = path.resolve(process.cwd(), zipPath) +if (!fs.existsSync(resolvedZipPath)) { + console.error(`Zip not found: ${resolvedZipPath}`) + process.exit(1) +} + +const releaseConfig = JSON.parse(fs.readFileSync(releaseConfigPath, "utf-8")) +const uiPackageJson = JSON.parse(fs.readFileSync(uiPackageJsonPath, "utf-8")) +const serverPackageJson = JSON.parse(fs.readFileSync(serverPackageJsonPath, "utf-8")) + +const bucket = process.env.CODENOMAD_R2_BUCKET + +if (!bucket) { + console.error("Missing env var: CODENOMAD_R2_BUCKET") + process.exit(1) +} + +const uiVersion = uiPackageJson.version +const serverVersion = serverPackageJson.version + +if (!uiVersion || !serverVersion) { + console.error("Missing version fields in package.json") + process.exit(1) +} + +const sha256 = createHash("sha256").update(fs.readFileSync(resolvedZipPath)).digest("hex") + +const uiPackageURL = `https://download.codenomad.neuralnomads.ai/ui/ui-${uiVersion}.zip` + +const manifest = { + minServerVersion: releaseConfig.minServerVersion, + latestUIVersion: uiVersion, + uiPackageURL, + sha256, + latestServerVersion: serverVersion, + latestServerUrl: releaseConfig.latestServerUrl, +} + +fs.mkdirSync(distDir, { recursive: true }) +fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8") + +console.log(`Wrote ${manifestPath}`) diff --git a/packages/cloudflare/scripts/release-ui.mjs b/packages/cloudflare/scripts/release-ui.mjs new file mode 100644 index 000000000..24de80e54 --- /dev/null +++ b/packages/cloudflare/scripts/release-ui.mjs @@ -0,0 +1,73 @@ +import { execFileSync } from "child_process" +import fs from "fs" +import os from "os" +import path from "path" +import { fileURLToPath } from "url" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +const root = path.resolve(__dirname, "..") +const repoRoot = path.resolve(root, "..", "..") + +const r2Bucket = process.env.CODENOMAD_R2_BUCKET + +if (!r2Bucket) { + console.error("Missing env var: CODENOMAD_R2_BUCKET") + process.exit(1) +} + +const uiPackageJsonPath = path.join(repoRoot, "packages/ui/package.json") +const uiPackageJson = JSON.parse(fs.readFileSync(uiPackageJsonPath, "utf-8")) +const uiVersion = uiPackageJson.version + +if (!uiVersion) { + console.error("Missing packages/ui/package.json version") + process.exit(1) +} + +const uiBuildDir = path.join(repoRoot, "packages/ui/src/renderer/dist") +if (!fs.existsSync(uiBuildDir)) { + console.error(`Missing UI build dir: ${uiBuildDir}. Run UI build first.`) + process.exit(1) +} + +const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "codenomad-ui-release-")) +const zipPath = path.join(tmpDir, `ui-${uiVersion}.zip`) + +try { + // Zip the CONTENTS of the dist dir (so index.html is at zip root). + execFileSync("/usr/bin/zip", ["-q", "-r", zipPath, "."], { cwd: uiBuildDir, stdio: "inherit" }) + + // Upload to R2. + const objectKey = `ui/ui-${uiVersion}.zip` + console.log(`[release-ui] Uploading ${zipPath} -> r2://${r2Bucket}/${objectKey}`) + + execFileSync( + "npx", + ["wrangler", "r2", "object", "put", r2Bucket, objectKey, "--file", zipPath], + { cwd: root, stdio: "inherit" }, + ) + + // Generate version.json into packages/cloudflare/dist + console.log("[release-ui] Generating version.json") + execFileSync( + process.execPath, + [path.join(root, "scripts/build-manifest.mjs"), "--zip", zipPath], + { + cwd: root, + stdio: "inherit", + env: { + ...process.env, + CODENOMAD_R2_BUCKET: r2Bucket, + }, + }, + ) + + console.log("[release-ui] Deploying worker") + execFileSync("npx", ["wrangler", "deploy"], { cwd: root, stdio: "inherit" }) + + console.log("[release-ui] Done") +} finally { + fs.rmSync(tmpDir, { recursive: true, force: true }) +} diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts new file mode 100644 index 000000000..46831ae97 --- /dev/null +++ b/packages/cloudflare/src/index.ts @@ -0,0 +1,29 @@ +export interface Env { + ASSETS: { fetch: (request: Request) => Promise } +} + +function withHeader(response: Response, key: string, value: string): Response { + const headers = new Headers(response.headers) + headers.set(key, value) + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers, + }) +} + +export default { + async fetch(request: Request, env: Env): Promise { + const url = new URL(request.url) + + if (url.pathname === "/version.json") { + const assetResponse = await env.ASSETS.fetch(request) + + // Ensure this stays fresh; the server uses it on startup. + const withCache = withHeader(assetResponse, "Cache-Control", "no-cache") + return withHeader(withCache, "Content-Type", "application/json; charset=utf-8") + } + + return new Response("Not found", { status: 404 }) + }, +} diff --git a/packages/cloudflare/wrangler.toml b/packages/cloudflare/wrangler.toml new file mode 100644 index 000000000..fe2d50ac9 --- /dev/null +++ b/packages/cloudflare/wrangler.toml @@ -0,0 +1,9 @@ +name = "codenomad-ui-host" +main = "src/index.ts" +compatibility_date = "2026-01-22" + +[assets] +directory = "./dist" +binding = "ASSETS" +not_found_handling = "404-page" +run_worker_first = ["/version.json"] From 43a476e96720a4250f77fcaa77849d794e8bef78 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 16:29:23 +0000 Subject: [PATCH 07/10] fix(cloudflare): use custom domain and remote R2 uploads --- packages/cloudflare/scripts/release-ui.mjs | 12 ++++++++++-- packages/cloudflare/wrangler.toml | 6 ++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare/scripts/release-ui.mjs b/packages/cloudflare/scripts/release-ui.mjs index 24de80e54..bfa202bc7 100644 --- a/packages/cloudflare/scripts/release-ui.mjs +++ b/packages/cloudflare/scripts/release-ui.mjs @@ -45,7 +45,7 @@ try { execFileSync( "npx", - ["wrangler", "r2", "object", "put", r2Bucket, objectKey, "--file", zipPath], + ["wrangler", "r2", "object", "put", "--remote", `${r2Bucket}/${objectKey}`, "--file", zipPath], { cwd: root, stdio: "inherit" }, ) @@ -65,7 +65,15 @@ try { ) console.log("[release-ui] Deploying worker") - execFileSync("npx", ["wrangler", "deploy"], { cwd: root, stdio: "inherit" }) + execFileSync("npx", ["wrangler", "deploy"], { + cwd: root, + stdio: "inherit", + env: { + ...process.env, + CLOUDFLARE_API_TOKEN: process.env.CLOUDFLARE_API_TOKEN, + CLOUDFLARE_ACCOUNT_ID: process.env.CLOUDFLARE_ACCOUNT_ID, + }, + }) console.log("[release-ui] Done") } finally { diff --git a/packages/cloudflare/wrangler.toml b/packages/cloudflare/wrangler.toml index fe2d50ac9..fa9d7f2fc 100644 --- a/packages/cloudflare/wrangler.toml +++ b/packages/cloudflare/wrangler.toml @@ -2,6 +2,12 @@ name = "codenomad-ui-host" main = "src/index.ts" compatibility_date = "2026-01-22" +# Custom domain for the manifest host. +# Note: Custom domains apply to all paths on the hostname. +[[routes]] +pattern = "ui.codenomad.neuralnomads.ai" +custom_domain = true + [assets] directory = "./dist" binding = "ASSETS" From 668ac7fa8862aacfbc0fc15de08181c6448c9a66 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 16:40:20 +0000 Subject: [PATCH 08/10] ci: publish remote UI on main --- .github/workflows/release-ui.yml | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/release-ui.yml diff --git a/.github/workflows/release-ui.yml b/.github/workflows/release-ui.yml new file mode 100644 index 000000000..c13785ab5 --- /dev/null +++ b/.github/workflows/release-ui.yml @@ -0,0 +1,43 @@ +on: + push: + branches: + - main + workflow_dispatch: {} + +permissions: + contents: read + +env: + NODE_VERSION: 20 + +jobs: + release-ui: + if: ${{ github.ref == 'refs/heads/main' }} + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + - name: Install dependencies + run: npm ci --workspaces --include=optional + + - name: Install Cloudflare worker deps + run: npm ci + working-directory: packages/cloudflare + + - name: Build UI + run: npm run build --workspace @codenomad/ui + + - name: Publish UI zip + update manifest + working-directory: packages/cloudflare + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CODENOMAD_R2_BUCKET: ${{ vars.CODENOMAD_R2_BUCKET }} + run: npm run release:ui From 267f1592c445bcde72fe64f07a5d5d6feaf5c0d4 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 16:42:47 +0000 Subject: [PATCH 09/10] chore: ignore local artifacts and add cloudflare lockfile --- .gitignore | 7 +- packages/cloudflare/package-lock.json | 1515 +++++++++++++++++++++++++ 2 files changed, 1521 insertions(+), 1 deletion(-) create mode 100644 packages/cloudflare/package-lock.json diff --git a/.gitignore b/.gitignore index da104bdd9..131c5a63d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,9 @@ release/ .electron-vite/ out/ .dir-locals.el -.opencode/bashOutputs/ \ No newline at end of file +.opencode/bashOutputs/ + +# Local runtime artifacts +.codenomad/ +.tmp/ +packages/cloudflare/.wrangler/ \ No newline at end of file diff --git a/packages/cloudflare/package-lock.json b/packages/cloudflare/package-lock.json new file mode 100644 index 000000000..12b89dffe --- /dev/null +++ b/packages/cloudflare/package-lock.json @@ -0,0 +1,1515 @@ +{ + "name": "@codenomad/ui-host-worker", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@codenomad/ui-host-worker", + "devDependencies": { + "wrangler": "^4.0.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", + "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", + "dev": true, + "license": "MIT OR Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@cloudflare/unenv-preset": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.11.0.tgz", + "integrity": "sha512-z3hxFajL765VniNPGV0JRStZolNz63gU3B3AktwoGdDlnQvz5nP+Ah4RL04PONlZQjwmDdGHowEStJ94+RsaJg==", + "dev": true, + "license": "MIT OR Apache-2.0", + "peerDependencies": { + "unenv": "2.0.0-rc.24", + "workerd": "^1.20260115.0" + }, + "peerDependenciesMeta": { + "workerd": { + "optional": true + } + } + }, + "node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260120.0.tgz", + "integrity": "sha512-JLHx3p5dpwz4wjVSis45YNReftttnI3ndhdMh5BUbbpdreN/g0jgxNt5Qp9tDFqEKl++N63qv+hxJiIIvSLR+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-darwin-arm64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260120.0.tgz", + "integrity": "sha512-1Md2tCRhZjwajsZNOiBeOVGiS3zbpLPzUDjHr4+XGTXWOA6FzzwScJwQZLa0Doc28Cp4Nr1n7xGL0Dwiz1XuOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260120.0.tgz", + "integrity": "sha512-O0mIfJfvU7F8N5siCoRDaVDuI12wkz2xlG4zK6/Ct7U9c9FiE0ViXNFWXFQm5PPj+qbkNRyhjUwhP+GCKTk5EQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260120.0.tgz", + "integrity": "sha512-aRHO/7bjxVpjZEmVVcpmhbzpN6ITbFCxuLLZSW0H9O0C0w40cDCClWSi19T87Ax/PQcYjFNT22pTewKsupkckA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260120.0.tgz", + "integrity": "sha512-ASZIz1E8sqZQqQCgcfY1PJbBpUDrxPt8NZ+lqNil0qxnO4qX38hbCsdDF2/TDAuq0Txh7nu8ztgTelfNDlb4EA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", + "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", + "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", + "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", + "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", + "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", + "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", + "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", + "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", + "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", + "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", + "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", + "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", + "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", + "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", + "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", + "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", + "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", + "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", + "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", + "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", + "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", + "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", + "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", + "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", + "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", + "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@poppinss/colors": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", + "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@speed-highlight/core": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz", + "integrity": "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/esbuild": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.0", + "@esbuild/android-arm": "0.27.0", + "@esbuild/android-arm64": "0.27.0", + "@esbuild/android-x64": "0.27.0", + "@esbuild/darwin-arm64": "0.27.0", + "@esbuild/darwin-x64": "0.27.0", + "@esbuild/freebsd-arm64": "0.27.0", + "@esbuild/freebsd-x64": "0.27.0", + "@esbuild/linux-arm": "0.27.0", + "@esbuild/linux-arm64": "0.27.0", + "@esbuild/linux-ia32": "0.27.0", + "@esbuild/linux-loong64": "0.27.0", + "@esbuild/linux-mips64el": "0.27.0", + "@esbuild/linux-ppc64": "0.27.0", + "@esbuild/linux-riscv64": "0.27.0", + "@esbuild/linux-s390x": "0.27.0", + "@esbuild/linux-x64": "0.27.0", + "@esbuild/netbsd-arm64": "0.27.0", + "@esbuild/netbsd-x64": "0.27.0", + "@esbuild/openbsd-arm64": "0.27.0", + "@esbuild/openbsd-x64": "0.27.0", + "@esbuild/openharmony-arm64": "0.27.0", + "@esbuild/sunos-x64": "0.27.0", + "@esbuild/win32-arm64": "0.27.0", + "@esbuild/win32-ia32": "0.27.0", + "@esbuild/win32-x64": "0.27.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/miniflare": { + "version": "4.20260120.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260120.0.tgz", + "integrity": "sha512-XXZyE2pDKMtP5OLuv0LPHEAzIYhov4jrYjcqrhhqtxGGtXneWOHvXIPo+eV8sqwqWd3R7j4DlEKcyb+87BR49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "sharp": "^0.34.5", + "undici": "7.18.2", + "workerd": "1.20260120.0", + "ws": "8.18.0", + "youch": "4.1.0-beta.10", + "zod": "^3.25.76" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/undici": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz", + "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unenv": { + "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "pathe": "^2.0.3" + } + }, + "node_modules/workerd": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260120.0.tgz", + "integrity": "sha512-R6X/VQOkwLTBGLp4VRUwLQZZVxZ9T9J8pGiJ6GQUMaRkY7TVWrCSkVfoNMM1/YyFsY5UYhhPoQe5IehnhZ3Pdw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "workerd": "bin/workerd" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@cloudflare/workerd-darwin-64": "1.20260120.0", + "@cloudflare/workerd-darwin-arm64": "1.20260120.0", + "@cloudflare/workerd-linux-64": "1.20260120.0", + "@cloudflare/workerd-linux-arm64": "1.20260120.0", + "@cloudflare/workerd-windows-64": "1.20260120.0" + } + }, + "node_modules/wrangler": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.60.0.tgz", + "integrity": "sha512-n4kibm/xY0Qd5G2K/CbAQeVeOIlwPNVglmFjlDRCCYk3hZh8IggO/rg8AXt/vByK2Sxsugl5Z7yvgWxrUbmS6g==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "@cloudflare/kv-asset-handler": "0.4.2", + "@cloudflare/unenv-preset": "2.11.0", + "blake3-wasm": "2.1.5", + "esbuild": "0.27.0", + "miniflare": "4.20260120.0", + "path-to-regexp": "6.3.0", + "unenv": "2.0.0-rc.24", + "workerd": "1.20260120.0" + }, + "bin": { + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@cloudflare/workers-types": "^4.20260120.0" + }, + "peerDependenciesMeta": { + "@cloudflare/workers-types": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "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/youch": { + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" + } + }, + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} From dfd397803f010e23cf4c9397a06af815582b2bc2 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Thu, 22 Jan 2026 17:14:28 +0000 Subject: [PATCH 10/10] Bump version to 0.7.6 --- package-lock.json | 19 ++++++++----------- package.json | 2 +- packages/electron-app/package.json | 2 +- packages/server/package-lock.json | 4 ++-- packages/server/package.json | 2 +- packages/tauri-app/package.json | 2 +- packages/ui/package.json | 2 +- 7 files changed, 15 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b6f9eeff..0d3816575 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codenomad-workspace", - "version": "0.7.5", + "version": "0.7.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codenomad-workspace", - "version": "0.7.5", + "version": "0.7.6", "dependencies": { "7zip-bin": "^5.2.0", "google-auth-library": "^10.5.0" @@ -1632,7 +1632,6 @@ "version": "2.10.3", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "@types/node": "*" } @@ -2271,7 +2270,6 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", - "dev": true, "license": "MIT", "engines": { "node": "*" @@ -3674,7 +3672,6 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", - "dev": true, "license": "MIT", "dependencies": { "pend": "~1.2.0" @@ -5352,7 +5349,6 @@ }, "node_modules/pend": { "version": "1.2.0", - "dev": true, "license": "MIT" }, "node_modules/picocolors": { @@ -7324,7 +7320,6 @@ }, "node_modules/yauzl": { "version": "2.10.0", - "dev": true, "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", @@ -7389,7 +7384,7 @@ }, "packages/electron-app": { "name": "@neuralnomads/codenomad-electron-app", - "version": "0.7.5", + "version": "0.7.6", "dependencies": { "@codenomad/ui": "file:../ui", "@neuralnomads/codenomad": "file:../server" @@ -7423,7 +7418,7 @@ }, "packages/server": { "name": "@neuralnomads/codenomad", - "version": "0.7.5", + "version": "0.7.6", "dependencies": { "@fastify/cors": "^8.5.0", "@fastify/reply-from": "^9.8.0", @@ -7433,12 +7428,14 @@ "fuzzysort": "^2.0.4", "pino": "^9.4.0", "undici": "^6.19.8", + "yauzl": "^2.10.0", "zod": "^3.23.8" }, "bin": { "codenomad": "dist/bin.js" }, "devDependencies": { + "@types/yauzl": "^2.10.0", "cross-env": "^7.0.3", "ts-node": "^10.9.2", "tsx": "^4.20.6", @@ -7458,14 +7455,14 @@ }, "packages/tauri-app": { "name": "@codenomad/tauri-app", - "version": "0.7.5", + "version": "0.7.6", "devDependencies": { "@tauri-apps/cli": "^2.9.4" } }, "packages/ui": { "name": "@codenomad/ui", - "version": "0.7.5", + "version": "0.7.6", "dependencies": { "@git-diff-view/solid": "^0.0.8", "@kobalte/core": "0.13.11", diff --git a/package.json b/package.json index 4ac6e1a85..20b5ae297 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codenomad-workspace", - "version": "0.7.5", + "version": "0.7.6", "private": true, "description": "CodeNomad monorepo workspace", "workspaces": { diff --git a/packages/electron-app/package.json b/packages/electron-app/package.json index 01c46c8bd..05e986c7f 100644 --- a/packages/electron-app/package.json +++ b/packages/electron-app/package.json @@ -1,6 +1,6 @@ { "name": "@neuralnomads/codenomad-electron-app", - "version": "0.7.5", + "version": "0.7.6", "description": "CodeNomad - AI coding assistant", "author": { "name": "Neural Nomads", diff --git a/packages/server/package-lock.json b/packages/server/package-lock.json index e7e9d36b8..96fd85cf7 100644 --- a/packages/server/package-lock.json +++ b/packages/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "@neuralnomads/codenomad", - "version": "0.7.5", + "version": "0.7.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@neuralnomads/codenomad", - "version": "0.7.5", + "version": "0.7.6", "dependencies": { "@fastify/cors": "^8.5.0", "@fastify/reply-from": "^9.8.0", diff --git a/packages/server/package.json b/packages/server/package.json index 694335a1b..c3dbdd4eb 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@neuralnomads/codenomad", - "version": "0.7.5", + "version": "0.7.6", "description": "CodeNomad Server", "author": { "name": "Neural Nomads", diff --git a/packages/tauri-app/package.json b/packages/tauri-app/package.json index 87879ccef..8fab37525 100644 --- a/packages/tauri-app/package.json +++ b/packages/tauri-app/package.json @@ -1,6 +1,6 @@ { "name": "@codenomad/tauri-app", - "version": "0.7.5", + "version": "0.7.6", "private": true, "scripts": { "dev": "tauri dev", diff --git a/packages/ui/package.json b/packages/ui/package.json index 1012c639f..acbd42f62 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@codenomad/ui", - "version": "0.7.5", + "version": "0.7.6", "private": true, "type": "module", "scripts": {