diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 36305930..27eaf814 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -22,6 +22,8 @@ "@edgeever/public-network": "workspace:*", "croner": "^10.0.1", "electron-updater": "^6.6.2", + "fflate": "0.8.3", + "npm": "11.6.2", "zod": "^4.6.5" } } diff --git a/apps/desktop/src/main/acp-adapter-manager.mjs b/apps/desktop/src/main/acp-adapter-manager.mjs new file mode 100644 index 00000000..21addc13 --- /dev/null +++ b/apps/desktop/src/main/acp-adapter-manager.mjs @@ -0,0 +1,270 @@ +import { spawn } from "node:child_process"; +import { createHash } from "node:crypto"; +import { createReadStream, createWriteStream, existsSync, readFileSync } from "node:fs"; +import { chmod, mkdir, open, readdir, rename, rm } from "node:fs/promises"; +import { once } from "node:events"; +import { createRequire } from "node:module"; +import path from "node:path"; +import { finished } from "node:stream/promises"; +import { Readable } from "node:stream"; +import { Unzip, UnzipInflate } from "fflate"; + +const require = createRequire(import.meta.url); +const npmCli = path.join(path.dirname(require.resolve("npm")), "bin", "npm-cli.js"); +const REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json"; +const MAX_ARCHIVE_BYTES = 300 * 1024 * 1024; +const MAX_EXTRACTED_BYTES = 1024 * 1024 * 1024; +const VERSION_PATTERN = /^\d+\.\d+\.\d+$/; +const SUPPORTED_MAJOR = { codex: 2, antigravity: 1 }; + +const platformTarget = (platform, arch) => { + const system = { darwin: "darwin", linux: "linux", win32: "windows" }[platform]; + const cpu = { arm64: "aarch64", x64: "x86_64" }[arch]; + return system && cpu ? `${system}-${cpu}` : null; +}; + +const compareVersions = (left, right) => { + const a = left.split(".").map(Number); + const b = right.split(".").map(Number); + for (let i = 0; i < 3; i += 1) if (a[i] !== b[i]) return a[i] - b[i]; + return 0; +}; + +const commandFile = (root, id, version, platform) => id === "codex" + ? path.join(root, id, version, "node_modules", "@agentclientprotocol", "codex-acp", "dist", "index.js") + : path.join(root, id, version, platform === "win32" ? "agy_acp_server.exe" : "agy_acp_server.par"); + +const antigravityArgs = (platform) => platform === "linux" ? ["--uid="] : []; + +export const managedAdapterCommand = (root, id, version, { platform = process.platform, executablePath = process.execPath } = {}) => { + if (!VERSION_PATTERN.test(version) || (id !== "codex" && id !== "antigravity")) return null; + const file = commandFile(root, id, version, platform); + if (!existsSync(file)) return null; + return id === "codex" + ? { command: executablePath, args: [file], env: { ELECTRON_RUN_AS_NODE: "1" } } + : { command: file, args: antigravityArgs(platform) }; +}; + +const readRegistry = async (fetchImpl) => { + const response = await fetchImpl(REGISTRY_URL, { signal: AbortSignal.timeout(15_000), redirect: "error" }); + if (!response.ok) throw new Error("registry_unavailable"); + const contentLength = Number(response.headers.get("content-length") || 0); + if (contentLength > 1024 * 1024) throw new Error("registry_too_large"); + const raw = await response.text(); + if (raw.length > 1024 * 1024) throw new Error("registry_too_large"); + const agents = JSON.parse(raw).agents; + if (!Array.isArray(agents)) throw new Error("invalid_registry"); + return agents; +}; + +export const selectAdapterRelease = (agents, id, { platform = process.platform, arch = process.arch } = {}) => { + const entry = agents.find((agent) => agent?.id === (id === "codex" ? "codex-acp" : "antigravity-acp")); + if (!entry || !VERSION_PATTERN.test(entry.version)) throw new Error("unsupported_release"); + if (Number(entry.version.split(".")[0]) !== SUPPORTED_MAJOR[id]) throw new Error("unsupported_release"); + if (id === "codex") { + const packageName = entry.distribution?.npx?.package; + if (packageName !== `@agentclientprotocol/codex-acp@${entry.version}`) throw new Error("unsupported_release"); + return { id, version: entry.version, kind: "npm", packageName }; + } + const target = platformTarget(platform, arch); + const binary = target && entry.distribution?.binary?.[target]; + const archive = binary?.archive; + if (!archive || !/^https:\/\/dl\.google\.com\/agy-extensions\/releases\/[^?#]+\.zip$/.test(archive)) { + throw new Error("unsupported_release"); + } + const expectedCommand = platform === "win32" ? "agy_acp_server.exe" : "agy_acp_server.par"; + if (binary.cmd !== `./${expectedCommand}`) throw new Error("unsupported_release"); + if (JSON.stringify(binary.args ?? []) !== JSON.stringify(antigravityArgs(platform))) throw new Error("unsupported_release"); + if (binary.sha256 != null && !/^[a-fA-F0-9]{64}$/.test(binary.sha256)) throw new Error("unsupported_release"); + return { id, version: entry.version, kind: "binary", archive, sha256: binary.sha256 ?? null }; +}; + +const runNpm = (args, cwd, { executablePath = process.execPath, spawnImpl = spawn, npmPath = npmCli } = {}) => new Promise((resolve, reject) => { + const child = spawnImpl(executablePath, [npmPath, ...args], { + cwd, + env: { ...process.env, ELECTRON_RUN_AS_NODE: "1", npm_config_update_notifier: "false" }, + stdio: "ignore", + windowsHide: true, + shell: false, + }); + const timer = setTimeout(() => child.kill(), 5 * 60 * 1000); + timer.unref?.(); + child.once("error", (error) => { clearTimeout(timer); reject(error); }); + child.once("exit", (code) => { + clearTimeout(timer); + code === 0 ? resolve() : reject(new Error("adapter_install_failed")); + }); +}); + +const downloadArchive = async (url, destination, expectedHash, fetchImpl) => { + const response = await fetchImpl(url, { signal: AbortSignal.timeout(120_000), redirect: "error" }); + if (!response.ok || !response.body) throw new Error("adapter_download_failed"); + if (Number(response.headers.get("content-length") || 0) > MAX_ARCHIVE_BYTES) throw new Error("adapter_too_large"); + const handle = await open(destination, "wx", 0o600); + const hash = createHash("sha256"); + let bytes = 0; + try { + for await (const chunk of Readable.fromWeb(response.body)) { + bytes += chunk.length; + if (bytes > MAX_ARCHIVE_BYTES) throw new Error("adapter_too_large"); + hash.update(chunk); + let offset = 0; + while (offset < chunk.length) { + const { bytesWritten } = await handle.write(chunk, offset, chunk.length - offset); + if (!bytesWritten) throw new Error("adapter_download_failed"); + offset += bytesWritten; + } + } + } finally { + await handle.close(); + } + if (expectedHash && hash.digest("hex").toLowerCase() !== expectedHash.toLowerCase()) throw new Error("adapter_integrity_failed"); +}; + +const extractAntigravity = async (archive, destination, platform) => { + const required = platform === "win32" ? "agy_acp_server.exe" : "agy_acp_server.par"; + const allowed = new Set([required, "localharness_external"]); + const extracted = new Set(); + let total = 0; + let failure = null; + let drain = null; + const writes = []; + const unzip = new Unzip((file) => { + if (!allowed.has(file.name) || extracted.has(file.name) || file.compression !== 8) { + failure = new Error("invalid_adapter_archive"); + return; + } + extracted.add(file.name); + const output = createWriteStream(path.join(destination, file.name), { flags: "wx", mode: 0o700 }); + const done = finished(output); + void done.catch(() => {}); + writes.push(done); + file.ondata = (error, chunk, final) => { + if (error) { failure = error; output.destroy(error); return; } + total += chunk.length; + if (total > MAX_EXTRACTED_BYTES) { + failure = new Error("adapter_too_large"); + output.destroy(failure); + return; + } + if (chunk.length && !output.write(chunk) && !final) drain = once(output, "drain"); + if (final) output.end(); + }; + file.start(); + }); + unzip.register(UnzipInflate); + try { + for await (const chunk of createReadStream(archive, { highWaterMark: 64 * 1024 })) { + if (failure) throw failure; + unzip.push(chunk); + if (drain) { await drain; drain = null; } + } + unzip.push(new Uint8Array(), true); + if (failure) throw failure; + await Promise.all(writes); + if (!extracted.has(required)) throw new Error("invalid_adapter_archive"); + } catch (error) { + for (const promise of writes) void promise.catch(() => {}); + throw error; + } +}; + +export function createAcpAdapterManager({ root, fetchImpl = fetch, platform = process.platform, arch = process.arch, executablePath = process.execPath, spawnImpl = spawn, npmPath = npmCli, installNpmImpl = runNpm, downloadImpl = downloadArchive, extractImpl = extractAntigravity } = {}) { + if (!root || !path.isAbsolute(root)) throw new Error("invalid_adapter_store"); + const manifestPath = path.join(root, "installed.json"); + const inFlight = new Map(); + let saveQueue = Promise.resolve(); + let state = {}; + try { state = JSON.parse(readFileSync(manifestPath, "utf8")); } catch { /* First run or damaged manifest. */ } + if (!state || typeof state !== "object" || Array.isArray(state)) state = {}; + + const get = (id) => { + const version = state[id]?.version; + if (typeof version !== "string") return null; + const command = managedAdapterCommand(root, id, version, { platform, executablePath }); + return command ? { id, version, command } : null; + }; + + const save = (id, version) => { + const write = saveQueue.then(async () => { + const next = { ...state, [id]: { version } }; + await mkdir(root, { recursive: true, mode: 0o700 }); + const temporary = `${manifestPath}.${process.pid}.tmp`; + const file = await open(temporary, "w", 0o600); + try { await file.writeFile(JSON.stringify(next)); } finally { await file.close(); } + await rename(temporary, manifestPath); + state = next; + }); + saveQueue = write.catch(() => {}); + return write; + }; + + const install = (id, validate, { updateOnly = false } = {}) => { + if (id !== "codex" && id !== "antigravity") return Promise.reject(new Error("unknown_adapter")); + if (inFlight.has(id)) return inFlight.get(id); + const task = (async () => { + const previous = get(id); + if (updateOnly && !previous) return { updated: false, reason: "not_managed" }; + const release = selectAdapterRelease(await readRegistry(fetchImpl), id, { platform, arch }); + if (previous && compareVersions(release.version, previous.version) <= 0) return { updated: false, version: previous.version }; + const versionRoot = path.join(root, id); + const target = path.join(versionRoot, release.version); + const stage = path.join(versionRoot, `.stage-${release.version}-${process.pid}-${Date.now()}`); + await mkdir(stage, { recursive: true, mode: 0o700 }); + try { + if (release.kind === "npm") { + await installNpmImpl(["install", "--prefix", stage, "--ignore-scripts", "--no-audit", "--no-fund", "--package-lock=false", "--registry=https://registry.npmjs.org", release.packageName], stage, { executablePath, spawnImpl, npmPath }); + } else { + const archive = path.join(stage, "adapter.zip"); + await downloadImpl(release.archive, archive, release.sha256, fetchImpl); + await extractImpl(archive, stage, platform); + await rm(archive); + if (platform !== "win32") await chmod(path.join(stage, "agy_acp_server.par"), 0o700); + } + // Inspect the staged command before making it visible to future sessions. + const stagedFile = release.kind === "npm" + ? path.join(stage, "node_modules", "@agentclientprotocol", "codex-acp", "dist", "index.js") + : path.join(stage, platform === "win32" ? "agy_acp_server.exe" : "agy_acp_server.par"); + if (!existsSync(stagedFile)) throw new Error("adapter_install_failed"); + const stagedCommand = release.kind === "npm" + ? { command: executablePath, args: [stagedFile], env: { ELECTRON_RUN_AS_NODE: "1" } } + : { command: stagedFile, args: antigravityArgs(platform) }; + const result = await validate(stagedCommand); + if (result?.state === "needs_login" && previous) { + const previousResult = await validate(previous.command); + if (previousResult?.state !== "needs_login") throw new Error("adapter_probe_failed"); + } else if (result?.state !== "available" && !(result?.state === "needs_login" && !previous)) { + throw new Error(result?.detail || result?.state || "adapter_probe_failed"); + } + await rm(target, { recursive: true, force: true }); + await rename(stage, target); + await save(id, release.version); + return { updated: true, version: release.version, adapter: result }; + } catch (error) { + await rm(stage, { recursive: true, force: true }); + throw error; + } + })(); + inFlight.set(id, task); + void task.finally(() => inFlight.delete(id)).catch(() => {}); + return task; + }; + + const prune = async () => { + for (const id of ["codex", "antigravity"]) { + const current = get(id)?.version; + if (!current) continue; + const folder = path.join(root, id); + const names = await readdir(folder).catch(() => []); + const versions = names.filter((name) => VERSION_PATTERN.test(name) && name !== current) + .sort((a, b) => compareVersions(b, a)); + for (const name of names) { + if (name.startsWith(".stage-") || (versions.includes(name) && name !== versions[0])) { + await rm(path.join(folder, name), { recursive: true, force: true }); + } + } + } + }; + + return { get, install, prune, installedIds: () => ["codex", "antigravity"].filter((id) => get(id)) }; +} diff --git a/apps/desktop/src/main/acp-adapter-manager.test.mjs b/apps/desktop/src/main/acp-adapter-manager.test.mjs new file mode 100644 index 00000000..73fdb17f --- /dev/null +++ b/apps/desktop/src/main/acp-adapter-manager.test.mjs @@ -0,0 +1,146 @@ +import { describe, expect, test } from "bun:test"; +import { existsSync } from "node:fs"; +import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { zipSync } from "fflate"; +import { createAcpAdapterManager, managedAdapterCommand, selectAdapterRelease } from "./acp-adapter-manager.mjs"; + +const codexEntry = (version) => ({ + id: "codex-acp", + version, + distribution: { npx: { package: `@agentclientprotocol/codex-acp@${version}` } }, +}); + +describe("managed ACP adapters", () => { + test("accepts only the supported official distribution and platform command", () => { + expect(selectAdapterRelease([codexEntry("2.0.1")], "codex").version).toBe("2.0.1"); + expect(() => selectAdapterRelease([{ ...codexEntry("2.0.1"), distribution: { npx: { package: "evil@2.0.1" } } }], "codex")).toThrow("unsupported_release"); + const entry = { + id: "antigravity-acp", + version: "1.2.1", + distribution: { binary: { "darwin-aarch64": { + archive: "https://dl.google.com/agy-extensions/releases/macos/test.zip", + cmd: "./agy_acp_server.par", + } } }, + }; + expect(selectAdapterRelease([entry], "antigravity", { platform: "darwin", arch: "arm64" }).kind).toBe("binary"); + entry.distribution.binary["linux-x86_64"] = { + archive: "https://dl.google.com/agy-extensions/releases/linux/test.zip", + cmd: "./agy_acp_server.par", + args: ["--uid="], + }; + expect(selectAdapterRelease([entry], "antigravity", { platform: "linux", arch: "x64" }).kind).toBe("binary"); + entry.distribution.binary["linux-x86_64"].args = []; + expect(() => selectAdapterRelease([entry], "antigravity", { platform: "linux", arch: "x64" })).toThrow("unsupported_release"); + entry.distribution.binary["darwin-aarch64"].archive = "https://example.com/test.zip"; + expect(() => selectAdapterRelease([entry], "antigravity", { platform: "darwin", arch: "arm64" })).toThrow("unsupported_release"); + }); + + test("keeps the previous version when a staged update fails the ACP handshake", async () => { + const root = await mkdtemp(path.join(tmpdir(), "edgeever-managed-acp-")); + let release = "2.0.1"; + const manager = createAcpAdapterManager({ + root, + platform: "darwin", + arch: "arm64", + executablePath: "/usr/bin/node", + fetchImpl: async () => new Response(JSON.stringify({ agents: [codexEntry(release)] }), { status: 200 }), + installNpmImpl: async (_args, stage) => { + const directory = path.join(stage, "node_modules", "@agentclientprotocol", "codex-acp", "dist"); + await mkdir(directory, { recursive: true }); + await writeFile(path.join(directory, "index.js"), "// fake agent"); + }, + }); + const valid = async () => ({ id: "codex", label: "Codex", state: "available" }); + try { + expect((await manager.install("codex", valid)).updated).toBe(true); + expect(manager.get("codex")?.version).toBe("2.0.1"); + expect(manager.installedIds()).toEqual(["codex"]); + expect((await manager.install("codex", valid, { updateOnly: true })).updated).toBe(false); + + release = "2.0.2"; + await expect(manager.install("codex", async () => ({ state: "failed", detail: "handshake_failed" }), { updateOnly: true })).rejects.toThrow("handshake_failed"); + expect(manager.get("codex")?.version).toBe("2.0.1"); + expect(JSON.parse(await readFile(path.join(root, "installed.json"), "utf8")).codex.version).toBe("2.0.1"); + + expect((await manager.install("codex", valid, { updateOnly: true })).updated).toBe(true); + expect(manager.get("codex")?.version).toBe("2.0.2"); + expect(await readFile(path.join(root, "codex", "2.0.1", "node_modules", "@agentclientprotocol", "codex-acp", "dist", "index.js"), "utf8")).toBe("// fake agent"); + + release = "2.0.3"; + expect((await manager.install("codex", valid, { updateOnly: true })).updated).toBe(true); + await manager.prune(); + expect(existsSync(path.join(root, "codex", "2.0.1"))).toBe(false); + expect(existsSync(path.join(root, "codex", "2.0.2"))).toBe(true); + expect(manager.get("codex")?.version).toBe("2.0.3"); + } finally { + await rm(root, { recursive: true, force: true }); + } + }); + + test("extracts only expected Antigravity files and rejects archive traversal", async () => { + const root = await mkdtemp(path.join(tmpdir(), "edgeever-managed-agy-")); + let malicious = false; + const entry = { + id: "antigravity-acp", + version: "1.2.1", + distribution: { binary: { "darwin-aarch64": { + archive: "https://dl.google.com/agy-extensions/releases/macos/test.zip", + cmd: "./agy_acp_server.par", + } } }, + }; + const manager = createAcpAdapterManager({ + root, + platform: "darwin", + arch: "arm64", + fetchImpl: async () => new Response(JSON.stringify({ agents: [entry] }), { status: 200 }), + downloadImpl: async (_url, destination) => { + const files = malicious + ? { "../outside": [new Uint8Array([1]), { level: 1 }] } + : { "agy_acp_server.par": [new Uint8Array([1]), { level: 1 }], localharness_external: [new Uint8Array([2]), { level: 1 }] }; + await writeFile(destination, zipSync(files)); + }, + }); + try { + malicious = true; + await expect(manager.install("antigravity", async () => ({ state: "available" }))).rejects.toThrow("invalid_adapter_archive"); + expect(existsSync(path.join(root, "outside"))).toBe(false); + malicious = false; + expect((await manager.install("antigravity", async () => ({ state: "available" }))).updated).toBe(true); + expect(manager.get("antigravity")?.version).toBe("1.2.1"); + expect(managedAdapterCommand(root, "antigravity", "1.2.1", { platform: "linux" })?.args).toEqual(["--uid="]); + } finally { + await rm(root, { recursive: true, force: true }); + } + }); + + test("updates an unauthenticated connector only when the previous version also needs login", async () => { + const root = await mkdtemp(path.join(tmpdir(), "edgeever-acp-login-update-")); + let release = "2.0.1"; + const manager = createAcpAdapterManager({ + root, + platform: "darwin", + arch: "arm64", + executablePath: "/usr/bin/node", + fetchImpl: async () => new Response(JSON.stringify({ agents: [codexEntry(release)] }), { status: 200 }), + installNpmImpl: async (_args, stage) => { + const directory = path.join(stage, "node_modules", "@agentclientprotocol", "codex-acp", "dist"); + await mkdir(directory, { recursive: true }); + await writeFile(path.join(directory, "index.js"), "// fake agent"); + }, + }); + try { + await manager.install("codex", async () => ({ state: "needs_login" })); + release = "2.0.2"; + await expect(manager.install("codex", async (command) => ({ + state: command.args[0].includes(".stage-") ? "needs_login" : "available", + }), { updateOnly: true })).rejects.toThrow("adapter_probe_failed"); + expect(manager.get("codex")?.version).toBe("2.0.1"); + expect((await manager.install("codex", async () => ({ state: "needs_login" }), { updateOnly: true })).updated).toBe(true); + expect(manager.get("codex")?.version).toBe("2.0.2"); + } finally { + await rm(root, { recursive: true, force: true }); + } + }); +}); diff --git a/apps/desktop/src/main/acp-host.mjs b/apps/desktop/src/main/acp-host.mjs index 1238ff84..4c5a5f65 100644 --- a/apps/desktop/src/main/acp-host.mjs +++ b/apps/desktop/src/main/acp-host.mjs @@ -1,16 +1,20 @@ import { spawn as nodeSpawn } from "node:child_process"; import { randomUUID } from "node:crypto"; -import { accessSync, constants as fsConstants, readFileSync, realpathSync, statSync } from "node:fs"; +import { accessSync, constants as fsConstants, existsSync, readFileSync, realpathSync, statSync } from "node:fs"; import { mkdtemp, rm } from "node:fs/promises"; import { homedir, tmpdir } from "node:os"; import path from "node:path"; import { Readable, Writable } from "node:stream"; import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, RequestError } from "@agentclientprotocol/sdk"; import { waitForChildProcessSpawn } from "./child-process-start.mjs"; +import { createAcpAdapterManager } from "./acp-adapter-manager.mjs"; const ADAPTERS = { codex: { id: "codex", label: "Codex" }, antigravity: { id: "antigravity", label: "Antigravity" }, + grokBuild: { id: "grokBuild", label: "Grok Build" }, + workbuddyCn: { id: "workbuddyCn", label: "WorkBuddy 中国版" }, + workbuddyIntl: { id: "workbuddyIntl", label: "WorkBuddy International" }, }; const AUTH_REQUIRED_CODE = -32000; const MAX_ATTACHMENTS = 8; @@ -68,8 +72,40 @@ const findOnPath = (names, { access, pathEnv, delimiter, sep }) => { return null; }; +export function detectInstalledAgentApps({ + platform = process.platform, + pathEnv = process.env.PATH ?? "", + home = homedir(), + env = process.env, + access = accessSync, + exists = existsSync, +} = {}) { + const lookup = (names) => !!findOnPath(names, { + access, + pathEnv, + delimiter: platform === "win32" ? ";" : ":", + sep: platform === "win32" ? "\\" : "/", + }); + const candidates = (id) => { + if (platform === "darwin") return id === "codex" + ? ["/Applications/Codex.app", path.join(home, "Applications", "Codex.app"), "/Applications/ChatGPT.app/Contents/Resources/codex"] + : ["/Applications/Antigravity.app", path.join(home, "Applications", "Antigravity.app")]; + if (platform === "win32") return id === "codex" + ? [env.LOCALAPPDATA && path.join(env.LOCALAPPDATA, "Programs", "Codex", "Codex.exe")] + : [env.LOCALAPPDATA && path.join(env.LOCALAPPDATA, "Programs", "Antigravity", "Antigravity.exe"), env.PROGRAMFILES && path.join(env.PROGRAMFILES, "Antigravity", "Antigravity.exe")]; + return []; + }; + return ["codex", "antigravity"].filter((id) => { + const names = platform === "win32" ? [`${id}.exe`, id] : [id]; + return lookup(names) || candidates(id).some((candidate) => candidate && exists(candidate)); + }); +} + const resolutionDeps = (deps = {}) => ({ platform: deps.platform ?? process.platform, + home: deps.home ?? homedir(), + env: deps.env ?? process.env, + executablePath: deps.executablePath ?? process.execPath, access: deps.accessSync ?? accessSync, realpath: deps.realpathSync ?? realpathSync, stat: deps.statSync ?? statSync, @@ -81,9 +117,65 @@ const resolutionDeps = (deps = {}) => ({ export function resolveAcpCommand(input, deps = {}) { const resolved = resolutionDeps(deps); const id = input?.id; - if (id !== "codex" && id !== "antigravity") return { ok: false, state: "failed", detail: "unknown_adapter" }; + if (!Object.hasOwn(ADAPTERS, id)) return { ok: false, state: "failed", detail: "unknown_adapter" }; if (id === "codex") return resolveCodex(input?.path, resolved); - return resolveAntigravity(input?.path, resolved); + if (id === "antigravity") return resolveAntigravity(input?.path, resolved); + if (id === "grokBuild") return resolveGrok(input?.path, resolved); + return resolveWorkBuddy(id, resolved); +} + +function resolveWorkBuddy(id, deps) { + const homeApplications = path.join(deps.home, "Applications"); + const macApps = id === "workbuddyCn" ? ["WorkBuddy.app", "WorkBuddy AI.app"] : ["WorkBuddy AI.app", "WorkBuddy.app"]; + const candidates = deps.platform === "darwin" + ? macApps.flatMap((name) => [path.join("/Applications", name), path.join(homeApplications, name)]) + .map((app) => path.join(app, "Contents", "Resources", "app.asar.unpacked", "cli", "bin", "codebuddy")) + : deps.platform === "win32" + ? [deps.env.LOCALAPPDATA && path.join(deps.env.LOCALAPPDATA, "Programs", "WorkBuddy"), deps.env.LOCALAPPDATA && path.join(deps.env.LOCALAPPDATA, "WorkBuddy"), deps.env.PROGRAMFILES && path.join(deps.env.PROGRAMFILES, "WorkBuddy")] + .filter(Boolean).map((folder) => path.join(folder, "resources", "app.asar.unpacked", "cli", "bin", "codebuddy")) + : []; + for (const candidate of candidates) { + try { + const actual = deps.realpath(candidate); + if (!deps.stat(actual).isFile()) continue; + deps.access(actual, fsConstants.R_OK); + return { ok: true, command: workBuddyCommand(deps.executablePath, actual, id) }; + } catch { /* Try another installed WorkBuddy application. */ } + } + const external = findOnPath(deps.platform === "win32" ? ["codebuddy.exe"] : ["codebuddy"], deps); + return external + ? { ok: true, command: workBuddyCommand(external, null, id) } + : { ok: false, state: "not_installed" }; +} + +function workBuddyCommand(executablePath, bundledCli, id) { + const edition = id === "workbuddyCn" ? { CODEBUDDY_INTERNET_ENVIRONMENT: "internal" } : {}; + return bundledCli + ? { command: executablePath, args: [bundledCli, "--acp"], env: { ...edition, ELECTRON_RUN_AS_NODE: "1" }, ...(id === "workbuddyIntl" ? { unsetEnv: ["CODEBUDDY_INTERNET_ENVIRONMENT"] } : {}) } + : { command: executablePath, args: ["--acp"], env: edition, ...(id === "workbuddyIntl" ? { unsetEnv: ["CODEBUDDY_INTERNET_ENVIRONMENT"] } : {}) }; +} + +function resolveGrok(configured, deps) { + const executable = deps.platform === "win32" ? "grok.exe" : "grok"; + const explicit = typeof configured === "string" && configured.trim() ? configured.trim() : null; + if (explicit && (explicit.includes("\0") || !path.isAbsolute(explicit) || explicit.split(/[/\\]/).includes(".."))) { + return { ok: false, state: "failed", detail: "invalid_path" }; + } + const candidates = explicit ? [explicit] : [ + findOnPath([executable], deps), + path.join(deps.home, ".grok", "bin", executable), + ...(deps.platform === "darwin" ? [`/opt/homebrew/bin/${executable}`, `/usr/local/bin/${executable}`] : []), + ]; + for (const candidate of candidates) { + if (!candidate) continue; + try { + const actual = deps.realpath(candidate); + if (!deps.stat(actual).isFile()) continue; + deps.access(actual, fsConstants.X_OK); + return { ok: true, command: { command: actual, args: ["agent", "stdio"] } }; + } catch { /* Try another installed Grok binary. */ } + } + return { ok: false, state: explicit ? "failed" : "not_installed", ...(explicit ? { detail: "invalid_path" } : {}) }; } function resolveCodex(configured, deps) { @@ -291,19 +383,27 @@ export function acpInitializeParams(version = clientVersion()) { }; } -export function createAcpSpawnPlan(command, cwd) { +export function createAcpSpawnPlan(input, cwd) { + const spec = typeof input === "string" ? { command: input, args: [] } : input; + const command = spec?.command; if (typeof command !== "string" || !command.trim() || command.includes("\0")) throw new Error("invalid_command"); + if (!Array.isArray(spec.args) || spec.args.some((arg) => typeof arg !== "string" || arg.includes("\0"))) throw new Error("invalid_command"); if (typeof cwd !== "string" || !cwd.trim()) throw new Error("invalid_workspace"); const workspace = path.resolve(cwd); if (workspace === path.resolve(homedir())) throw new Error("invalid_workspace"); return { command, - args: [], + args: spec.args, options: { cwd: workspace, stdio: ["pipe", "pipe", "ignore"], windowsHide: true, shell: false, + ...(spec.env || spec.unsetEnv ? { env: (() => { + const env = { ...process.env, ...spec.env }; + for (const key of spec.unsetEnv ?? []) delete env[key]; + return env; + })() } : {}), }, }; } @@ -312,7 +412,7 @@ export function spawnAcpChild(spawnImpl, command, cwd) { const plan = createAcpSpawnPlan(command, cwd); const options = { ...plan.options, shell: false, stdio: ["pipe", "pipe", "ignore"] }; if (options.shell !== false) throw new Error("ACP processes cannot use a shell"); - const child = spawnImpl(plan.command, [], options); + const child = spawnImpl(plan.command, plan.args, options); trackChild(child); return waitForChildProcessSpawn(child); } @@ -403,6 +503,13 @@ const normalizePromptCapabilities = (initialized) => { }; }; +const publicAuthMethods = (initialized) => ( + Array.isArray(initialized?.authMethods) + ? initialized.authMethods.filter((method) => (method?.type == null || method.type === "agent") && typeof method.id === "string" && method.id.length <= 120) + .map((method) => ({ id: method.id, name: typeof method.name === "string" ? method.name.slice(0, 120) : method.id })) + : [] +); + const createEdgeEverAcpClient = (requestId, emit) => ({ requestPermission() { return { outcome: { outcome: "cancelled" } }; @@ -459,11 +566,23 @@ export function createAcpHostRuntime(options = {}) { const version = options.clientVersion ?? clientVersion(); const handshakeTimeoutMs = options.handshakeTimeoutMs ?? HANDSHAKE_TIMEOUT_MS; const commandDeps = options; + const manager = options.adapterManager ?? (options.adapterStore ? createAcpAdapterManager({ root: options.adapterStore, executablePath: options.executablePath }) : null); const active = new Map(); + const updateFailures = new Map(); + const installingIds = new Set(); + const latestStatus = new Map(); - const connect = async (command, requestId, emit, signal) => { + const resolveCommand = (input) => { + if (input?.id === "antigravity" && typeof input.path === "string" && input.path.trim()) return resolveAcpCommand(input, commandDeps); + const managed = manager?.get(input?.id); + if (managed) return { ok: true, command: managed.command, version: managed.version, managed: true }; + return resolveAcpCommand(input, commandDeps); + }; + + const connect = async (command, requestId, emit, signal, authMethodId) => { const cwd = await createAcpWorkspace(mkdtempImpl, options.tmpRoot); let child = null; + let authMethods = []; const abort = () => stopChild(child); signal?.addEventListener("abort", abort, { once: true }); try { @@ -474,6 +593,11 @@ export function createAcpHostRuntime(options = {}) { const connection = new ClientSideConnection(() => createEdgeEverAcpClient(requestId, emit), stream); void connection.closed?.catch(() => {}); const initialized = await connection.initialize(acpInitializeParams(version)); + authMethods = publicAuthMethods(initialized); + if (authMethodId) { + if (!authMethods.some((method) => method.id === authMethodId)) throw new Error("invalid_auth_method"); + await connection.authenticate({ methodId: authMethodId }); + } const session = await connection.newSession({ cwd, mcpServers: [] }); if (signal?.aborted) throw Object.assign(new Error("connection_timeout"), { code: "TIMEOUT" }); return { @@ -485,6 +609,7 @@ export function createAcpHostRuntime(options = {}) { stop: abort, }; } catch (error) { + if (authMethods.length && error && typeof error === "object") error.authMethods = authMethods; abort(); await removeAcpWorkspace(cwd, rmImpl); throw error; @@ -493,9 +618,9 @@ export function createAcpHostRuntime(options = {}) { } }; - const withHandshakeTimeout = async (operation) => { + const withHandshakeTimeout = async (operation, timeoutMs = handshakeTimeoutMs) => { const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), handshakeTimeoutMs); + const timer = setTimeout(() => controller.abort(), timeoutMs); timer.unref?.(); try { return await operation(controller.signal); @@ -511,44 +636,131 @@ export function createAcpHostRuntime(options = {}) { return { listAdapters() { - const codex = resolveAcpCommand({ id: "codex" }, commandDeps); - const codexAdapter = codex.ok - ? { ...adapterShell("codex"), state: "failed", detail: "not_probed" } - : adapterFromResolution("codex", codex); - return [codexAdapter, { ...adapterShell("antigravity"), state: "not_installed" }]; + return ["codex", "antigravity", "grokBuild", "workbuddyCn", "workbuddyIntl"].map((id) => { + if (installingIds.has(id)) return { ...adapterShell(id), state: "installing" }; + const resolved = resolveCommand({ id }); + return resolved.ok + ? { ...adapterShell(id), ...(latestStatus.get(id) ?? { state: "failed", detail: "not_probed" }), ...(resolved.version ? { version: resolved.version, managed: true } : {}), ...(updateFailures.has(id) ? { updateError: updateFailures.get(id) } : {}) } + : adapterFromResolution(id, resolved); + }); + }, + + async installAdapter(id, { updateOnly = false } = {}) { + if (!manager) return { updated: false, reason: "adapter_store_unavailable" }; + installingIds.add(id); + try { + const result = await manager.install(id, async (command) => { + let connected; + try { + connected = await withHandshakeTimeout((signal) => connect(command, `install-${id}`, () => {}, signal), 90_000); + return { ...adapterShell(id), state: "available", promptCapabilities: connected.promptCapabilities }; + } catch (error) { + return { ...adapterShell(id), ...failureFields(classifyAcpFailure(error)), ...(isAuthRequiredError(error) ? { authMethods: error.authMethods ?? [] } : {}) }; + } finally { + if (connected) { + connected.stop(); + await removeAcpWorkspace(connected.cwd, rmImpl); + } + } + }, { updateOnly }); + if (result.adapter) latestStatus.set(id, result.adapter); + return result; + } finally { + installingIds.delete(id); + } + }, + + async installDetected() { + if (!manager) return []; + const detected = options.detectInstalledAgentApps?.() ?? detectInstalledAgentApps(); + const results = []; + for (const id of detected) { + if (manager.get(id) || resolveAcpCommand({ id }, commandDeps).ok) continue; + try { + results.push({ id, ...await this.installAdapter(id) }); + } catch (error) { + results.push({ id, updated: false, reason: sanitizeFailureDetail(error) }); + } + } + return results; + }, + + async updateInstalled() { + if (!manager) return []; + const results = []; + for (const id of manager.installedIds()) { + try { + const result = await this.installAdapter(id, { updateOnly: true }); + updateFailures.delete(id); + results.push({ id, ...result }); + } catch (error) { + const reason = sanitizeFailureDetail(error); + updateFailures.set(id, reason); + results.push({ id, updated: false, reason }); + } + } + return results; + }, + + async pruneAdapters() { + await manager?.prune(); }, async probeAdapter(input) { const id = input?.id; - if (id !== "codex" && id !== "antigravity") throw new Error("unknown_adapter"); - const resolved = resolveAcpCommand(input, commandDeps); + if (!Object.hasOwn(ADAPTERS, id)) throw new Error("unknown_adapter"); + const resolved = resolveCommand(input); if (!resolved.ok) return adapterFromResolution(id, resolved); let connected; try { connected = await withHandshakeTimeout((signal) => connect(resolved.command, `probe-${id}`, () => {}, signal)); } catch (error) { - return { ...adapterShell(id), ...failureFields(classifyAcpFailure(error)) }; + const failure = { ...adapterShell(id), ...failureFields(classifyAcpFailure(error)), ...(isAuthRequiredError(error) ? { authMethods: error.authMethods ?? [] } : {}), ...(resolved.version ? { version: resolved.version, managed: true } : {}) }; + latestStatus.set(id, failure); + return failure; } const adapter = { ...adapterShell(id), state: "available", promptCapabilities: connected.promptCapabilities, + ...(resolved.version ? { version: resolved.version, managed: true } : {}), }; + latestStatus.set(id, adapter); connected.stop(); await removeAcpWorkspace(connected.cwd, rmImpl); return adapter; }, + async authenticateAdapter(input) { + const id = input?.id; + if (!Object.hasOwn(ADAPTERS, id)) throw new Error("unknown_adapter"); + if (typeof input.methodId !== "string" || !input.methodId || input.methodId.length > 120) throw new Error("invalid_auth_method"); + const resolved = resolveCommand(input); + if (!resolved.ok) return adapterFromResolution(id, resolved); + let connected; + try { + connected = await withHandshakeTimeout((signal) => connect(resolved.command, `auth-${id}`, () => {}, signal, input.methodId), 5 * 60_000); + return { ...adapterShell(id), state: "available", promptCapabilities: connected.promptCapabilities, ...(resolved.version ? { version: resolved.version, managed: true } : {}) }; + } catch (error) { + return { ...adapterShell(id), ...failureFields(classifyAcpFailure(error)), ...(isAuthRequiredError(error) ? { authMethods: error.authMethods ?? [] } : {}), ...(resolved.version ? { version: resolved.version, managed: true } : {}) }; + } finally { + if (connected) { + connected.stop(); + await removeAcpWorkspace(connected.cwd, rmImpl); + } + } + }, + async prompt(input, emit = () => {}) { if (!input || typeof input !== "object" || typeof input.prompt !== "string") throw new Error("invalid_prompt"); - if (input.adapterId !== "codex" && input.adapterId !== "antigravity") throw new Error("unknown_adapter"); + if (!Object.hasOwn(ADAPTERS, input.adapterId)) throw new Error("unknown_adapter"); const requestId = randomUUID(); const notify = (event) => emit(event); const fail = (message) => { notify({ requestId, type: "error", message }); return { requestId }; }; - const resolved = resolveAcpCommand({ id: input.adapterId, path: input.path }, commandDeps); + const resolved = resolveCommand({ id: input.adapterId, path: input.path }); if (!resolved.ok) return fail(promptFailureMessage(resolved)); let connected; @@ -616,13 +828,21 @@ const failureFields = (failure) => ( : { state: failure.state } ); -export function registerAcpIpc(ipcMain, runtime = createAcpHostRuntime()) { +export function registerAcpIpc(ipcMain, runtime = createAcpHostRuntime(), { allowInstall = () => false } = {}) { const send = (sender, event) => { if (!sender || sender.isDestroyed?.()) return; sender.send("desktop:acp-event", event); }; ipcMain.handle("desktop:acp-list", () => runtime.listAdapters()); ipcMain.handle("desktop:acp-probe", (_event, input) => runtime.probeAdapter(input)); + ipcMain.handle("desktop:acp-install", (event, id) => { + if (!allowInstall(event.sender)) throw new Error("adapter_install_forbidden"); + return runtime.installAdapter(id); + }); + ipcMain.handle("desktop:acp-authenticate", (event, input) => { + if (!allowInstall(event.sender)) throw new Error("adapter_auth_forbidden"); + return runtime.authenticateAdapter(input); + }); ipcMain.handle("desktop:acp-prompt", (event, input) => runtime.prompt(input, (acpEvent) => send(event.sender, acpEvent))); ipcMain.handle("desktop:acp-cancel", (_event, requestId) => runtime.cancel(requestId)); return runtime; diff --git a/apps/desktop/src/main/acp-host.test.mjs b/apps/desktop/src/main/acp-host.test.mjs index 52edac87..d59cde37 100644 --- a/apps/desktop/src/main/acp-host.test.mjs +++ b/apps/desktop/src/main/acp-host.test.mjs @@ -1,7 +1,7 @@ import { describe, expect, test } from "bun:test"; import { EventEmitter } from "node:events"; import { realpathSync } from "node:fs"; -import { chmod, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { createRequire } from "node:module"; import { homedir, tmpdir } from "node:os"; import path from "node:path"; @@ -13,6 +13,7 @@ import { classifyAcpFailure, createAcpHostRuntime, createAcpSpawnPlan, + detectInstalledAgentApps, eventsFromSessionUpdate, isAuthRequiredError, registerAcpIpc, @@ -59,7 +60,7 @@ const collector = () => { }; }; -const fakeAgentSource = ({ reportPath, secretPath, allowImage, allowEmbedded, hold }) => `#!/usr/bin/env bun +const fakeAgentSource = ({ reportPath, secretPath, allowImage, allowEmbedded, hold, requireAuth }) => `#!/usr/bin/env bun import * as acp from ${JSON.stringify(sdkHref)}; import { writeFileSync } from "node:fs"; @@ -68,7 +69,9 @@ const secretPath = ${JSON.stringify(secretPath)}; const allowImage = ${allowImage ? "true" : "false"}; const allowEmbedded = ${allowEmbedded ? "true" : "false"}; const hold = ${hold ? "true" : "false"}; -const report = { initialize: null, newSession: null, permission: null, readError: null, readResult: null, prompt: null }; +const requireAuth = ${requireAuth ? "true" : "false"}; +let authenticated = false; +const report = { initialize: null, newSession: null, permission: null, readError: null, readResult: null, prompt: null, authMethod: null }; const save = () => writeFileSync(reportPath, JSON.stringify(report)); writeFileSync(reportPath + ".pid", String(process.pid)); let releaseHold = () => {}; @@ -107,9 +110,17 @@ acp.agent({ name: "edgeever-fake-agent" }) return { protocolVersion: ctx.params.protocolVersion, agentCapabilities: { promptCapabilities: { image: allowImage, embeddedContext: allowEmbedded } }, + authMethods: requireAuth ? [{ id: "browser", name: "Browser" }] : [], }; }) + .onRequest("authenticate", (ctx) => { + report.authMethod = ctx.params.methodId; + authenticated = true; + save(); + return {}; + }) .onRequest("session/new", (ctx) => { + if (requireAuth && !authenticated) throw new acp.RequestError(-32000, "auth_required"); report.newSession = { cwd: ctx.params.cwd, mcpServers: ctx.params.mcpServers }; save(); return { sessionId: "sess-1" }; @@ -212,6 +223,39 @@ describe("ACP command allow-list", () => { } }); + test("detects installed agent applications separately from ACP connectors", () => { + const detected = detectInstalledAgentApps({ + platform: "darwin", + home: "/Users/example", + pathEnv: "", + access() { throw new Error("not on PATH"); }, + exists(candidate) { return candidate === "/Applications/Antigravity.app" || candidate === "/Applications/ChatGPT.app/Contents/Resources/codex"; }, + }); + expect(detected).toEqual(["codex", "antigravity"]); + }); + + test("automatically installs only a detected agent missing its ACP connector", async () => { + let installed = false; + let installs = 0; + const runtime = createAcpHostRuntime({ + platform: "darwin", + pathEnv: "", + detectInstalledAgentApps: () => ["codex"], + adapterManager: { + get: () => installed ? { version: "2.0.1", command: { command: "/tmp/codex-acp", args: [] } } : null, + install: async () => { + installs += 1; + installed = true; + return { updated: true, version: "2.0.1", adapter: { id: "codex", label: "Codex", state: "available" } }; + }, + }, + }); + expect((await runtime.installDetected())[0]?.updated).toBe(true); + expect(runtime.listAdapters()[0]?.state).toBe("available"); + expect(await runtime.installDetected()).toEqual([]); + expect(installs).toBe(1); + }); + test("looks up codex-acp.exe only as a win32 basename", () => { const seen = []; resolveAcpCommand({ id: "codex" }, { @@ -229,6 +273,47 @@ describe("ACP command allow-list", () => { expect(seen).toEqual(["C:\\Tools\\codex-acp", "C:\\Tools\\codex-acp.exe"]); }); + test("runs an installed Grok Build CLI through its built-in ACP mode", async () => { + const directory = await mkdtemp(path.join(tmpdir(), "edgeever-grok-acp-")); + try { + const binary = path.join(directory, ".grok", "bin", "grok"); + await mkdir(path.dirname(binary), { recursive: true }); + await writeFile(binary, "#!/bin/sh\nexit 0\n", { mode: 0o755 }); + const resolved = resolveAcpCommand({ id: "grokBuild" }, { platform: "darwin", pathEnv: "", home: directory }); + expect(resolved).toEqual({ ok: true, command: { command: realpathSync(binary), args: ["agent", "stdio"] } }); + const runtime = createAcpHostRuntime({ platform: "darwin", pathEnv: "", home: directory }); + expect(runtime.listAdapters().find((adapter) => adapter.id === "grokBuild")?.state).toBe("failed"); + expect(resolveAcpCommand({ id: "grokBuild", path: "../grok" }, { platform: "darwin", home: directory }).detail).toBe("invalid_path"); + } finally { + await rm(directory, { recursive: true, force: true }); + } + }); + + test("keeps WorkBuddy domestic and international ACP authentication environments separate", async () => { + const directory = await mkdtemp(path.join(tmpdir(), "edgeever-workbuddy-acp-")); + try { + const bundled = path.join(directory, "Applications", "WorkBuddy AI.app", "Contents", "Resources", "app.asar.unpacked", "cli", "bin", "codebuddy"); + await mkdir(path.dirname(bundled), { recursive: true }); + await writeFile(bundled, "// fake bundled CLI"); + const deps = { + platform: "darwin", home: directory, pathEnv: "", executablePath: "/usr/bin/node", + realpathSync(candidate) { + if (!candidate.startsWith(directory)) throw new Error("not installed"); + return realpathSync(candidate); + }, + }; + const domestic = resolveAcpCommand({ id: "workbuddyCn" }, deps); + const international = resolveAcpCommand({ id: "workbuddyIntl" }, deps); + expect(domestic.command.args).toEqual([realpathSync(bundled), "--acp"]); + expect(domestic.command.env.CODEBUDDY_INTERNET_ENVIRONMENT).toBe("internal"); + expect(international.command.args).toEqual([realpathSync(bundled), "--acp"]); + expect(international.command.unsetEnv).toEqual(["CODEBUDDY_INTERNET_ENVIRONMENT"]); + expect(createAcpSpawnPlan(international.command, directory).options.env.CODEBUDDY_INTERNET_ENVIRONMENT).toBeUndefined(); + } finally { + await rm(directory, { recursive: true, force: true }); + } + }); + test("requires an Antigravity absolute file and does not accept a directory", async () => { const directory = await mkdtemp(path.join(tmpdir(), "edgeever-acp-agy-")); try { @@ -424,6 +509,30 @@ describe("ACP stdio session", () => { } }, 15_000); + sessionTest("offers advertised agent login and verifies a session after authentication", async () => { + const directory = await mkdtemp(path.join(tmpdir(), "edgeever-acp-auth-")); + const reportPath = path.join(directory, "report.json"); + try { + const scriptPath = await writeFakeAgent(directory, { + reportPath, + secretPath: path.join(directory, "secret.txt"), + allowImage: false, + allowEmbedded: false, + hold: false, + requireAuth: true, + }); + const runtime = createAcpHostRuntime(); + const probed = await runtime.probeAdapter({ id: "antigravity", path: scriptPath }); + expect(probed.state).toBe("needs_login"); + expect(probed.authMethods).toEqual([{ id: "browser", name: "Browser" }]); + const authenticated = await runtime.authenticateAdapter({ id: "antigravity", path: scriptPath, methodId: "browser" }); + expect(authenticated.state).toBe("available"); + expect((await readReport(reportPath)).authMethod).toBe("browser"); + } finally { + await rm(directory, { recursive: true, force: true }); + } + }, 15_000); + sessionTest("streams updates, cancels permission, and never reads a local file", async () => { const directory = await mkdtemp(path.join(tmpdir(), "edgeever-acp-prompt-")); const reportPath = path.join(directory, "report.json"); @@ -541,7 +650,9 @@ test("desktop preload and main process expose the ACP host", async () => { Bun.file(new URL("../preload/index.cjs", import.meta.url)).text(), Bun.file(new URL("./acp-host.mjs", import.meta.url)).text(), ]); - expect(main).toContain("registerAcpIpc(ipcMain)"); + expect(main).toContain("registerAcpIpc(ipcMain, createAcpHostRuntime({"); + expect(preload).toContain('installAcpAdapter: (id) => ipcRenderer.invoke("desktop:acp-install", id)'); + expect(preload).toContain('authenticateAcpAdapter: (input) => ipcRenderer.invoke("desktop:acp-authenticate", input)'); expect(host).toContain('ipcMain.handle("desktop:acp-prompt"'); expect(preload).toContain('listAcpAdapters: () => ipcRenderer.invoke("desktop:acp-list")'); expect(preload).toContain('probeAcpAdapter: (input) => ipcRenderer.invoke("desktop:acp-probe", input)'); @@ -550,5 +661,5 @@ test("desktop preload and main process expose the ACP host", async () => { expect(preload).toContain('ipcRenderer.on("desktop:acp-event"'); const handlers = new Map(); registerAcpIpc({ handle: (channel, handler) => handlers.set(channel, handler) }); - expect([...handlers.keys()]).toEqual(["desktop:acp-list", "desktop:acp-probe", "desktop:acp-prompt", "desktop:acp-cancel"]); + expect([...handlers.keys()]).toEqual(["desktop:acp-list", "desktop:acp-probe", "desktop:acp-install", "desktop:acp-authenticate", "desktop:acp-prompt", "desktop:acp-cancel"]); }); diff --git a/apps/desktop/src/main/index.mjs b/apps/desktop/src/main/index.mjs index d2b7007e..90a8cb90 100644 --- a/apps/desktop/src/main/index.mjs +++ b/apps/desktop/src/main/index.mjs @@ -48,7 +48,7 @@ import { import electronUpdater from "electron-updater"; import { createPluginPublicNetworkRuntime } from "./plugin-public-network.mjs"; import { createAiDirectRuntime } from "./ai-direct.mjs"; -import { registerAcpIpc } from "./acp-host.mjs"; +import { createAcpHostRuntime, registerAcpIpc } from "./acp-host.mjs"; import { shouldQuitAfterAllWindowsClosed } from "./window-lifecycle.mjs"; import { RENDERER_HIBERNATE_PREPARE_TIMEOUT_MS, @@ -1594,7 +1594,15 @@ const startApplication = async () => { ipcMain.on("desktop:ai-direct-cancel", (event, requestId) => { if (event.sender === mainWindow?.webContents && typeof requestId === "string") aiDirect.cancel(requestId); }); - registerAcpIpc(ipcMain); + const acpRuntime = registerAcpIpc(ipcMain, createAcpHostRuntime({ + adapterStore: join(app.getPath("userData"), "acp-adapters"), + }), { allowInstall: (sender) => sender === mainWindow?.webContents }); + await acpRuntime.pruneAdapters().catch(() => {}); + const refreshAdapters = () => { void acpRuntime.installDetected().catch(() => []).then(() => acpRuntime.updateInstalled()).catch(() => {}); }; + const firstAdapterRefresh = setTimeout(refreshAdapters, 10_000); + firstAdapterRefresh.unref?.(); + const adapterRefreshInterval = setInterval(refreshAdapters, 24 * 60 * 60 * 1000); + adapterRefreshInterval.unref?.(); ipcMain.handle("desktop:sync-scheduled-tasks", async (event, tasks) => { if (event.sender !== mainWindow?.webContents) throw new Error("Scheduled tasks must come from the main window"); if (!Array.isArray(tasks) || tasks.length > 1_000) throw new Error("Invalid scheduled task list"); diff --git a/apps/desktop/src/preload/index.cjs b/apps/desktop/src/preload/index.cjs index d7fb2579..1bdd05c4 100644 --- a/apps/desktop/src/preload/index.cjs +++ b/apps/desktop/src/preload/index.cjs @@ -50,6 +50,8 @@ contextBridge.exposeInMainWorld("edgeeverDesktop", Object.freeze({ }, listAcpAdapters: () => ipcRenderer.invoke("desktop:acp-list"), probeAcpAdapter: (input) => ipcRenderer.invoke("desktop:acp-probe", input), + installAcpAdapter: (id) => ipcRenderer.invoke("desktop:acp-install", id), + authenticateAcpAdapter: (input) => ipcRenderer.invoke("desktop:acp-authenticate", input), promptAcp: (input) => ipcRenderer.invoke("desktop:acp-prompt", input), cancelAcp: (requestId) => ipcRenderer.invoke("desktop:acp-cancel", requestId), onAcpEvent: (callback) => { diff --git a/apps/web/src/components/ai-sidebar/AiSidebar.tsx b/apps/web/src/components/ai-sidebar/AiSidebar.tsx index 7d9bcfce..816e2b6c 100644 --- a/apps/web/src/components/ai-sidebar/AiSidebar.tsx +++ b/apps/web/src/components/ai-sidebar/AiSidebar.tsx @@ -347,9 +347,9 @@ function AiSidebarThreadMenu({ const readLocalAdapter = (): { id: DesktopAcpAdapterId; path?: string } | null => { const id = readStorage(AI_SIDEBAR_ADAPTER_KEY); - if (id !== "codex" && id !== "antigravity") return null; + if (id !== "codex" && id !== "antigravity" && id !== "grokBuild" && id !== "workbuddyCn" && id !== "workbuddyIntl") return null; const path = readStorage(AI_SIDEBAR_ADAPTER_PATH_KEY)?.trim(); - return path ? { id, path } : { id }; + return id === "antigravity" && path ? { id, path } : { id }; }; const attachmentServerCode = (code: string | undefined) => ( diff --git a/apps/web/src/components/settings/DesktopAcpAgentCard.tsx b/apps/web/src/components/settings/DesktopAcpAgentCard.tsx index f43de26f..41babd34 100644 --- a/apps/web/src/components/settings/DesktopAcpAgentCard.tsx +++ b/apps/web/src/components/settings/DesktopAcpAgentCard.tsx @@ -10,6 +10,8 @@ import { AI_SIDEBAR_ADAPTER_PATH_KEY, AI_SIDEBAR_SOURCE_KEY, listDesktopAcpAdapters, + installDesktopAcpAdapter, + authenticateDesktopAcpAdapter, probeDesktopAcpAdapter, type AiSidebarSource, type DesktopAcpAdapter, @@ -25,29 +27,33 @@ import { SETTINGS_ITEM_TITLE_CLASSNAME, } from "./settings-ui"; -const adapterIds: DesktopAcpAdapterId[] = ["codex", "antigravity"]; +const adapterIds: DesktopAcpAdapterId[] = ["codex", "antigravity", "grokBuild", "workbuddyCn", "workbuddyIntl"]; const agentCatalog = [ { id: "codex", connectable: true }, { id: "antigravity", connectable: true }, { id: "claudeCode", connectable: false }, - { id: "workbuddy", connectable: false }, - { id: "grokBuild", connectable: false }, + { id: "workbuddyCn", connectable: true }, + { id: "workbuddyIntl", connectable: true }, + { id: "grokBuild", connectable: true }, ] as const; const readSource = (): AiSidebarSource => ( localStorage.getItem(AI_SIDEBAR_SOURCE_KEY) === "local" ? "local" : "builtin" ); -const readAdapterId = (): DesktopAcpAdapterId => ( - localStorage.getItem(AI_SIDEBAR_ADAPTER_KEY) === "antigravity" ? "antigravity" : "codex" -); +const readAdapterId = (): DesktopAcpAdapterId => { + const stored = localStorage.getItem(AI_SIDEBAR_ADAPTER_KEY); + return adapterIds.find((id) => id === stored) ?? "codex"; +}; const statusKey = (adapter: DesktopAcpAdapter | undefined, probing: boolean) => { if (probing) return "aiAssistant.agentSource.probing"; if (!adapter || adapter.detail === "not_probed") return "aiAssistant.agentSource.notProbed"; if (adapter.detail === "invalid_path") return "aiAssistant.agentSource.invalidPath"; if (adapter.detail === "desktop_unavailable") return "aiAssistant.agentSource.localDisabled"; + if (adapter.id === "grokBuild" && adapter.state === "not_installed") return "aiAssistant.agentSource.grokBuildNotFound"; + if ((adapter.id === "workbuddyCn" || adapter.id === "workbuddyIntl") && adapter.state === "not_installed") return "aiAssistant.agentSource.workbuddyNotFound"; return `aiAssistant.agentSource.states.${adapter.state}`; }; @@ -64,6 +70,9 @@ const DesktopAcpAgentCardBody = ({ bridge }: { bridge: boolean }) => { const [listed, setListed] = useState([]); const [probed, setProbed] = useState(null); const [probing, setProbing] = useState(false); + const [installing, setInstalling] = useState(false); + const [installError, setInstallError] = useState(false); + const [authenticating, setAuthenticating] = useState(false); useEffect(() => { const stored = readSource(); @@ -82,24 +91,72 @@ const DesktopAcpAgentCardBody = ({ bridge }: { bridge: boolean }) => { useEffect(() => { let cancelled = false; - void listDesktopAcpAdapters().then((adapters) => { + const refresh = () => void listDesktopAcpAdapters().then((adapters) => { if (!cancelled) setListed(adapters); }).catch(() => {}); + refresh(); + const interval = window.setInterval(refresh, 5_000); return () => { cancelled = true; + window.clearInterval(interval); }; }, []); - const shown = probed?.id === adapterId ? probed : listed.find((adapter) => adapter.id === adapterId); + const listedCurrent = listed.find((adapter) => adapter.id === adapterId); + const shown = listedCurrent?.state === "installing" || (listedCurrent?.managed && (!probed?.managed || listedCurrent.version !== probed.version)) + ? listedCurrent + : probed?.id === adapterId ? probed : listedCurrent; const status = t(statusKey(shown, probing)); + const authMethods = shown?.authMethods?.filter((method) => ( + adapterId === "workbuddyCn" ? method.id !== "external" : adapterId === "workbuddyIntl" ? method.id !== "internal" : true + )) ?? []; const selectAdapter = (id: DesktopAcpAdapterId) => { setAdapterId(id); setProbed(null); + setInstallError(false); + }; + + const install = async () => { + if (adapterId !== "codex" && adapterId !== "antigravity") return; + setInstalling(true); + setInstallError(false); + try { + const result = await installDesktopAcpAdapter(adapterId); + if (result.updated) { + if (adapterId === "antigravity") setAdapterPath(""); + setListed(await listDesktopAcpAdapters()); + if (result.adapter) setProbed({ ...result.adapter, version: result.version, managed: true }); + } else { + setInstallError(true); + } + } catch { + setInstallError(true); + } finally { + setInstalling(false); + } + }; + + const authenticate = async (methodId: string) => { + setAuthenticating(true); + setInstallError(false); + try { + const result = await authenticateDesktopAcpAdapter({ + id: adapterId, + methodId, + ...(adapterId === "antigravity" && adapterPath.trim() ? { path: adapterPath.trim() } : {}), + }); + setProbed(result); + } catch { + setProbed({ id: adapterId, label: t(`aiAssistant.agentSource.${adapterId}`), state: "failed" }); + } finally { + setAuthenticating(false); + } }; const probe = async () => { setProbing(true); + setInstallError(false); try { const result = await probeDesktopAcpAdapter({ id: adapterId, @@ -107,7 +164,7 @@ const DesktopAcpAgentCardBody = ({ bridge }: { bridge: boolean }) => { }); setProbed(result); } catch { - setProbed({ id: adapterId, label: adapterId === "codex" ? "Codex" : "Antigravity", state: "failed" }); + setProbed({ id: adapterId, label: t(`aiAssistant.agentSource.${adapterId}`), state: "failed" }); } finally { setProbing(false); } @@ -228,11 +285,45 @@ const DesktopAcpAgentCardBody = ({ bridge }: { bridge: boolean }) => { ) : null}
-

