diff --git a/.github/workflows/ai-triage.yml b/.github/workflows/ai-triage.yml index 7a725a0cc..04bc87ae8 100644 --- a/.github/workflows/ai-triage.yml +++ b/.github/workflows/ai-triage.yml @@ -96,41 +96,47 @@ jobs: Be generous in your assessment — only flag clear violations. Ambiguous cases should be marked as aligned. Do NOT flag issues/PRs that are legitimately reporting bugs or requesting features, even if they could be better written.`; - const response = await fetch('https://api.anthropic.com/v1/messages', { - method: 'POST', - headers: { - 'x-api-key': process.env.ANTHROPIC_API_KEY, - 'content-type': 'application/json', - 'anthropic-version': '2023-06-01' - }, - body: JSON.stringify({ - model: 'claude-haiku-4-5-20251001', - max_tokens: 1024, - messages: [{ role: 'user', content: prompt }] - }) - }); - - if (!response.ok) { - const err = await response.text(); - core.setFailed(`Anthropic API error: ${response.status} ${err}`); - return; - } - - const data = await response.json(); - const text = data.content[0].text; - - // Extract JSON from response (handle markdown code blocks) - const jsonMatch = text.match(/\{[\s\S]*\}/); - if (!jsonMatch) { - core.setFailed(`Could not parse Claude response: ${text}`); + if (!process.env.ANTHROPIC_API_KEY) { + core.warning('Skipping AI triage because ANTHROPIC_API_KEY is not configured.'); return; } let result; try { + const response = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'x-api-key': process.env.ANTHROPIC_API_KEY, + 'content-type': 'application/json', + 'anthropic-version': '2023-06-01' + }, + body: JSON.stringify({ + model: 'claude-haiku-4-5-20251001', + max_tokens: 1024, + messages: [{ role: 'user', content: prompt }] + }), + signal: AbortSignal.timeout(20000) + }); + + if (!response.ok) { + const err = await response.text(); + core.warning(`Skipping AI triage after Anthropic API error: ${response.status} ${err}`); + return; + } + + const data = await response.json(); + const text = data.content?.[0]?.text ?? ''; + + // Extract JSON from response (handle markdown code blocks) + const jsonMatch = text.match(/\{[\s\S]*\}/); + if (!jsonMatch) { + core.warning(`Skipping AI triage because the model response was not parseable JSON: ${text}`); + return; + } + result = JSON.parse(jsonMatch[0]); } catch (e) { - core.setFailed(`JSON parse error: ${e.message}\nRaw text: ${text}`); + core.warning(`Skipping AI triage after unexpected failure: ${e.message}`); return; } core.info(`Triage result: ${JSON.stringify(result, null, 2)}`); diff --git a/package-lock.json b/package-lock.json index f26359add..cae86f699 100644 --- a/package-lock.json +++ b/package-lock.json @@ -846,13 +846,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", - "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.17.tgz", + "integrity": "sha512-Ra7hjqAZf1OXRRMueB13qex7mFJRDK/pgCvdSFemXBT8KCGnQDPoKzHY1SjN+TjJVmnpSF14W5tJ1vDamFu+Gg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.13.0", - "fast-xml-parser": "5.4.1", + "@smithy/types": "^4.14.0", + "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" }, "engines": { @@ -1245,9 +1245,9 @@ } }, "node_modules/@discordjs/builders": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.14.0.tgz", - "integrity": "sha512-7pVKxVWkeLUtrTo9nTYkjRcJk0Hlms6lYervXAD7E7+K5lil9ms2JrEB1TalMiHvQMh7h1HJZ4fCJa0/vHpl4w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.14.1.tgz", + "integrity": "sha512-gSKkhXLqs96TCzk66VZuHHl8z2bQMJFGwrXC0f33ngK+FLNau4hU1PYny3DNJfNdSH+gVMzE85/d5FQ2BpcNwQ==", "license": "Apache-2.0", "dependencies": { "@discordjs/formatters": "^0.6.2", @@ -2002,9 +2002,9 @@ "link": true }, "node_modules/@hono/node-server": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", - "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -3694,9 +3694,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz", - "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.0.tgz", + "integrity": "sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4616,9 +4616,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", - "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.1.tgz", + "integrity": "sha512-0yaL8JdxTknKDILitVpfYfV2Ob6yb3udX/hK97M7I3jOeznBNxQPtVvTUtnhUkyHlxFWyr5Lvknmgzoc7jf+1Q==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -4679,9 +4679,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -5216,24 +5216,24 @@ ] }, "node_modules/discord.js": { - "version": "14.25.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.25.1.tgz", - "integrity": "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g==", + "version": "14.26.2", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.26.2.tgz", + "integrity": "sha512-feShi+gULJ6R2MAA4/KkCFnkJcuVrROJrKk4czplzq8gE1oqhqgOy9K0Scu44B8oGeWKe04egquzf+ia6VtXAw==", "license": "Apache-2.0", "dependencies": { - "@discordjs/builders": "^1.13.0", + "@discordjs/builders": "^1.14.1", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", - "@discordjs/rest": "^2.6.0", + "@discordjs/rest": "^2.6.1", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", - "discord-api-types": "^0.38.33", + "discord-api-types": "^0.38.40", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", - "magic-bytes.js": "^1.10.0", + "magic-bytes.js": "^1.13.0", "tslib": "^2.6.3", - "undici": "6.21.3" + "undici": "6.24.1" }, "engines": { "node": ">=18" @@ -5243,9 +5243,9 @@ } }, "node_modules/discord.js/node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "license": "MIT", "engines": { "node": ">=18.17" @@ -5281,9 +5281,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "41.0.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-41.0.3.tgz", - "integrity": "sha512-IDjx8liW1q+r7+MOip5W1Eo1eMwJzVObmYrd9yz2dPCkS7XlgLq3qPVMR80TpiROFp73iY30kTzMdpA6fEVs3A==", + "version": "41.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-41.2.0.tgz", + "integrity": "sha512-0OKLiymqfV0WK68RBXqAm3Myad2TpI5wwxLCBEUcH5Nugo3YfSk7p1Js/AL9266qTz5xZioUnxt9hG8FFwax0g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5703,9 +5703,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.2.tgz", - "integrity": "sha512-NJAmiuVaJEjVa7TjLZKlYd7RqmzOC91EtPFXHvlTcqBVo50Qh7XV5IwvXi1c7NRz2Q/majGX9YLcwJtWgHjtkA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", "funding": [ { "type": "github", @@ -5718,9 +5718,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", - "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", + "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==", "funding": [ { "type": "github", @@ -5729,8 +5729,9 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.0.0", - "strnum": "^2.1.2" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -5788,9 +5789,9 @@ } }, "node_modules/file-type": { - "version": "21.3.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.1.tgz", - "integrity": "sha512-SrzXX46I/zsRDjTb82eucsGg0ODq2NpGDp4HcsFKApPy8P8vACjpJRDoGGMfEzhFC0ry61ajd7f72J3603anBA==", + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", "license": "MIT", "dependencies": { "@tokenizer/inflate": "^0.4.1", @@ -6262,9 +6263,9 @@ } }, "node_modules/hono": { - "version": "4.12.8", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.8.tgz", - "integrity": "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==", + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", + "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -6923,9 +6924,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.snakecase": { @@ -7389,9 +7390,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", - "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.4.0.tgz", + "integrity": "sha512-s4DQMxIdhj3jLFWd9LxHOplj4p9yQ4ffMGowFf3cpEgrrJjEhN0V5nxw4Ye1EViAGDoL4/1AeO6qHpqYPOzE4Q==", "funding": [ { "type": "github", @@ -7429,9 +7430,9 @@ } }, "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -7451,9 +7452,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -8301,9 +8302,9 @@ } }, "node_modules/strnum": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", - "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", + "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", "funding": [ { "type": "github", @@ -8603,9 +8604,9 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", "peer": true, @@ -9299,9 +9300,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/src/resource-loader.ts b/src/resource-loader.ts index 30ad9c21d..901d8e1b1 100644 --- a/src/resource-loader.ts +++ b/src/resource-loader.ts @@ -413,12 +413,14 @@ export function initResources(agentDir: string): void { const currentVersion = getBundledGsdVersion() const manifest = readManagedResourceManifest(agentDir) + const extensionsDir = join(agentDir, 'extensions') // Always prune root-level extension files that were removed from the bundle. // This is cheap (a few existence checks + at most one rmSync) and must run // unconditionally so that stale files left by a previous version are cleaned // up even when the version/hash match causes the full sync to be skipped. pruneRemovedBundledExtensions(manifest, agentDir) + pruneStaleSiblingFiles(bundledExtensionsDir, extensionsDir) // Ensure ~/.gsd/agent/node_modules symlinks to GSD's node_modules on EVERY // launch, not just during resource syncs. A stale/broken symlink makes ALL @@ -435,7 +437,7 @@ export function initResources(agentDir: string): void { if (manifest && manifest.gsdVersion === currentVersion) { // Version matches — check content fingerprint for same-version staleness. const currentHash = computeResourceFingerprint() - const hasStaleExtensionFiles = hasStaleCompiledExtensionSiblings(join(agentDir, 'extensions')) + const hasStaleExtensionFiles = hasStaleCompiledExtensionSiblings(extensionsDir, bundledExtensionsDir) if (manifest.contentHash && manifest.contentHash === currentHash && !hasStaleExtensionFiles) { return } @@ -571,12 +573,26 @@ function migrateSkillsToEcosystemDir(agentDir: string): void { } } -export function hasStaleCompiledExtensionSiblings(extensionsDir: string): boolean { +export function hasStaleCompiledExtensionSiblings(extensionsDir: string, sourceDir: string = bundledExtensionsDir): boolean { if (!existsSync(extensionsDir)) return false + const sourceFiles = existsSync(sourceDir) + ? new Set( + readdirSync(sourceDir, { withFileTypes: true }) + .filter((entry) => entry.isFile()) + .map((entry) => entry.name), + ) + : new Set() for (const entry of readdirSync(extensionsDir, { withFileTypes: true })) { - if (!entry.isFile() || !entry.name.endsWith('.ts')) continue - const jsName = entry.name.replace(/\.ts$/, '.js') - if (existsSync(join(extensionsDir, jsName))) { + if (!entry.isFile()) continue + if (!entry.name.endsWith('.ts') && !entry.name.endsWith('.js')) continue + + const siblingName = entry.name.endsWith('.ts') + ? entry.name.replace(/\.ts$/, '.js') + : entry.name.replace(/\.js$/, '.ts') + + if (!existsSync(join(extensionsDir, siblingName))) continue + if (sourceFiles.has(entry.name) && sourceFiles.has(siblingName)) continue + if (sourceFiles.has(entry.name) || sourceFiles.has(siblingName)) { return true } } diff --git a/src/resources/extensions/get-secrets-from-user.ts b/src/resources/extensions/get-secrets-from-user.ts index 7fe418f59..a8f1cfe36 100644 --- a/src/resources/extensions/get-secrets-from-user.ts +++ b/src/resources/extensions/get-secrets-from-user.ts @@ -47,6 +47,14 @@ function shellEscapeSingle(value: string): string { return `'${value.replace(/'/g, `'\\''`)}'`; } +function isSafeEnvVarKey(key: string): boolean { + return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key); +} + +function isSupportedDeploymentEnvironment(env: string): boolean { + return env === "development" || env === "preview" || env === "production"; +} + function hydrateProcessEnv(key: string, value: string): void { // Make newly collected secrets immediately visible to the current session. // Some extensions read process.env directly and do not reload .env on every call. @@ -330,12 +338,22 @@ async function applySecrets( if ((destination === "vercel" || destination === "convex") && opts.exec) { const env = opts.environment ?? "development"; + if (!isSupportedDeploymentEnvironment(env)) { + errors.push(`environment: unsupported target environment "${env}"`); + return { applied, errors }; + } for (const { key, value } of provided) { + if (!isSafeEnvVarKey(key)) { + errors.push(`${key}: invalid environment variable name`); + continue; + } const cmd = destination === "vercel" ? `printf %s ${shellEscapeSingle(value)} | vercel env add ${key} ${env}` - : `npx convex env set ${key} ${shellEscapeSingle(value)}`; + : ""; try { - const result = await opts.exec("sh", ["-c", cmd]); + const result = destination === "vercel" + ? await opts.exec("sh", ["-c", cmd]) + : await opts.exec("npx", ["convex", "env", "set", key, value]); if (result.code !== 0) { errors.push(`${key}: ${result.stderr.slice(0, 200)}`); } else { diff --git a/src/resources/extensions/gsd/workflow-reconcile.ts b/src/resources/extensions/gsd/workflow-reconcile.ts index 6ad84a635..9f304cfbb 100644 --- a/src/resources/extensions/gsd/workflow-reconcile.ts +++ b/src/resources/extensions/gsd/workflow-reconcile.ts @@ -249,11 +249,6 @@ export function extractEntityKey( ? { type: "slice", id: p["sliceId"] } : null; - case "complete_milestone": - return typeof p["milestoneId"] === "string" - ? { type: "milestone", id: p["milestoneId"] } - : null; - case "plan_slice": return typeof p["sliceId"] === "string" ? { type: "slice_plan", id: p["sliceId"] } diff --git a/src/resources/extensions/gsd/worktree-manager.ts b/src/resources/extensions/gsd/worktree-manager.ts index d8eb3760a..37490a30b 100644 --- a/src/resources/extensions/gsd/worktree-manager.ts +++ b/src/resources/extensions/gsd/worktree-manager.ts @@ -124,8 +124,9 @@ export function worktreeBranchName(name: string): string { * nativeWorktreeRemove --force) to prevent #2365-style data loss. */ export function isInsideWorktreesDir(basePath: string, targetPath: string): boolean { - const wtDir = resolve(worktreesDir(basePath)); - const resolved = resolve(targetPath); + const wtDirPath = worktreesDir(basePath); + const wtDir = existsSync(wtDirPath) ? realpathSync(wtDirPath) : resolve(wtDirPath); + const resolved = existsSync(targetPath) ? realpathSync(targetPath) : resolve(targetPath); // The resolved path must start with the worktrees dir followed by a separator, // not merely be a prefix match (e.g. ".gsd/worktrees-extra" must not match). return resolved === wtDir || resolved.startsWith(wtDir + sep); @@ -517,6 +518,9 @@ export function removeWorktree( rmSync(wtInternalDir, { recursive: true, force: true }); } rmSync(resolvedWtPath, { recursive: true, force: true }); + if (wtPath !== resolvedWtPath && existsSync(wtPath)) { + rmSync(wtPath, { recursive: true, force: true }); + } } catch { logWarning( "reconcile", diff --git a/src/tests/integration/web-mode-assembled.test.ts b/src/tests/integration/web-mode-assembled.test.ts index d476c7c89..6bc3cafa5 100644 --- a/src/tests/integration/web-mode-assembled.test.ts +++ b/src/tests/integration/web-mode-assembled.test.ts @@ -350,6 +350,7 @@ test("assembled lifecycle: boot → onboard → prompt → streaming text → to onboarding.configureOnboardingServiceForTests({ authStorage, + getEnvApiKey: () => undefined, validateApiKey: async () => ({ ok: true, message: "openai credentials validated" }), }); @@ -694,6 +695,7 @@ test("assembled settings controls keep retry visibility and daily-use mutations authStorage: AuthStorage.inMemory({ anthropic: { type: "api_key", key: "sk-test-assembled-settings" }, } as any), + getEnvApiKey: () => undefined, }); t.after(async () => { @@ -964,6 +966,7 @@ test("assembled slash-command behavior keeps built-ins safe while preserving GSD authStorage: AuthStorage.inMemory({ anthropic: { type: "api_key", key: "sk-test-assembled-slash" }, } as any), + getEnvApiKey: () => undefined, }); t.after(async () => { diff --git a/src/tests/integration/web-mode-onboarding.test.ts b/src/tests/integration/web-mode-onboarding.test.ts index a3c9943a9..8977a42cf 100644 --- a/src/tests/integration/web-mode-onboarding.test.ts +++ b/src/tests/integration/web-mode-onboarding.test.ts @@ -301,6 +301,7 @@ test("successful browser onboarding restarts the stale bridge child and unlocks const harness = configureBridgeRuntime(fixture, authStorage); onboarding.configureOnboardingServiceForTests({ authStorage, + getEnvApiKey: () => undefined, validateApiKey: async () => ({ ok: true, message: "openai credentials validated" }), }); @@ -368,6 +369,7 @@ test("refresh failures keep the workspace locked and expose the failed bridge-re const harness = configureBridgeRuntime(fixture, authStorage, { failRestart: true }); onboarding.configureOnboardingServiceForTests({ authStorage, + getEnvApiKey: () => undefined, validateApiKey: async () => ({ ok: true, message: "openai credentials validated" }), }); diff --git a/src/tests/integration/web-mode-runtime-harness.ts b/src/tests/integration/web-mode-runtime-harness.ts index 62c491cec..3083d6bc9 100644 --- a/src/tests/integration/web-mode-runtime-harness.ts +++ b/src/tests/integration/web-mode-runtime-harness.ts @@ -13,6 +13,52 @@ const packagedWebHostPath = join(projectRoot, "dist", "web", "standalone", "serv let runtimeArtifactsReady = false +const SANITIZED_PROVIDER_ENV_KEYS = [ + "ANTHROPIC_OAUTH_TOKEN", + "ANTHROPIC_API_KEY", + "OPENAI_API_KEY", + "AZURE_OPENAI_API_KEY", + "GEMINI_API_KEY", + "GROQ_API_KEY", + "CEREBRAS_API_KEY", + "XAI_API_KEY", + "OPENROUTER_API_KEY", + "AI_GATEWAY_API_KEY", + "ZAI_API_KEY", + "MISTRAL_API_KEY", + "MINIMAX_API_KEY", + "MINIMAX_CN_API_KEY", + "HF_TOKEN", + "OPENCODE_API_KEY", + "KIMI_API_KEY", + "ALIBABA_API_KEY", + "COPILOT_GITHUB_TOKEN", + "GH_TOKEN", + "GITHUB_TOKEN", + "GOOGLE_APPLICATION_CREDENTIALS", + "GOOGLE_CLOUD_PROJECT", + "GCLOUD_PROJECT", + "GOOGLE_CLOUD_LOCATION", + "AWS_PROFILE", + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_BEARER_TOKEN_BEDROCK", + "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", + "AWS_CONTAINER_CREDENTIALS_FULL_URI", + "AWS_WEB_IDENTITY_TOKEN_FILE", +] as const + +function buildSanitizedRuntimeEnv(overrides?: NodeJS.ProcessEnv): NodeJS.ProcessEnv { + const env: NodeJS.ProcessEnv = { ...process.env } + for (const key of SANITIZED_PROVIDER_ENV_KEYS) { + env[key] = "" + } + return { + ...env, + ...overrides, + } +} + type RuntimeEndpoint = "boot" | "events" type RuntimeRequestDiagnostic = { @@ -147,12 +193,11 @@ export async function launchPackagedWebHost(options: { { cwd: options.launchCwd, env: { - ...process.env, + ...buildSanitizedRuntimeEnv(options.env), HOME: options.tempHome, PATH: `${fakeBin}:${process.env.PATH || ""}`, CI: "1", FORCE_COLOR: "0", - ...options.env, }, stdio: ["ignore", "pipe", "pipe"], }, diff --git a/src/tests/resource-loader.test.ts b/src/tests/resource-loader.test.ts index 03bbd5db2..06dd615c5 100644 --- a/src/tests/resource-loader.test.ts +++ b/src/tests/resource-loader.test.ts @@ -53,18 +53,24 @@ test("hasStaleCompiledExtensionSiblings only flags top-level .ts/.js sibling pai const { hasStaleCompiledExtensionSiblings } = await import("../resource-loader.ts"); const tmp = mkdtempSync(join(tmpdir(), "gsd-resource-loader-")); const extensionsDir = join(tmp, "extensions"); + const bundledDir = join(tmp, "bundled"); t.after(() => { rmSync(tmp, { recursive: true, force: true }); }); + mkdirSync(bundledDir, { recursive: true }); mkdirSync(join(extensionsDir, "gsd"), { recursive: true }); writeFileSync(join(extensionsDir, "gsd", "index.ts"), "export {};\n"); - assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir), false); + assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir, bundledDir), false); + writeFileSync(join(bundledDir, "ask-user-questions.js"), "export {};\n"); writeFileSync(join(extensionsDir, "ask-user-questions.js"), "export {};\n"); - assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir), false); + assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir, bundledDir), false); writeFileSync(join(extensionsDir, "ask-user-questions.ts"), "export {};\n"); - assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir), true); + assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir, bundledDir), true); + + writeFileSync(join(bundledDir, "ask-user-questions.ts"), "export {};\n"); + assert.equal(hasStaleCompiledExtensionSiblings(extensionsDir, bundledDir), false); }); test("buildResourceLoader excludes duplicate top-level pi extensions when bundled resources use .js", async (t) => { @@ -148,16 +154,30 @@ test("initResources prunes stale top-level extension siblings next to bundled co const staleSiblingPath = bundledPath.endsWith(".js") ? bundledTsPath : bundledJsPath; + const siblingWasBundled = existsSync(staleSiblingPath); + const staleContent = "export {};\n"; assert.equal(existsSync(bundledPath), true, "bundled top-level extension should exist"); // Simulate a stale opposite-format sibling left from a previous sync/build mismatch. - writeFileSync(staleSiblingPath, "export {};\n"); + writeFileSync(staleSiblingPath, staleContent); assert.equal(existsSync(staleSiblingPath), true); + // Force a full resync so this test exercises the prune/copy path rather than + // the early-return manifest fast path. + const manifestPath = join(fakeAgentDir, "managed-resources.json"); + const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")); + manifest.contentHash = "force-resync"; + writeFileSync(manifestPath, JSON.stringify(manifest)); + initResources(fakeAgentDir); - assert.equal(existsSync(staleSiblingPath), false, "stale top-level sibling should be removed during sync"); + if (siblingWasBundled) { + assert.equal(existsSync(staleSiblingPath), true, "bundled sibling should be restored during sync"); + assert.notEqual(readFileSync(staleSiblingPath, "utf-8"), staleContent, "bundled sibling should overwrite stale contents"); + } else { + assert.equal(existsSync(staleSiblingPath), false, "stale top-level sibling should be removed during sync"); + } assert.equal(existsSync(bundledPath), true, "bundled extension should remain after cleanup"); }); diff --git a/src/web/onboarding-service.ts b/src/web/onboarding-service.ts index 26f4d6883..259865da5 100644 --- a/src/web/onboarding-service.ts +++ b/src/web/onboarding-service.ts @@ -231,7 +231,9 @@ function resolveOnboardingLockReason( function hasStoredCredentialValue(authStorage: AuthStorageInstance, providerId: string): boolean { return authStorage.getCredentialsForProvider(providerId).some((credential) => { - if (credential.type === "oauth") return true; + if (credential.type === "oauth") { + return typeof credential.access === "string" && credential.access.trim().length > 0; + } return typeof credential.key === "string" && credential.key.trim().length > 0; }); } @@ -247,9 +249,6 @@ function resolveCredentialSource( if (getEnvApiKeyFn(providerId)) { return "environment"; } - if (authStorage.getCredentialsForProvider(providerId).length > 0) { - return "runtime"; - } return null; } diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json index 67102cd86..c7a0636db 100644 --- a/vscode-extension/package-lock.json +++ b/vscode-extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "gsd-2", - "version": "0.1.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gsd-2", - "version": "0.1.0", + "version": "0.3.0", "license": "MIT", "devDependencies": { "@types/vscode": "^1.95.0", @@ -955,9 +955,9 @@ "license": "BSD-2-Clause" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -1808,9 +1808,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2352,9 +2352,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -2903,9 +2903,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { diff --git a/vscode-extension/src/git-integration.ts b/vscode-extension/src/git-integration.ts index dbec79dba..82f727d51 100644 --- a/vscode-extension/src/git-integration.ts +++ b/vscode-extension/src/git-integration.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import { exec } from "node:child_process"; +import { execFile } from "node:child_process"; import type { GsdChangeTracker } from "./change-tracker.js"; /** @@ -31,11 +31,11 @@ export class GsdGitIntegration implements vscode.Disposable { }); if (!message) return; - try { - // Stage the modified files - await this.git(`add ${files.map((f) => `"${f}"`).join(" ")}`); - // Commit - await this.git(`commit -m "${message.replace(/"/g, '\\"')}"`); + try { + // Stage the modified files + await this.git(["add", ...files]); + // Commit + await this.git(["commit", "-m", message]); // Accept all changes (clear tracking since they're committed) this.tracker.acceptAll(); @@ -62,8 +62,8 @@ export class GsdGitIntegration implements vscode.Disposable { }); if (!branchName) return; - try { - await this.git(`checkout -b "${branchName}"`); + try { + await this.git(["checkout", "-b", branchName]); vscode.window.showInformationMessage(`Created and switched to branch: ${branchName}`); } catch (err) { const msg = err instanceof Error ? err.message : String(err); @@ -81,11 +81,11 @@ export class GsdGitIntegration implements vscode.Disposable { return; } - try { - const diff = await this.git("diff"); - if (!diff.trim()) { - // Files may be untracked — show status instead - const status = await this.git("status --short"); + try { + const diff = await this.git(["diff"]); + if (!diff.trim()) { + // Files may be untracked — show status instead + const status = await this.git(["status", "--short"]); const channel = vscode.window.createOutputChannel("GSD Git Diff"); channel.appendLine("# Agent-modified files (unstaged):"); channel.appendLine(status); @@ -108,9 +108,9 @@ export class GsdGitIntegration implements vscode.Disposable { } } - private git(args: string): Promise { + private git(args: string[]): Promise { return new Promise((resolve, reject) => { - exec(`git ${args}`, { cwd: this.cwd, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => { + execFile("git", args, { cwd: this.cwd, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => { if (err) { reject(new Error(stderr.trim() || err.message)); } else { diff --git a/web/package-lock.json b/web/package-lock.json index fd7c24a3c..8fb9c84a1 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -52,7 +52,7 @@ "input-otp": "1.4.2", "lucide-react": "^0.564.0", "motion": "^12.36.0", - "next": "16.1.6", + "next": "16.2.3", "next-themes": "^0.4.6", "node-pty": "^1.1.0", "react": "19.2.4", @@ -77,7 +77,7 @@ "@types/react-dom": "19.2.3", "esbuild": "^0.27.4", "eslint": "^9.38.0", - "eslint-config-next": "16.1.6", + "eslint-config-next": "16.2.3", "postcss": "^8.5", "tailwindcss": "^4.2.0", "tw-animate-css": "1.3.3", @@ -2181,15 +2181,15 @@ } }, "node_modules/@next/env": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", - "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.3.tgz", + "integrity": "sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz", - "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.3.tgz", + "integrity": "sha512-nE/b9mht28XJxjTwKs/yk7w4XTaU3t40UHVAky6cjiijdP/SEy3hGsnQMPxmXPTpC7W4/97okm6fngKnvCqVaA==", "dev": true, "license": "MIT", "dependencies": { @@ -2197,9 +2197,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", - "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.3.tgz", + "integrity": "sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==", "cpu": [ "arm64" ], @@ -2213,9 +2213,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", - "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.3.tgz", + "integrity": "sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==", "cpu": [ "x64" ], @@ -2229,12 +2229,15 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", - "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.3.tgz", + "integrity": "sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2245,12 +2248,15 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", - "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.3.tgz", + "integrity": "sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2261,12 +2267,15 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", - "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.3.tgz", + "integrity": "sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2277,12 +2286,15 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", - "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.3.tgz", + "integrity": "sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2293,9 +2305,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", - "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.3.tgz", + "integrity": "sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==", "cpu": [ "arm64" ], @@ -2309,9 +2321,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", - "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.3.tgz", + "integrity": "sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==", "cpu": [ "x64" ], @@ -4882,9 +4894,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5731,9 +5743,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -6785,13 +6797,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz", - "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.3.tgz", + "integrity": "sha512-Dnkrylzjof/Az7iNoIQJqD18zTxQZcngir19KJaiRsMnnjpQSVoa6aEg/1Q4hQC+cW90uTlgQYadwL1CYNwFWA==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.1.6", + "@next/eslint-plugin-next": "16.2.3", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -7324,9 +7336,9 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -8764,9 +8776,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.merge": { @@ -9828,14 +9840,14 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", - "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.3.tgz", + "integrity": "sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==", "license": "MIT", "dependencies": { - "@next/env": "16.1.6", + "@next/env": "16.2.3", "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.8.3", + "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -9847,15 +9859,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.6", - "@next/swc-darwin-x64": "16.1.6", - "@next/swc-linux-arm64-gnu": "16.1.6", - "@next/swc-linux-arm64-musl": "16.1.6", - "@next/swc-linux-x64-gnu": "16.1.6", - "@next/swc-linux-x64-musl": "16.1.6", - "@next/swc-win32-arm64-msvc": "16.1.6", - "@next/swc-win32-x64-msvc": "16.1.6", - "sharp": "^0.34.4" + "@next/swc-darwin-arm64": "16.2.3", + "@next/swc-darwin-x64": "16.2.3", + "@next/swc-linux-arm64-gnu": "16.2.3", + "@next/swc-linux-arm64-musl": "16.2.3", + "@next/swc-linux-x64-gnu": "16.2.3", + "@next/swc-linux-x64-musl": "16.2.3", + "@next/swc-win32-arm64-msvc": "16.2.3", + "@next/swc-win32-x64-msvc": "16.2.3", + "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -10248,9 +10260,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -11453,9 +11465,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/web/package.json b/web/package.json index a41573ffe..1763afe22 100644 --- a/web/package.json +++ b/web/package.json @@ -55,7 +55,7 @@ "input-otp": "1.4.2", "lucide-react": "^0.564.0", "motion": "^12.36.0", - "next": "16.1.6", + "next": "16.2.3", "next-themes": "^0.4.6", "node-pty": "^1.1.0", "react": "19.2.4", @@ -80,7 +80,7 @@ "@types/react-dom": "19.2.3", "esbuild": "^0.27.4", "eslint": "^9.38.0", - "eslint-config-next": "16.1.6", + "eslint-config-next": "16.2.3", "postcss": "^8.5", "tailwindcss": "^4.2.0", "tw-animate-css": "1.3.3",