fix remote access review blockers
This commit is contained in:
parent
16ab83cfa5
commit
d87ff12291
|
|
@ -60,6 +60,7 @@
|
|||
"@xterm/xterm": "^6.0.0",
|
||||
"next": "^15.1.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"server-only": "^0.0.1",
|
||||
|
|
@ -73,6 +74,7 @@
|
|||
"@tailwindcss/postcss": "^4.0.0",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
||||
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
||||
import {
|
||||
createDefaultGlobalConfig,
|
||||
getGlobalConfigPath,
|
||||
|
|
@ -12,7 +12,6 @@ export type RemoteAuthCredentials = {
|
|||
|
||||
const TOKEN_VERSION = "v1";
|
||||
const TOKEN_TTL_MS = 5 * 60 * 1000;
|
||||
let lastActiveCredentialKey: string | undefined;
|
||||
|
||||
function base64UrlEncode(input: string | Buffer): string {
|
||||
return Buffer.from(input).toString("base64url");
|
||||
|
|
@ -44,16 +43,12 @@ export function rotateRemoteWsTokenSecret(): string {
|
|||
return secret;
|
||||
}
|
||||
|
||||
function noteActiveCredentials(credentials: RemoteAuthCredentials): void {
|
||||
const credentialKey = `${credentials.username}\0${credentials.password ?? ""}`;
|
||||
if (lastActiveCredentialKey === undefined) {
|
||||
lastActiveCredentialKey = credentialKey;
|
||||
return;
|
||||
}
|
||||
if (lastActiveCredentialKey !== credentialKey) {
|
||||
rotateRemoteWsTokenSecret();
|
||||
lastActiveCredentialKey = credentialKey;
|
||||
}
|
||||
function credentialDigest(credentials: RemoteAuthCredentials): string {
|
||||
return createHash("sha256")
|
||||
.update(credentials.username)
|
||||
.update("\0")
|
||||
.update(credentials.password ?? "")
|
||||
.digest("base64url");
|
||||
}
|
||||
|
||||
export function decodeBasicToken(
|
||||
|
|
@ -83,33 +78,35 @@ export function readConfiguredRemoteAuth(): RemoteAuthCredentials {
|
|||
};
|
||||
}
|
||||
|
||||
export function activeRemoteAuth(initialConfiguredAuth?: RemoteAuthCredentials): RemoteAuthCredentials {
|
||||
export function activeRemoteAuth(
|
||||
initialConfiguredAuth?: RemoteAuthCredentials,
|
||||
): RemoteAuthCredentials {
|
||||
const configured = readConfiguredRemoteAuth();
|
||||
if (
|
||||
initialConfiguredAuth &&
|
||||
(configured.username !== initialConfiguredAuth.username ||
|
||||
configured.password !== initialConfiguredAuth.password)
|
||||
) {
|
||||
noteActiveCredentials(configured);
|
||||
return configured;
|
||||
}
|
||||
|
||||
const active = {
|
||||
username: process.env.AO_REMOTE_AUTH_USER || initialConfiguredAuth?.username || configured.username,
|
||||
password: process.env.AO_REMOTE_AUTH_PASSWORD || initialConfiguredAuth?.password || configured.password,
|
||||
username:
|
||||
process.env.AO_REMOTE_AUTH_USER || initialConfiguredAuth?.username || configured.username,
|
||||
password:
|
||||
process.env.AO_REMOTE_AUTH_PASSWORD || initialConfiguredAuth?.password || configured.password,
|
||||
};
|
||||
noteActiveCredentials(active);
|
||||
return active;
|
||||
}
|
||||
|
||||
export function createRemoteWsToken(credentials: RemoteAuthCredentials): string | undefined {
|
||||
noteActiveCredentials(credentials);
|
||||
const secret = getTokenSecret();
|
||||
if (!secret) return undefined;
|
||||
|
||||
const payload = base64UrlEncode(
|
||||
JSON.stringify({
|
||||
u: credentials.username,
|
||||
c: credentialDigest(credentials),
|
||||
exp: Date.now() + TOKEN_TTL_MS,
|
||||
n: randomBytes(12).toString("base64url"),
|
||||
}),
|
||||
|
|
@ -144,9 +141,10 @@ export function verifyRemoteWsToken(
|
|||
if (!rawPayload) return false;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(rawPayload) as { u?: unknown; exp?: unknown };
|
||||
const parsed = JSON.parse(rawPayload) as { u?: unknown; c?: unknown; exp?: unknown };
|
||||
return (
|
||||
parsed.u === expectedCredentials.username &&
|
||||
parsed.c === credentialDigest(expectedCredentials) &&
|
||||
typeof parsed.exp === "number" &&
|
||||
parsed.exp >= Date.now()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { type ChildProcess } from "node:child_process";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
||||
import { resolve, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
|
@ -53,10 +52,11 @@ type RemoteAccessConfig = {
|
|||
|
||||
const globalConfig = loadGlobalConfig(getGlobalConfigPath()) ?? createDefaultGlobalConfig();
|
||||
const remoteAccessConfig: RemoteAccessConfig =
|
||||
globalConfig.remoteAccess && typeof globalConfig.remoteAccess === "object" ? globalConfig.remoteAccess : {};
|
||||
globalConfig.remoteAccess && typeof globalConfig.remoteAccess === "object"
|
||||
? globalConfig.remoteAccess
|
||||
: {};
|
||||
process.env["AO_REMOTE_AUTH_USER"] ||= remoteAccessConfig.username?.trim() || "ao";
|
||||
process.env["AO_REMOTE_AUTH_PASSWORD"] ||=
|
||||
remoteAccessConfig.password?.trim() || randomBytes(18).toString("base64url");
|
||||
process.env["AO_REMOTE_AUTH_PASSWORD"] ||= remoteAccessConfig.password?.trim() || "";
|
||||
ensureRemoteWsTokenSecret();
|
||||
|
||||
function log(label: string, msg: string): void {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import Image from "next/image";
|
||||
import QRCode from "qrcode";
|
||||
|
||||
interface RemoteHost {
|
||||
url: string;
|
||||
|
|
@ -18,10 +19,6 @@ interface RemoteInfo {
|
|||
hosts: RemoteHost[];
|
||||
}
|
||||
|
||||
function qrImageUrl(data: string, size = 200): string {
|
||||
return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(data)}&margin=10`;
|
||||
}
|
||||
|
||||
export function RemoteAccessQR() {
|
||||
const [info, setInfo] = useState<RemoteInfo | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
|
@ -31,6 +28,7 @@ export function RemoteAccessQR() {
|
|||
const [username, setUsername] = useState("ao");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [qrDataUrl, setQrDataUrl] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
|
@ -48,7 +46,9 @@ export function RemoteAccessQR() {
|
|||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
return () => { cancelled = true; };
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const toggle = useCallback(() => setOpen((v) => !v), []);
|
||||
|
|
@ -98,7 +98,28 @@ export function RemoteAccessQR() {
|
|||
const hosts = info?.hosts ?? [];
|
||||
const host = hosts[selectedHost] ?? hosts[0];
|
||||
const hasMultiple = hosts.length > 1;
|
||||
const qrUrl = host ? qrImageUrl(host.url) : null;
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setQrDataUrl(null);
|
||||
if (!host?.url) return;
|
||||
|
||||
QRCode.toDataURL(host.url, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
errorCorrectionLevel: "M",
|
||||
})
|
||||
.then((dataUrl) => {
|
||||
if (!cancelled) setQrDataUrl(dataUrl);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setQrDataUrl(null);
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [host?.url]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -145,16 +166,23 @@ export function RemoteAccessQR() {
|
|||
onClick={toggle}
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M18 6L6 18M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="remote-qr-modal__body">
|
||||
{qrUrl && host ? (
|
||||
{qrDataUrl && host ? (
|
||||
<Image
|
||||
src={qrUrl}
|
||||
src={qrDataUrl}
|
||||
alt={`QR code for ${host.url}`}
|
||||
className="remote-qr-modal__qr"
|
||||
width={200}
|
||||
|
|
@ -193,6 +221,7 @@ export function RemoteAccessQR() {
|
|||
<label className="remote-qr-modal__credential-field">
|
||||
<span>Password</span>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(event) => setPassword(event.target.value)}
|
||||
autoComplete="current-password"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import {
|
|||
spawnManagedDaemonChild,
|
||||
type GlobalConfig,
|
||||
} from "@aoagents/ao-core";
|
||||
import { rotateRemoteWsTokenSecret } from "../../server/remote-auth";
|
||||
|
||||
interface RemoteHost {
|
||||
url: string;
|
||||
|
|
@ -62,14 +61,14 @@ function configuredUsername(config = loadGlobalConfigOrDefault()): string {
|
|||
}
|
||||
|
||||
function configuredPassword(config = loadGlobalConfigOrDefault()): string | undefined {
|
||||
const password = remoteAccessConfig(config).password?.trim() || process.env["AO_REMOTE_AUTH_PASSWORD"];
|
||||
const password =
|
||||
remoteAccessConfig(config).password?.trim() || process.env["AO_REMOTE_AUTH_PASSWORD"];
|
||||
return password && password.length > 0 ? password : undefined;
|
||||
}
|
||||
|
||||
function applyRemoteCredentials(username: string, password: string): void {
|
||||
process.env["AO_REMOTE_AUTH_USER"] = username;
|
||||
process.env["AO_REMOTE_AUTH_PASSWORD"] = password;
|
||||
rotateRemoteWsTokenSecret();
|
||||
}
|
||||
|
||||
function generatePassword(): string {
|
||||
|
|
@ -81,7 +80,12 @@ function tryCloudflareUrl(line: string): string | null {
|
|||
}
|
||||
|
||||
function getCloudflaredCachePath(): string {
|
||||
return resolve(homedir(), ".agent-orchestrator", "bin", isWindows() ? "cloudflared.exe" : "cloudflared");
|
||||
return resolve(
|
||||
homedir(),
|
||||
".agent-orchestrator",
|
||||
"bin",
|
||||
isWindows() ? "cloudflared.exe" : "cloudflared",
|
||||
);
|
||||
}
|
||||
|
||||
function getCloudflaredDownload(): { url: string; archive: boolean } {
|
||||
|
|
@ -109,15 +113,18 @@ function getCloudflaredDownload(): { url: string; archive: boolean } {
|
|||
}
|
||||
|
||||
async function fetchExpectedCloudflaredChecksum(assetName: string): Promise<string> {
|
||||
const response = await fetch("https://api.github.com/repos/cloudflare/cloudflared/releases/latest", {
|
||||
headers: { Accept: "application/vnd.github+json" },
|
||||
});
|
||||
const response = await fetch(
|
||||
"https://api.github.com/repos/cloudflare/cloudflared/releases/latest",
|
||||
{
|
||||
headers: { Accept: "application/vnd.github+json" },
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error(`Failed to fetch cloudflared checksums (${response.status})`);
|
||||
|
||||
const release = (await response.json()) as { body?: unknown };
|
||||
const body = typeof release.body === "string" ? release.body : "";
|
||||
const escapedAssetName = assetName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const match = new RegExp(`${escapedAssetName}:\\s*([a-fA-F0-9]{64})`).exec(body);
|
||||
const match = new RegExp(`^${escapedAssetName}:\\s*([a-fA-F0-9]{64})`, "m").exec(body);
|
||||
if (!match?.[1]) throw new Error(`Missing SHA-256 checksum for ${assetName}`);
|
||||
return match[1].toLowerCase();
|
||||
}
|
||||
|
|
@ -149,7 +156,8 @@ async function downloadCloudflaredBinary(targetPath: string): Promise<void> {
|
|||
const { execFileSync } = await import("node:child_process");
|
||||
execFileSync("tar", ["-xzf", archivePath, "-C", tempDir]);
|
||||
const extractedPath = resolve(tempDir, "cloudflared");
|
||||
if (!existsSync(extractedPath)) throw new Error("cloudflared archive did not contain a binary");
|
||||
if (!existsSync(extractedPath))
|
||||
throw new Error("cloudflared archive did not contain a binary");
|
||||
chmodSync(extractedPath, 0o755);
|
||||
renameSync(extractedPath, targetPath);
|
||||
return;
|
||||
|
|
@ -214,7 +222,9 @@ export function saveRemoteAccessCredentials(input: {
|
|||
return getRemoteAccessInfo();
|
||||
}
|
||||
|
||||
async function startCloudflareTunnel(port: string): Promise<{ publicUrl: string; child: ChildProcess }> {
|
||||
async function startCloudflareTunnel(
|
||||
port: string,
|
||||
): Promise<{ publicUrl: string; child: ChildProcess }> {
|
||||
const cloudflared = await resolveCloudflaredBinary();
|
||||
const child = spawnManagedDaemonChild(
|
||||
"remote-tunnel",
|
||||
|
|
@ -230,7 +240,11 @@ async function startCloudflareTunnel(port: string): Promise<{ publicUrl: string;
|
|||
if (settled) return;
|
||||
settled = true;
|
||||
child.kill();
|
||||
reject(new Error(`Timed out waiting for cloudflared.${recentOutput ? ` Last output: ${recentOutput.trim()}` : ""}`));
|
||||
reject(
|
||||
new Error(
|
||||
`Timed out waiting for cloudflared.${recentOutput ? ` Last output: ${recentOutput.trim()}` : ""}`,
|
||||
),
|
||||
);
|
||||
}, 30_000);
|
||||
|
||||
function settle(publicUrl: string) {
|
||||
|
|
@ -259,7 +273,11 @@ async function startCloudflareTunnel(port: string): Promise<{ publicUrl: string;
|
|||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timeout);
|
||||
reject(new Error(`cloudflared exited before creating a tunnel${code === null ? "" : ` (${code})`}`));
|
||||
reject(
|
||||
new Error(
|
||||
`cloudflared exited before creating a tunnel${code === null ? "" : ` (${code})`}`,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
113
pnpm-lock.yaml
113
pnpm-lock.yaml
|
|
@ -727,6 +727,9 @@ importers:
|
|||
next-themes:
|
||||
specifier: ^0.4.6
|
||||
version: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
qrcode:
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4
|
||||
react:
|
||||
specifier: ^19.0.0
|
||||
version: 19.2.5
|
||||
|
|
@ -756,6 +759,9 @@ importers:
|
|||
'@testing-library/react':
|
||||
specifier: ^16.1.0
|
||||
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
'@types/qrcode':
|
||||
specifier: ^1.5.6
|
||||
version: 1.5.6
|
||||
'@types/react':
|
||||
specifier: ^19.0.0
|
||||
version: 19.2.14
|
||||
|
|
@ -1967,6 +1973,9 @@ packages:
|
|||
'@types/node@25.6.0':
|
||||
resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==}
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
|
||||
|
||||
'@types/react-dom@19.2.3':
|
||||
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -2267,6 +2276,10 @@ packages:
|
|||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
camelcase@5.3.1:
|
||||
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
caniuse-lite@1.0.30001787:
|
||||
resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==}
|
||||
|
||||
|
|
@ -2311,6 +2324,9 @@ packages:
|
|||
client-only@0.0.1:
|
||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||
|
||||
cliui@6.0.0:
|
||||
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -2372,6 +2388,10 @@ packages:
|
|||
supports-color:
|
||||
optional: true
|
||||
|
||||
decamelize@1.2.0:
|
||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
decimal.js@10.6.0:
|
||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||
|
||||
|
|
@ -2406,6 +2426,9 @@ packages:
|
|||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
dijkstrajs@1.0.3:
|
||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -3340,6 +3363,10 @@ packages:
|
|||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
pngjs@5.0.0:
|
||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
postcss@8.4.31:
|
||||
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
|
@ -3386,6 +3413,11 @@ packages:
|
|||
pusher-js@8.5.0:
|
||||
resolution: {integrity: sha512-V7uzGi9bqOOOyM/6IkJdpFyjGZj7llz1v0oWnYkZKcYLvbz6VcHVLmzKqkvegjuMumpfIEKGLmWHwFb39XFCpw==}
|
||||
|
||||
qrcode@1.5.4:
|
||||
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
quansync@0.2.11:
|
||||
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
|
||||
|
||||
|
|
@ -3428,6 +3460,9 @@ packages:
|
|||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
require-main-filename@2.0.0:
|
||||
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||
|
||||
resolve-from@5.0.0:
|
||||
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -3495,6 +3530,9 @@ packages:
|
|||
server-only@0.0.1:
|
||||
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
|
||||
|
||||
set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
|
||||
sharp@0.34.5:
|
||||
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
|
|
@ -3958,6 +3996,9 @@ packages:
|
|||
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
which-module@2.0.1:
|
||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||
|
||||
which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
|
@ -3977,6 +4018,10 @@ packages:
|
|||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -4019,6 +4064,9 @@ packages:
|
|||
xmlchars@2.2.0:
|
||||
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||
|
||||
y18n@4.0.3:
|
||||
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -4038,10 +4086,18 @@ packages:
|
|||
engines: {node: '>= 14.6'}
|
||||
hasBin: true
|
||||
|
||||
yargs-parser@18.1.3:
|
||||
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yargs@15.4.1:
|
||||
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -5069,6 +5125,10 @@ snapshots:
|
|||
dependencies:
|
||||
undici-types: 7.19.2
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
dependencies:
|
||||
'@types/node': 25.6.0
|
||||
|
||||
'@types/react-dom@19.2.3(@types/react@19.2.14)':
|
||||
dependencies:
|
||||
'@types/react': 19.2.14
|
||||
|
|
@ -5448,6 +5508,8 @@ snapshots:
|
|||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
|
||||
camelcase@5.3.1: {}
|
||||
|
||||
caniuse-lite@1.0.30001787: {}
|
||||
|
||||
chai@5.3.3:
|
||||
|
|
@ -5484,6 +5546,12 @@ snapshots:
|
|||
|
||||
client-only@0.0.1: {}
|
||||
|
||||
cliui@6.0.0:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 6.2.0
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
|
@ -5541,6 +5609,8 @@ snapshots:
|
|||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
decimal.js@10.6.0: {}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
|
|
@ -5563,6 +5633,8 @@ snapshots:
|
|||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
dijkstrajs@1.0.3: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
|
@ -6515,6 +6587,8 @@ snapshots:
|
|||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
pngjs@5.0.0: {}
|
||||
|
||||
postcss@8.4.31:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
|
|
@ -6569,6 +6643,12 @@ snapshots:
|
|||
dependencies:
|
||||
tweetnacl: 1.0.3
|
||||
|
||||
qrcode@1.5.4:
|
||||
dependencies:
|
||||
dijkstrajs: 1.0.3
|
||||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
quansync@0.2.11: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
|
@ -6613,6 +6693,8 @@ snapshots:
|
|||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
require-main-filename@2.0.0: {}
|
||||
|
||||
resolve-from@5.0.0: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
|
|
@ -6696,6 +6778,8 @@ snapshots:
|
|||
|
||||
server-only@0.0.1: {}
|
||||
|
||||
set-blocking@2.0.0: {}
|
||||
|
||||
sharp@0.34.5:
|
||||
dependencies:
|
||||
'@img/colour': 1.1.0
|
||||
|
|
@ -7152,6 +7236,8 @@ snapshots:
|
|||
tr46: 5.1.1
|
||||
webidl-conversions: 7.0.0
|
||||
|
||||
which-module@2.0.1: {}
|
||||
|
||||
which@2.0.2:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
|
@ -7167,6 +7253,12 @@ snapshots:
|
|||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
|
@ -7190,6 +7282,8 @@ snapshots:
|
|||
|
||||
xmlchars@2.2.0: {}
|
||||
|
||||
y18n@4.0.3: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
|
@ -7200,8 +7294,27 @@ snapshots:
|
|||
|
||||
yaml@2.8.3: {}
|
||||
|
||||
yargs-parser@18.1.3:
|
||||
dependencies:
|
||||
camelcase: 5.3.1
|
||||
decamelize: 1.2.0
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs@15.4.1:
|
||||
dependencies:
|
||||
cliui: 6.0.0
|
||||
decamelize: 1.2.0
|
||||
find-up: 4.1.0
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
require-main-filename: 2.0.0
|
||||
set-blocking: 2.0.0
|
||||
string-width: 4.2.3
|
||||
which-module: 2.0.1
|
||||
y18n: 4.0.3
|
||||
yargs-parser: 18.1.3
|
||||
|
||||
yargs@17.7.2:
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
|
|
|
|||
Loading…
Reference in New Issue