{status}

- +

+ {installing ? t("aiAssistant.agentSource.installing") : installError ? t("aiAssistant.agentSource.installFailed") : status} + {shown?.managed && shown.version ? ` · v${shown.version} · ${t("aiAssistant.agentSource.autoUpdated")}` : ""} +

+
+ {shown?.state === "not_installed" && (adapterId === "codex" || adapterId === "antigravity") ? ( + + ) : null} + + + + + + {t("aiAssistant.agentSource.probeHint")} + + +
+ {shown?.state === "not_installed" ?

{t(adapterId === "grokBuild" ? "aiAssistant.agentSource.grokBuildMissingHint" : adapterId === "workbuddyCn" || adapterId === "workbuddyIntl" ? "aiAssistant.agentSource.workbuddyMissingHint" : "aiAssistant.agentSource.installHint")}

: null} + {shown?.state === "needs_login" && (adapterId === "workbuddyCn" || adapterId === "workbuddyIntl") ? ( +

{t(adapterId === "workbuddyCn" ? "aiAssistant.agentSource.workbuddyCnLoginHint" : "aiAssistant.agentSource.workbuddyIntlLoginHint")}

+ ) : null} + {shown?.state === "needs_login" && authMethods.length ? ( +
+ {authMethods.map((method) => ( + + ))} +
+ ) : null} + {shown?.state === "needs_login" && !authMethods.length ? ( +

{t("aiAssistant.agentSource.loginUnavailable")}

+ ) : null} + {shown?.updateError ?

{t("aiAssistant.agentSource.updateFailed")}

: null} ) : null} diff --git a/apps/web/src/lib/desktop-acp.ts b/apps/web/src/lib/desktop-acp.ts index 4dd7ea99..077540cc 100644 --- a/apps/web/src/lib/desktop-acp.ts +++ b/apps/web/src/lib/desktop-acp.ts @@ -1,5 +1,5 @@ -export type DesktopAcpAdapterId = "codex" | "antigravity"; -export type DesktopAcpAdapterState = "not_installed" | "needs_login" | "available" | "failed"; +export type DesktopAcpAdapterId = "codex" | "antigravity" | "grokBuild" | "workbuddyCn" | "workbuddyIntl"; +export type DesktopAcpAdapterState = "not_installed" | "installing" | "needs_login" | "available" | "failed"; export type DesktopAcpPromptCapabilities = { image?: boolean; @@ -12,6 +12,10 @@ export type DesktopAcpAdapter = { state: DesktopAcpAdapterState; detail?: string; promptCapabilities?: DesktopAcpPromptCapabilities; + version?: string; + managed?: boolean; + updateError?: string; + authMethods?: Array<{ id: string; name: string }>; }; export type DesktopAcpAttachment = { @@ -67,6 +71,18 @@ export const probeDesktopAcpAdapter = async (input: { id: DesktopAcpAdapterId; p return desktop.probeAcpAdapter(input); }; +export const installDesktopAcpAdapter = async (id: Extract) => { + const desktop = bridge(); + if (!desktop?.installAcpAdapter) throw new Error("desktop_acp_unavailable"); + return desktop.installAcpAdapter(id); +}; + +export const authenticateDesktopAcpAdapter = async (input: { id: DesktopAcpAdapterId; path?: string; methodId: string }): Promise => { + const desktop = bridge(); + if (!desktop?.authenticateAcpAdapter) throw new Error("desktop_acp_unavailable"); + return desktop.authenticateAcpAdapter(input); +}; + export const promptDesktopAcp = async (input: DesktopAcpPromptInput): Promise => { const desktop = bridge(); if (!desktop?.promptAcp) throw new Error("desktop_acp_unavailable"); diff --git a/apps/web/src/vite-env.d.ts b/apps/web/src/vite-env.d.ts index 314b4298..bd1b62f0 100644 --- a/apps/web/src/vite-env.d.ts +++ b/apps/web/src/vite-env.d.ts @@ -92,21 +92,43 @@ interface EdgeEverDesktopBridge { finishWeChatImport?(importId: string, success: boolean): Promise; retryWeChatImport?(importId: string): Promise; listAcpAdapters?(): Promise; }>>; - probeAcpAdapter?(input: { id: "codex" | "antigravity"; path?: string }): Promise<{ + probeAcpAdapter?(input: { id: "codex" | "antigravity" | "grokBuild" | "workbuddyCn" | "workbuddyIntl"; path?: string }): Promise<{ + id: "codex" | "antigravity" | "grokBuild" | "workbuddyCn" | "workbuddyIntl"; + label: string; + state: "not_installed" | "needs_login" | "available" | "failed"; + detail?: string; + promptCapabilities?: { image?: boolean; embeddedContext?: boolean }; + version?: string; + managed?: boolean; + authMethods?: Array<{ id: string; name: string }>; + }>; + installAcpAdapter?(id: "codex" | "antigravity"): Promise<{ updated: boolean; version?: string; adapter?: { id: "codex" | "antigravity"; label: string; state: "not_installed" | "needs_login" | "available" | "failed"; detail?: string; promptCapabilities?: { image?: boolean; embeddedContext?: boolean }; + authMethods?: Array<{ id: string; name: string }>; + } }>; + authenticateAcpAdapter?(input: { id: "codex" | "antigravity" | "grokBuild" | "workbuddyCn" | "workbuddyIntl"; path?: string; methodId: string }): Promise<{ + id: "codex" | "antigravity" | "grokBuild" | "workbuddyCn" | "workbuddyIntl"; + label: string; + state: "not_installed" | "needs_login" | "available" | "failed"; + detail?: string; + authMethods?: Array<{ id: string; name: string }>; }>; promptAcp?(input: { - adapterId: "codex" | "antigravity"; + adapterId: "codex" | "antigravity" | "grokBuild" | "workbuddyCn" | "workbuddyIntl"; path?: string; prompt: string; contextText?: string; diff --git a/bun.lock b/bun.lock index a5a199ba..066b8f43 100644 --- a/bun.lock +++ b/bun.lock @@ -102,12 +102,14 @@ }, "apps/desktop": { "name": "@edgeever/desktop", - "version": "1.90.0", + "version": "1.92.1", "dependencies": { "@agentclientprotocol/sdk": "^1.5.1", "@edgeever/public-network": "workspace:*", "croner": "^10.0.1", "electron-updater": "^6.6.2", + "fflate": "0.8.3", + "npm": "11.6.2", "zod": "^4.6.5", }, "devDependencies": { @@ -3324,6 +3326,8 @@ "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + "npm": ["npm@11.6.2", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^9.1.6", "@npmcli/config": "^10.4.2", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.1", "@npmcli/promise-spawn": "^8.0.3", "@npmcli/redact": "^3.2.2", "@npmcli/run-script": "^10.0.0", "@sigstore/tuf": "^4.0.0", "abbrev": "^3.0.1", "archy": "~1.0.0", "cacache": "^20.0.1", "chalk": "^5.6.2", "ci-info": "^4.3.1", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^11.0.3", "graceful-fs": "^4.2.11", "hosted-git-info": "^9.0.2", "ini": "^5.0.0", "init-package-json": "^8.2.2", "is-cidr": "^6.0.1", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^10.0.3", "libnpmdiff": "^8.0.9", "libnpmexec": "^10.1.8", "libnpmfund": "^7.0.9", "libnpmorg": "^8.0.1", "libnpmpack": "^9.0.9", "libnpmpublish": "^11.1.2", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.2", "make-fetch-happen": "^15.0.2", "minimatch": "^10.0.3", "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^11.4.2", "nopt": "^8.1.0", "npm-audit-report": "^6.0.0", "npm-install-checks": "^7.1.2", "npm-package-arg": "^13.0.1", "npm-pick-manifest": "^11.0.1", "npm-profile": "^12.0.0", "npm-registry-fetch": "^19.0.0", "npm-user-validate": "^3.0.0", "p-map": "^7.0.3", "pacote": "^21.0.3", "parse-conflict-json": "^4.0.0", "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", "read": "^4.1.0", "semver": "^7.7.3", "spdx-expression-parse": "^4.0.0", "ssri": "^12.0.0", "supports-color": "^10.2.2", "tar": "^7.5.1", "text-table": "~0.2.0", "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^6.0.2", "which": "^5.0.0" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" } }, "sha512-7iKzNfy8lWYs3zq4oFPa8EXZz5xt9gQNKJZau3B1ErLBb6bF7sBJ00x09485DOvRT2l5Gerbl3VlZNT57MxJVA=="], + "npm-package-arg": ["npm-package-arg@11.0.3", "", { "dependencies": { "hosted-git-info": "^7.0.0", "proc-log": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" } }, "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw=="], "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], @@ -4772,6 +4776,348 @@ "node-gyp/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + "npm/@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], + + "npm/@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], + + "npm/@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "npm/@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", { "bundled": true }, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="], + + "npm/@npmcli/agent": ["@npmcli/agent@4.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg=="], + + "npm/@npmcli/arborist": ["@npmcli/arborist@9.9.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bundled": true, "bin": { "arborist": "bin/index.js" } }, "sha512-N8FWFgZPRtkBYUhrEXagY9ADlGylgQgWHw1ywlHRXE8IobqFH+RAK2KL2at5YM33XFFpIDeoDh6nJ+3ZqB/sQQ=="], + + "npm/@npmcli/config": ["@npmcli/config@10.13.0", "", { "dependencies": { "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-FS1eopTDqQ7EgmkuyC7QrarKr7ZXx1aVrQerER+AT27fsqt4ZvyaH/LeVYUOa2V6UUtokQsPOTfsb+LoqYyphw=="], + + "npm/@npmcli/fs": ["@npmcli/fs@4.0.0", "", { "dependencies": { "semver": "^7.3.5" }, "bundled": true }, "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q=="], + + "npm/@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="], + + "npm/@npmcli/installed-package-contents": ["@npmcli/installed-package-contents@4.0.0", "", { "dependencies": { "npm-bundled": "^5.0.0", "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" } }, "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA=="], + + "npm/@npmcli/map-workspaces": ["@npmcli/map-workspaces@5.0.3", "", { "dependencies": { "@npmcli/name-from-folder": "^4.0.0", "@npmcli/package-json": "^7.0.0", "glob": "^13.0.0", "minimatch": "^10.0.3" }, "bundled": true }, "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw=="], + + "npm/@npmcli/metavuln-calculator": ["@npmcli/metavuln-calculator@9.0.3", "", { "dependencies": { "cacache": "^20.0.0", "json-parse-even-better-errors": "^5.0.0", "pacote": "^21.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5" } }, "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg=="], + + "npm/@npmcli/name-from-folder": ["@npmcli/name-from-folder@4.0.0", "", {}, "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg=="], + + "npm/@npmcli/node-gyp": ["@npmcli/node-gyp@5.0.0", "", {}, "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ=="], + + "npm/@npmcli/package-json": ["@npmcli/package-json@7.0.5", "", { "dependencies": { "@npmcli/git": "^7.0.0", "glob": "^13.0.0", "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", "spdx-expression-parse": "^4.0.0" }, "bundled": true }, "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ=="], + + "npm/@npmcli/promise-spawn": ["@npmcli/promise-spawn@8.0.3", "", { "dependencies": { "which": "^5.0.0" }, "bundled": true }, "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg=="], + + "npm/@npmcli/query": ["@npmcli/query@5.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" } }, "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ=="], + + "npm/@npmcli/redact": ["@npmcli/redact@3.2.2", "", { "bundled": true }, "sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg=="], + + "npm/@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" }, "bundled": true }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="], + + "npm/@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "npm/@sigstore/bundle": ["@sigstore/bundle@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A=="], + + "npm/@sigstore/core": ["@sigstore/core@3.2.1", "", {}, "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g=="], + + "npm/@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.2", "", {}, "sha512-SQqvFMt4V78fdjcDdYX6HbiVSOR4QK3ZgwCa2KOsopAgPIHy1rU5UDUmzLl02r5oyyaYcYHR1hpwDRk/yUe+Mw=="], + + "npm/@sigstore/sign": ["@sigstore/sign@4.1.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.2", "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.4", "proc-log": "^6.1.0" } }, "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ=="], + + "npm/@sigstore/tuf": ["@sigstore/tuf@4.0.2", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" }, "bundled": true }, "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ=="], + + "npm/@sigstore/verify": ["@sigstore/verify@3.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA=="], + + "npm/@tufjs/canonical-json": ["@tufjs/canonical-json@2.0.0", "", {}, "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA=="], + + "npm/@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="], + + "npm/abbrev": ["abbrev@3.0.1", "", { "bundled": true }, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="], + + "npm/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "npm/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "npm/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "npm/aproba": ["aproba@2.1.0", "", {}, "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew=="], + + "npm/archy": ["archy@1.0.0", "", { "bundled": true }, "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw=="], + + "npm/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "npm/bin-links": ["bin-links@6.0.2", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-frE1t78WOwJ45PKV2cF2tNPjTcs9L1J9s6VkrV59wanRP4GlaomuxYPVma7BwthMg8WnfSory4w5PTE6FZZ81w=="], + + "npm/binary-extensions": ["binary-extensions@3.2.0", "", {}, "sha512-DjR9rgAOMQn9TrUHzijJUtvoKviDRvFxuIyqZrubxHqRpQN9OzfXrrLivm7xb3q8oJSKsiuBor4bAS2wafLnVQ=="], + + "npm/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + + "npm/cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" }, "bundled": true }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], + + "npm/chalk": ["chalk@5.6.2", "", { "bundled": true }, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "npm/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "npm/ci-info": ["ci-info@4.4.0", "", { "bundled": true }, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], + + "npm/cidr-regex": ["cidr-regex@5.0.5", "", {}, "sha512-59tdLZcC+BJXa4C5rOmVSuJTy/UneqfJJtCraqwdx5BDHTkGrBtKCUl3u2uiCFvXu+wk0kVuX8axX7yHCZOI9w=="], + + "npm/cli-columns": ["cli-columns@4.0.0", "", { "dependencies": { "string-width": "^4.2.3", "strip-ansi": "^6.0.1" }, "bundled": true }, "sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ=="], + + "npm/cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="], + + "npm/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "npm/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "npm/common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="], + + "npm/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "npm/cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "npm/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "npm/diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], + + "npm/eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "npm/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "npm/encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + + "npm/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "npm/err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], + + "npm/exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], + + "npm/fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", { "bundled": true }, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="], + + "npm/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "npm/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "npm/fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], + + "npm/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bundled": true, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="], + + "npm/graceful-fs": ["graceful-fs@4.2.11", "", { "bundled": true }, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "npm/hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" }, "bundled": true }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="], + + "npm/http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + + "npm/http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "npm/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "npm/iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], + + "npm/ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="], + + "npm/imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "npm/ini": ["ini@5.0.0", "", { "bundled": true }, "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw=="], + + "npm/init-package-json": ["init-package-json@8.2.5", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "npm-package-arg": "^13.0.0", "promzard": "^3.0.1", "read": "^5.0.1", "semver": "^7.7.2", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-IknQ+upLuJU6t3p0uo9wS3GjFD/1GtxIwcIGYOWR8zL2HxQeJwvxYTgZr9brJ8pyZ4kvpkebM8ZKcyqOeLOHSg=="], + + "npm/ip-address": ["ip-address@10.7.2", "", {}, "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w=="], + + "npm/is-cidr": ["is-cidr@6.0.4", "", { "dependencies": { "cidr-regex": "^5.0.4" }, "bundled": true }, "sha512-tOIBU3QiXy0W4LvHbcKWAWSuQfGwDiEILphFCAZtDqj7C57uv3ClO6K8aNEGV4VTA7bWJlpQ0suKQkUe6Rd6ag=="], + + "npm/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "npm/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + + "npm/jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], + + "npm/json-parse-even-better-errors": ["json-parse-even-better-errors@4.0.0", "", { "bundled": true }, "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA=="], + + "npm/json-stringify-nice": ["json-stringify-nice@1.1.4", "", {}, "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw=="], + + "npm/jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + + "npm/just-diff": ["just-diff@6.0.2", "", {}, "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA=="], + + "npm/just-diff-apply": ["just-diff-apply@5.5.0", "", {}, "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw=="], + + "npm/libnpmaccess": ["libnpmaccess@10.0.3", "", { "dependencies": { "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-JPHTfWJxIK+NVPdNMNGnkz4XGX56iijPbe0qFWbdt68HL+kIvSzh+euBL8npLZvl2fpaxo+1eZSdoG15f5YdIQ=="], + + "npm/libnpmdiff": ["libnpmdiff@8.1.13", "", { "dependencies": { "@npmcli/arborist": "^9.9.2", "@npmcli/installed-package-contents": "^4.0.0", "binary-extensions": "^3.0.0", "diff": "^8.0.2", "minimatch": "^10.0.3", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "tar": "^7.5.1" }, "bundled": true }, "sha512-CllgpfURhglenD7OEhMx1oMk8Z0+IEkZ9+IQlxhbO6TA9I7rfQrLZf7M7EXRYI0mRQxQnn2NcOzYQRMOK77XgA=="], + + "npm/libnpmexec": ["libnpmexec@10.3.3", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/arborist": "^9.9.2", "@npmcli/package-json": "^7.0.0", "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "proc-log": "^6.0.0", "read": "^5.0.1", "semver": "^7.3.7", "signal-exit": "^4.1.0", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-IqoZup+ubFEwv7mqGd4ztYQilq6NSOi/r+y0O9Kmio8+/DX5b1irE4VnyO5Qf4TjVygnAFL8I91/rVvEfr6K0w=="], + + "npm/libnpmfund": ["libnpmfund@7.0.27", "", { "dependencies": { "@npmcli/arborist": "^9.9.2" }, "bundled": true }, "sha512-2eU661BS4TrnrfmLYQBTTKwkFuV8dLibxRkBhCUNRsJFY0wjcdTG4SR1X22H0iXTkiI++9wb3M+/r4gmwrO4HA=="], + + "npm/libnpmorg": ["libnpmorg@8.0.1", "", { "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-/QeyXXg4hqMw0ESM7pERjIT2wbR29qtFOWIOug/xO4fRjS3jJJhoAPQNsnHtdwnCqgBdFpGQ45aIdFFZx2YhTA=="], + + "npm/libnpmpack": ["libnpmpack@9.1.14", "", { "dependencies": { "@npmcli/arborist": "^9.9.2", "@npmcli/run-script": "^10.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2" }, "bundled": true }, "sha512-zSUaxpUc91dMyDgWmPlCqeW14FQdRvk3YHEwGAbfOTKaczD+gOr0cweXQ3bOkfLKXdnaOhvkqFZp0MvIfpkElQ=="], + + "npm/libnpmpublish": ["libnpmpublish@11.2.1", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7", "sigstore": "^4.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-eZTRbsvZnG7tV0b9Hxug9feQt9Z29GyijcWpEKpLrgaJjxuVCkUNPW+w6OcXmlvSaopkyrGN65hgtk+Z0YP3Hg=="], + + "npm/libnpmsearch": ["libnpmsearch@9.0.1", "", { "dependencies": { "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-oKw58X415ERY/BOGV3jQPVMcep8YeMRWMzuuqB0BAIM5VxicOU1tQt19ExCu4SV77SiTOEoziHxGEgJGw3FBYQ=="], + + "npm/libnpmteam": ["libnpmteam@8.0.2", "", { "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-ypLrDUQoi8EhG+gzx5ENMcYq23YjPV17Mfvx4nOnQiHOi8vp47+4GvZBrMsEM4yeHPwxguF/HZoXH4rJfHdH/w=="], + + "npm/libnpmversion": ["libnpmversion@8.0.4", "", { "dependencies": { "@npmcli/git": "^7.0.0", "@npmcli/run-script": "^10.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7" }, "bundled": true }, "sha512-5NiNpLxXkNeLHCYVTLxX/qRgdAoRAjiR0arFdVpQ5kZOJ2b0pHdXS9G3qC7uiqVsLa/gfQQIbQIEPdmSMKXF2A=="], + + "npm/lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], + + "npm/make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], + + "npm/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" }, "bundled": true }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "npm/minipass": ["minipass@7.1.3", "", { "bundled": true }, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "npm/minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="], + + "npm/minipass-fetch": ["minipass-fetch@5.0.2", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "optionalDependencies": { "iconv-lite": "^0.7.2" } }, "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ=="], + + "npm/minipass-flush": ["minipass-flush@1.0.7", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA=="], + + "npm/minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" }, "bundled": true }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], + + "npm/minipass-sized": ["minipass-sized@2.0.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA=="], + + "npm/minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + + "npm/ms": ["ms@2.1.3", "", { "bundled": true }, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "npm/mute-stream": ["mute-stream@2.0.0", "", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="], + + "npm/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "npm/node-gyp": ["node-gyp@11.5.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bundled": true, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="], + + "npm/nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bundled": true, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], + + "npm/npm-audit-report": ["npm-audit-report@6.0.0", "", { "bundled": true }, "sha512-Ag6Y1irw/+CdSLqEEAn69T8JBgBThj5mw0vuFIKeP7hATYuQuS5jkMjK6xmVB8pr7U4g5Audbun0lHhBDMIBRA=="], + + "npm/npm-bundled": ["npm-bundled@5.0.0", "", { "dependencies": { "npm-normalize-package-bin": "^5.0.0" } }, "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw=="], + + "npm/npm-install-checks": ["npm-install-checks@7.1.2", "", { "dependencies": { "semver": "^7.1.1" }, "bundled": true }, "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ=="], + + "npm/npm-normalize-package-bin": ["npm-normalize-package-bin@5.0.0", "", {}, "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="], + + "npm/npm-package-arg": ["npm-package-arg@13.0.2", "", { "dependencies": { "hosted-git-info": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA=="], + + "npm/npm-packlist": ["npm-packlist@10.0.4", "", { "dependencies": { "ignore-walk": "^8.0.0", "proc-log": "^6.0.0" } }, "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng=="], + + "npm/npm-pick-manifest": ["npm-pick-manifest@11.0.3", "", { "dependencies": { "npm-install-checks": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "bundled": true }, "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ=="], + + "npm/npm-profile": ["npm-profile@12.0.2", "", { "dependencies": { "npm-registry-fetch": "^19.0.0", "proc-log": "^6.1.0" }, "bundled": true }, "sha512-+OKkPvqvx83vRG8aIetzABI99e5uzZZ6HIS5zCr+oRPSuDL0Buk1KJh/LqyR0BuiWmLeDWGI77igNYipztLm1w=="], + + "npm/npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" }, "bundled": true }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="], + + "npm/npm-user-validate": ["npm-user-validate@3.0.0", "", { "bundled": true }, "sha512-9xi0RdSmJ4mPYTC393VJPz1Sp8LyCx9cUnm/L9Qcb3cFO8gjT4mN20P9FAsea8qDHdQ7LtcN8VLh2UT47SdKCw=="], + + "npm/p-map": ["p-map@7.0.8", "", { "bundled": true }, "sha512-MitaVsCuCFIvOLLPIU7NnfrZvS9H9h7kwMUkDo+T2pEISaJD48IV9S8iIdXB7PsvvdxyYcsSTTrr90XKsbulNw=="], + + "npm/package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "npm/pacote": ["pacote@21.5.1", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bundled": true, "bin": { "pacote": "bin/index.js" } }, "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg=="], + + "npm/parse-conflict-json": ["parse-conflict-json@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "bundled": true }, "sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ=="], + + "npm/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "npm/path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + + "npm/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "npm/postcss-selector-parser": ["postcss-selector-parser@7.1.6", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw=="], + + "npm/proc-log": ["proc-log@5.0.0", "", { "bundled": true }, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], + + "npm/proggy": ["proggy@4.0.0", "", {}, "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ=="], + + "npm/promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], + + "npm/promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="], + + "npm/promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], + + "npm/promzard": ["promzard@3.0.1", "", { "dependencies": { "read": "^5.0.0" } }, "sha512-M5mHhWh+Adz0BIxgSrqcc6GTCSconR7zWQV9vnOSptNtr6cSFlApLc28GbQhuN6oOWBQeV2C0bNE47JCY/zu3Q=="], + + "npm/qrcode-terminal": ["qrcode-terminal@0.12.0", "", { "bundled": true, "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="], + + "npm/read": ["read@4.1.0", "", { "dependencies": { "mute-stream": "^2.0.0" }, "bundled": true }, "sha512-uRfX6K+f+R8OOrYScaM3ixPY4erg69f8DN6pgTvMcA9iRc8iDhwrA4m3Yu8YYKsXJgVvum+m8PkRboZwwuLzYA=="], + + "npm/read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="], + + "npm/retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "npm/safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "npm/semver": ["semver@7.8.5", "", { "bundled": true, "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "npm/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "npm/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "npm/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "npm/sigstore": ["sigstore@4.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.1", "@sigstore/tuf": "^4.0.2", "@sigstore/verify": "^3.1.1" } }, "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w=="], + + "npm/smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + + "npm/socks": ["socks@2.8.10", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ=="], + + "npm/socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], + + "npm/spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "npm/spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" }, "bundled": true }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="], + + "npm/spdx-license-ids": ["spdx-license-ids@3.0.24", "", {}, "sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ=="], + + "npm/ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="], + + "npm/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "npm/string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "npm/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "npm/strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "npm/supports-color": ["supports-color@10.2.2", "", { "bundled": true }, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + + "npm/tar": ["tar@7.5.19", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "bundled": true }, "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw=="], + + "npm/text-table": ["text-table@0.2.0", "", { "bundled": true }, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], + + "npm/tiny-relative-date": ["tiny-relative-date@2.0.2", "", { "bundled": true }, "sha512-rGxAbeL9z3J4pI2GtBEoFaavHdO4RKAU54hEuOef5kfx5aPqiQtbhYktMOTL5OA33db8BjsDcLXuNp+/v19PHw=="], + + "npm/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "npm/treeverse": ["treeverse@3.0.0", "", { "bundled": true }, "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ=="], + + "npm/tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], + + "npm/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="], + + "npm/unique-filename": ["unique-filename@4.0.0", "", { "dependencies": { "unique-slug": "^5.0.0" } }, "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ=="], + + "npm/unique-slug": ["unique-slug@5.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg=="], + + "npm/util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "npm/validate-npm-package-name": ["validate-npm-package-name@6.0.2", "", { "bundled": true }, "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ=="], + + "npm/walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], + + "npm/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bundled": true, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + + "npm/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "npm/wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "npm/write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], + + "npm/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + "npm-package-arg/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], "ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], @@ -5392,6 +5738,122 @@ "npm-package-arg/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "npm/@npmcli/arborist/@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="], + + "npm/@npmcli/arborist/@npmcli/redact": ["@npmcli/redact@4.0.0", "", {}, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], + + "npm/@npmcli/arborist/nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], + + "npm/@npmcli/arborist/npm-install-checks": ["npm-install-checks@8.0.0", "", { "dependencies": { "semver": "^7.1.1" } }, "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA=="], + + "npm/@npmcli/arborist/parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], + + "npm/@npmcli/arborist/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/@npmcli/arborist/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + + "npm/@npmcli/config/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], + + "npm/@npmcli/config/nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], + + "npm/@npmcli/config/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/@npmcli/git/@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" } }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="], + + "npm/@npmcli/git/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], + + "npm/@npmcli/git/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/@npmcli/git/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + + "npm/@npmcli/map-workspaces/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + + "npm/@npmcli/metavuln-calculator/json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], + + "npm/@npmcli/metavuln-calculator/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/@npmcli/package-json/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + + "npm/@npmcli/package-json/json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], + + "npm/@npmcli/package-json/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/@npmcli/run-script/@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" } }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="], + + "npm/@npmcli/run-script/node-gyp": ["node-gyp@12.4.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "undici": "^6.25.0", "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw=="], + + "npm/@npmcli/run-script/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/@sigstore/sign/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/bin-links/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/cacache/@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="], + + "npm/cacache/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + + "npm/cacache/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + + "npm/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "npm/encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "npm/init-package-json/read": ["read@5.0.1", "", { "dependencies": { "mute-stream": "^3.0.0" } }, "sha512-+nsqpqYkkpet2UVPG8ZiuE8d113DK4vHYEoEhcrXBAlPiq6di7QRTuNiKQAbaRYegobuX2BpZ6QjanKOXnJdTA=="], + + "npm/init-package-json/validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", {}, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], + + "npm/libnpmexec/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/libnpmexec/read": ["read@5.0.1", "", { "dependencies": { "mute-stream": "^3.0.0" } }, "sha512-+nsqpqYkkpet2UVPG8ZiuE8d113DK4vHYEoEhcrXBAlPiq6di7QRTuNiKQAbaRYegobuX2BpZ6QjanKOXnJdTA=="], + + "npm/libnpmpublish/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/libnpmpublish/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + + "npm/libnpmversion/json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], + + "npm/libnpmversion/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/make-fetch-happen/@npmcli/redact": ["@npmcli/redact@4.0.0", "", {}, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], + + "npm/make-fetch-happen/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/make-fetch-happen/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + + "npm/minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "npm/minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "npm/node-gyp/make-fetch-happen": ["make-fetch-happen@14.0.3", "", { "dependencies": { "@npmcli/agent": "^3.0.0", "cacache": "^19.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", "ssri": "^12.0.0" } }, "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ=="], + + "npm/npm-package-arg/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/npm-package-arg/validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", {}, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], + + "npm/npm-packlist/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/npm-pick-manifest/npm-install-checks": ["npm-install-checks@8.0.0", "", { "dependencies": { "semver": "^7.1.1" } }, "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA=="], + + "npm/npm-profile/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/npm-registry-fetch/@npmcli/redact": ["@npmcli/redact@4.0.0", "", {}, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="], + + "npm/npm-registry-fetch/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/pacote/@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" } }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="], + + "npm/pacote/proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + + "npm/pacote/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + + "npm/promzard/read": ["read@5.0.1", "", { "dependencies": { "mute-stream": "^3.0.0" } }, "sha512-+nsqpqYkkpet2UVPG8ZiuE8d113DK4vHYEoEhcrXBAlPiq6di7QRTuNiKQAbaRYegobuX2BpZ6QjanKOXnJdTA=="], + + "npm/wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "npm/wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "npm/wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], "ora/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], @@ -5640,6 +6102,44 @@ "metro/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "npm/@npmcli/arborist/nopt/abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], + + "npm/@npmcli/arborist/parse-conflict-json/json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", {}, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="], + + "npm/@npmcli/config/nopt/abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], + + "npm/@npmcli/git/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + + "npm/@npmcli/run-script/@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + + "npm/@npmcli/run-script/node-gyp/nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], + + "npm/@npmcli/run-script/node-gyp/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + + "npm/cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "npm/init-package-json/read/mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + + "npm/libnpmexec/read/mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + + "npm/minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "npm/minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "npm/node-gyp/make-fetch-happen/@npmcli/agent": ["@npmcli/agent@3.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q=="], + + "npm/node-gyp/make-fetch-happen/cacache": ["cacache@19.0.1", "", { "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^12.0.0", "tar": "^7.4.3", "unique-filename": "^4.0.0" } }, "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ=="], + + "npm/node-gyp/make-fetch-happen/minipass-fetch": ["minipass-fetch@4.0.1", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ=="], + + "npm/pacote/@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + + "npm/promzard/read/mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + + "npm/wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "npm/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ora/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], @@ -5710,6 +6210,22 @@ "log-symbols/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + "npm/@npmcli/run-script/@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + + "npm/@npmcli/run-script/node-gyp/nopt/abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], + + "npm/@npmcli/run-script/node-gyp/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + + "npm/node-gyp/make-fetch-happen/@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob": ["glob@10.5.0", "", { "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" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "npm/node-gyp/make-fetch-happen/cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "npm/node-gyp/make-fetch-happen/minipass-fetch/minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], + + "npm/pacote/@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + "ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "workbox-build/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], @@ -5725,5 +6241,29 @@ "@react-native/metro-config/metro-config/metro/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "@react-native/metro-config/metro-config/metro/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "npm/node-gyp/make-fetch-happen/minipass-fetch/minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "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" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="], + + "npm/node-gyp/make-fetch-happen/minipass-fetch/minipass-sized/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "npm/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], } } diff --git a/package.json b/package.json index 9b34a2dd..61a69b9b 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "release": "bun scripts/release.mjs", "publish:stores": "bun scripts/store-delivery.mjs", "test:desktop:sidecar": "bun scripts/test-desktop-sidecar.mjs", - "test:desktop": "bun test apps/desktop/src/main/account-scope.test.mjs apps/desktop/src/main/app-protocol.test.mjs apps/desktop/src/main/child-process-start.test.mjs apps/desktop/src/main/clipboard-write.test.mjs apps/desktop/src/main/desktop-diagnostics.test.mjs apps/desktop/src/main/desktop-menu.test.mjs apps/desktop/src/main/desktop-update-flow.test.mjs apps/desktop/src/main/diagnostic-log.test.mjs apps/desktop/src/main/file-association.test.mjs apps/desktop/src/main/wechat-chat-archive.test.mjs apps/desktop/src/main/wechat-share-import.test.mjs apps/desktop/src/main/share-extension-registration.test.mjs apps/desktop/src/main/windows-share-menu.test.mjs apps/desktop/src/main/file-permissions.test.mjs apps/desktop/src/main/installation-location.test.mjs apps/desktop/src/main/local-data-reset.test.mjs apps/desktop/src/main/ai-direct.test.mjs apps/desktop/src/main/plugin-public-network.test.mjs apps/desktop/src/main/renderer-recovery.test.mjs apps/desktop/src/main/renderer-hibernate.test.mjs apps/desktop/src/main/renderer-startup-guard.test.mjs apps/desktop/src/main/renderer-storage-migration.test.mjs apps/desktop/src/main/resource-request.test.mjs apps/desktop/src/main/resource-url.test.mjs apps/desktop/src/main/scheduled-task-scheduler.test.mjs apps/desktop/src/main/screenshot-capture.test.mjs apps/desktop/src/main/staged-resource.test.mjs apps/desktop/src/main/staged-resource-alias.test.mjs apps/desktop/src/main/tray-icon.test.mjs apps/desktop/src/main/user-data-directory.test.mjs apps/desktop/src/main/window-open-policy.test.mjs apps/desktop/src/main/window-visibility.test.mjs apps/desktop/src/main/windows-update-trust.test.mjs apps/web/src/lib/api.test.mjs apps/web/src/lib/app-page-path.test.mjs apps/web/src/lib/clipboard.test.mjs apps/web/src/lib/desktop-renderer-ready.test.mjs apps/web/src/lib/desktop-workspace-restore.test.mjs apps/web/src/lib/desktop-repository.test.mjs apps/web/src/lib/desktop-resources.test.mjs apps/web/src/lib/desktop-sync.test.mjs apps/web/src/lib/local-mirror.test.mjs apps/web/src/lib/repository.test.mjs apps/web/src/lib/runtime.test.mjs apps/web/src/lib/screenshot-import.test.mjs apps/web/src/lib/wechat-chat-import.test.mjs apps/web/src/lib/shared-file-import.test.mjs apps/web/src/lib/sync-queue.test.mjs && bun test --cwd apps/desktop src/main/acp-host.test.mjs && bun run test:desktop:sidecar", + "test:desktop": "bun test apps/desktop/src/main/account-scope.test.mjs apps/desktop/src/main/app-protocol.test.mjs apps/desktop/src/main/child-process-start.test.mjs apps/desktop/src/main/clipboard-write.test.mjs apps/desktop/src/main/desktop-diagnostics.test.mjs apps/desktop/src/main/desktop-menu.test.mjs apps/desktop/src/main/desktop-update-flow.test.mjs apps/desktop/src/main/diagnostic-log.test.mjs apps/desktop/src/main/file-association.test.mjs apps/desktop/src/main/wechat-chat-archive.test.mjs apps/desktop/src/main/wechat-share-import.test.mjs apps/desktop/src/main/share-extension-registration.test.mjs apps/desktop/src/main/windows-share-menu.test.mjs apps/desktop/src/main/file-permissions.test.mjs apps/desktop/src/main/installation-location.test.mjs apps/desktop/src/main/local-data-reset.test.mjs apps/desktop/src/main/ai-direct.test.mjs apps/desktop/src/main/plugin-public-network.test.mjs apps/desktop/src/main/renderer-recovery.test.mjs apps/desktop/src/main/renderer-hibernate.test.mjs apps/desktop/src/main/renderer-startup-guard.test.mjs apps/desktop/src/main/renderer-storage-migration.test.mjs apps/desktop/src/main/resource-request.test.mjs apps/desktop/src/main/resource-url.test.mjs apps/desktop/src/main/scheduled-task-scheduler.test.mjs apps/desktop/src/main/screenshot-capture.test.mjs apps/desktop/src/main/staged-resource.test.mjs apps/desktop/src/main/staged-resource-alias.test.mjs apps/desktop/src/main/tray-icon.test.mjs apps/desktop/src/main/user-data-directory.test.mjs apps/desktop/src/main/window-open-policy.test.mjs apps/desktop/src/main/window-visibility.test.mjs apps/desktop/src/main/windows-update-trust.test.mjs apps/web/src/lib/api.test.mjs apps/web/src/lib/app-page-path.test.mjs apps/web/src/lib/clipboard.test.mjs apps/web/src/lib/desktop-renderer-ready.test.mjs apps/web/src/lib/desktop-workspace-restore.test.mjs apps/web/src/lib/desktop-repository.test.mjs apps/web/src/lib/desktop-resources.test.mjs apps/web/src/lib/desktop-sync.test.mjs apps/web/src/lib/local-mirror.test.mjs apps/web/src/lib/repository.test.mjs apps/web/src/lib/runtime.test.mjs apps/web/src/lib/screenshot-import.test.mjs apps/web/src/lib/wechat-chat-import.test.mjs apps/web/src/lib/shared-file-import.test.mjs apps/web/src/lib/sync-queue.test.mjs && bun test --cwd apps/desktop src/main/acp-adapter-manager.test.mjs src/main/acp-host.test.mjs && bun run test:desktop:sidecar", "verify:desktop-renderer": "bun scripts/verify-desktop-renderer.mjs", "verify:renderer-origin-migration": "bun run --cwd apps/desktop verify-origin-migration", "verify:desktop-cross-version-startup": "bun scripts/verify-desktop-cross-version-startup.mjs", diff --git a/packages/shared/src/i18n/en-US.ts b/packages/shared/src/i18n/en-US.ts index 1b7f4ca6..7370b91c 100644 --- a/packages/shared/src/i18n/en-US.ts +++ b/packages/shared/src/i18n/en-US.ts @@ -1375,23 +1375,41 @@ export const enUS = { builtin: "Built-in companion", builtinHint: "Uses the AI model configured below.", local: "Local ACP agent", - localHint: "Codex or Antigravity on this computer. EdgeEver does not grant the agent your notes folder or a terminal.", + localHint: "Codex, Antigravity, Grok Build, or WorkBuddy on this computer. EdgeEver does not provide your notes folder or a terminal through ACP; the agent's own tool permissions follow its configuration.", adapter: "Agent", codex: "Codex", antigravity: "Antigravity", claudeCode: "Claude Code", workbuddy: "WorkBuddy", + workbuddyCn: "WorkBuddy China", + workbuddyIntl: "WorkBuddy International", grokBuild: "Grok Build", pathLabel: "Antigravity executable", pathPlaceholder: "/absolute/path/to/antigravity-acp", - pathHint: "Absolute path to an executable file. EdgeEver does not download agents or search application folders.", + pathHint: "Enter the absolute path of an existing Antigravity ACP executable, or let EdgeEver install one on demand.", + install: "Install connector", + installHint: "When a local agent is detected, its connector downloads from the official source. This may exceed 100 MiB; updates are checked automatically.", + grokBuildMissingHint: "Grok Build includes ACP. Install the Grok CLI first; EdgeEver uses the local grok command without a separate connector download.", + grokBuildNotFound: "Grok CLI not found", + workbuddyNotFound: "WorkBuddy ACP program not found", + workbuddyMissingHint: "Install the WorkBuddy desktop app or CodeBuddy CLI. EdgeEver uses its built-in ACP; China and International accounts sign in separately.", + workbuddyCnLoginHint: "ACP sign-in is separate from the WorkBuddy desktop app. Use WeChat or your organization's sign-in method for China.", + workbuddyIntlLoginHint: "ACP sign-in is separate from the WorkBuddy desktop app. Use Google/GitHub or your organization's sign-in method for International.", + installing: "Installing and checking…", + installFailed: "Installation or connection check failed. The previous version was kept.", + updateFailed: "Automatic update failed. The previous version remains in use.", + authenticating: "Signing in…", + authenticateWith: "Sign in with {{method}}", + loginUnavailable: "This ACP connector requires authentication but provides no ACP sign-in method. Authenticate in the agent, then check the connection again.", + autoUpdated: "Auto-updated", probe: "Check connection", probing: "Checking…", probeHint: "Opens a temporary session and closes it. Your notes are not sent.", notProbed: "Not checked yet", invalidPath: "Enter an absolute path to an executable file.", states: { - not_installed: "Not installed", + not_installed: "ACP connector not found", + installing: "Installing the ACP connector automatically…", needs_login: "Needs login", available: "Session available", failed: "Connection failed", diff --git a/packages/shared/src/i18n/ja.ts b/packages/shared/src/i18n/ja.ts index 00d9a08c..b14b450a 100644 --- a/packages/shared/src/i18n/ja.ts +++ b/packages/shared/src/i18n/ja.ts @@ -1375,23 +1375,41 @@ export const ja = { builtin: "内蔵コンパニオン", builtinHint: "下で設定した AI モデルを使います。", local: "ローカル ACP エージェント", - localHint: "このコンピュータ上の Codex または Antigravity です。EdgeEver はノートのフォルダもターミナルも渡しません。", + localHint: "このコンピュータ上の Codex、Antigravity、Grok Build、または WorkBuddy です。EdgeEver は ACP 経由でノートのフォルダやターミナルを提供しません。エージェント自身のツール権限はその設定に従います。", adapter: "エージェント", codex: "Codex", antigravity: "Antigravity", claudeCode: "Claude Code", workbuddy: "WorkBuddy", + workbuddyCn: "WorkBuddy 中国版", + workbuddyIntl: "WorkBuddy 国際版", grokBuild: "Grok Build", pathLabel: "Antigravity の実行ファイル", pathPlaceholder: "/absolute/path/to/antigravity-acp", - pathHint: "実行ファイルの絶対パスを入力します。EdgeEver はエージェントをダウンロードせず、アプリケーションフォルダも検索しません。", + pathHint: "既存の Antigravity ACP 実行ファイルの絶対パスを入力するか、EdgeEver で必要時にインストールします。", + install: "接続コンポーネントをインストール", + installHint: "ローカルのエージェントを検出すると、公式配布元からダウンロードします。100 MiB を超える場合があり、インストール後は更新を自動確認します。", + grokBuildMissingHint: "Grok Build には ACP が内蔵されています。先に Grok CLI をインストールしてください。EdgeEver はローカルの grok コマンドを使用し、別の接続コンポーネントはダウンロードしません。", + grokBuildNotFound: "Grok CLI が見つかりません", + workbuddyNotFound: "WorkBuddy の ACP プログラムが見つかりません", + workbuddyMissingHint: "WorkBuddy デスクトップアプリまたは CodeBuddy CLI をインストールしてください。内蔵 ACP を使用し、中国版と国際版は別々にログインします。", + workbuddyCnLoginHint: "ACP ログインは WorkBuddy デスクトップアプリとは別です。中国版は WeChat または組織指定の方法を使用してください。", + workbuddyIntlLoginHint: "ACP ログインは WorkBuddy デスクトップアプリとは別です。国際版は Google/GitHub または組織指定の方法を使用してください。", + installing: "インストールして確認中…", + installFailed: "インストールまたは接続確認に失敗しました。以前のバージョンは保持されています。", + updateFailed: "自動更新に失敗しました。以前のバージョンを継続して使用します。", + authenticating: "ログイン中…", + authenticateWith: "{{method}} でログイン", + loginUnavailable: "この ACP 接続コンポーネントは認証が必要ですが、ACP のログイン方法を提供していません。エージェント側で認証してから再確認してください。", + autoUpdated: "自動更新", probe: "接続を確認", probing: "確認中…", probeHint: "一時的なセッションを開いてすぐ閉じます。ノートは送信しません。", notProbed: "未確認", invalidPath: "実行ファイルの絶対パスを入力してください。", states: { - not_installed: "未インストール", + not_installed: "ACP 接続コンポーネントが見つかりません", + installing: "ACP 接続コンポーネントを自動インストール中…", needs_login: "ログインが必要", available: "セッションを利用できます", failed: "接続に失敗しました", diff --git a/packages/shared/src/i18n/zh-CN.ts b/packages/shared/src/i18n/zh-CN.ts index 204e24f9..1a68ab5c 100644 --- a/packages/shared/src/i18n/zh-CN.ts +++ b/packages/shared/src/i18n/zh-CN.ts @@ -1375,23 +1375,41 @@ export const zhCN = { builtin: "内置助手", builtinHint: "使用下方配置的 AI 模型。", local: "本地 ACP 代理", - localHint: "使用这台电脑上的 Codex 或 Antigravity。EdgeEver 不会把笔记目录或终端交给代理。", + localHint: "使用这台电脑上的 Codex、Antigravity、Grok Build 或 WorkBuddy。EdgeEver 不通过 ACP 提供笔记目录或终端;代理自身的工具权限由其配置决定。", adapter: "代理", codex: "Codex", antigravity: "Antigravity", claudeCode: "Claude Code", workbuddy: "WorkBuddy", + workbuddyCn: "WorkBuddy 国内版", + workbuddyIntl: "WorkBuddy 国际版", grokBuild: "Grok Build", pathLabel: "Antigravity 可执行文件", pathPlaceholder: "/absolute/path/to/antigravity-acp", - pathHint: "填写可执行文件的绝对路径。EdgeEver 不会下载代理,也不会搜索应用程序目录。", + pathHint: "如需使用已有的 Antigravity ACP 程序,可填写其绝对路径;也可以让 EdgeEver 按需安装。", + install: "安装连接组件", + installHint: "检测到本机 Agent 后会从官方来源下载,可能超过 100 MiB;安装后会自动检查更新。", + grokBuildMissingHint: "Grok Build 已内置 ACP。请先安装 Grok CLI;EdgeEver 会使用本机的 grok 命令,无需下载单独适配器。", + grokBuildNotFound: "未找到 Grok CLI", + workbuddyNotFound: "未找到 WorkBuddy 的 ACP 程序", + workbuddyMissingHint: "请安装 WorkBuddy 桌面版或 CodeBuddy CLI。EdgeEver 使用其内置 ACP,国内版和国际版分别登录。", + workbuddyCnLoginHint: "ACP 登录与 WorkBuddy 桌面 App 的登录分开。国内版请使用微信或组织指定的登录方式。", + workbuddyIntlLoginHint: "ACP 登录与 WorkBuddy 桌面 App 的登录分开。国际版请使用 Google/GitHub 或组织指定的登录方式。", + installing: "正在安装并检查…", + installFailed: "安装或连接检查失败,原版本已保留。", + updateFailed: "自动更新失败,正在继续使用原版本。", + authenticating: "正在登录…", + authenticateWith: "使用 {{method}} 登录", + loginUnavailable: "这个 ACP 连接组件要求认证,但没有提供 ACP 登录入口。请在对应代理中完成认证后重试连接。", + autoUpdated: "自动更新", probe: "检查连接", probing: "正在检查…", probeHint: "会临时建立会话并立即关闭,不会发送你的笔记。", notProbed: "尚未检查", invalidPath: "请填写可执行文件的绝对路径。", states: { - not_installed: "未安装", + not_installed: "未找到 ACP 连接组件", + installing: "正在自动安装 ACP 连接组件…", needs_login: "需要登录", available: "会话可用", failed: "连接失败",