added sandbox to agent
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
"chat": "^4.29.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"drizzle-orm": "catalog:",
|
||||
"e2b": "^2.8.4",
|
||||
"hono": "4.12.7",
|
||||
"postgres": "catalog:",
|
||||
"zod": "^3.25.23"
|
||||
|
||||
@@ -5,9 +5,12 @@ import type { MessageListInput } from "@mastra/core/agent/message-list";
|
||||
import { z } from "zod";
|
||||
import { env as chatEnv } from "../lib/env.js";
|
||||
import { logger as rootLogger } from "../lib/logger.js";
|
||||
import { createE2bSandboxProvider } from "../providers/e2b/e2bSandboxProvider.js";
|
||||
import type { ChatContextMessage } from "../types.js";
|
||||
import { createFirecrawlTools } from "./firecrawl.js";
|
||||
import { createAutumnMcpClient, getAutumnMcpTools } from "./mcp.js";
|
||||
import { sandboxConfig } from "./sandbox/config.js";
|
||||
import { createSandboxTools } from "./sandbox/createSandboxTools.js";
|
||||
|
||||
export const agentDocUris = [
|
||||
"autumn://docs/tool-composition",
|
||||
@@ -31,6 +34,7 @@ Use web search only for current or external web context. Never use web search fo
|
||||
When web content influences the answer, cite the source URLs.
|
||||
Prefer searchWeb first, then scrapeUrl only for the most relevant result.
|
||||
Use listFeatures only when creating/customizing plan items or setting non-zero prepaid feature quantities and feature ids/types are not already known; never invent feature ids.
|
||||
Use the sandbox only for short parsing, calculation, transformation, and file-analysis tasks. Never send secrets to the sandbox, never use it for Autumn writes, and treat sandbox output as advisory.
|
||||
Preview billing-impacting changes first, summarize the preview in short Slack-friendly bullets, then call the matching write tool with the same request args.
|
||||
When Autumn responses include epoch millisecond timestamps, use epochMillisecondsToDate before explaining those timestamps to a user.
|
||||
Treat Slack PDFs and images attached to the latest message as part of the user's request. If an attachment was skipped or unavailable, say so briefly instead of pretending to have read it.
|
||||
@@ -112,20 +116,24 @@ export const runChatAgent = async ({
|
||||
env,
|
||||
logger = rootLogger,
|
||||
message,
|
||||
channelId,
|
||||
threadId,
|
||||
resourceId,
|
||||
onAction,
|
||||
provider,
|
||||
workspaceId,
|
||||
recentMessages,
|
||||
}: {
|
||||
token: string;
|
||||
env: AppEnv;
|
||||
logger?: AutumnLogger;
|
||||
message: MessageListInput;
|
||||
channelId: string;
|
||||
onAction?: (message: string) => Promise<void> | void;
|
||||
threadId: string;
|
||||
resourceId: string;
|
||||
provider: string;
|
||||
workspaceId: string;
|
||||
recentMessages?: ChatContextMessage[];
|
||||
}) => {
|
||||
const mcp = createAutumnMcpClient({
|
||||
@@ -171,13 +179,37 @@ export const runChatAgent = async ({
|
||||
apiKey: chatEnv.FIRECRAWL_API_KEY,
|
||||
onAction,
|
||||
});
|
||||
const sandboxTools =
|
||||
sandboxConfig.enabled && chatEnv.E2B_API_KEY
|
||||
? createSandboxTools({
|
||||
logger,
|
||||
onAction,
|
||||
provider: createE2bSandboxProvider({
|
||||
apiKey: chatEnv.E2B_API_KEY,
|
||||
context: {
|
||||
channelId,
|
||||
env,
|
||||
orgId: resourceId,
|
||||
provider,
|
||||
threadId,
|
||||
workspaceId,
|
||||
},
|
||||
sessionTimeoutMs: sandboxConfig.sessionTimeoutMs,
|
||||
}),
|
||||
})
|
||||
: {};
|
||||
if (sandboxConfig.enabled && !chatEnv.E2B_API_KEY) {
|
||||
logger.warn("Sandbox is enabled without an E2B API key", {
|
||||
event: "leaf.sandbox_disabled",
|
||||
});
|
||||
}
|
||||
await onAction?.("Reasoning over the request");
|
||||
const agent = new Agent({
|
||||
id: "autumn-chat",
|
||||
name: "Autumn Chat",
|
||||
instructions: `${instructions}\n\nCurrent Autumn environment: ${env}.\n\n${docsText}`,
|
||||
model: chatEnv.CHAT_MODEL,
|
||||
tools: { ...tools, ...firecrawlTools },
|
||||
tools: { ...tools, ...firecrawlTools, ...sandboxTools },
|
||||
});
|
||||
|
||||
const output = await agent.generate(message, {
|
||||
|
||||
@@ -21,6 +21,7 @@ export const runMessage = async ({
|
||||
onAction,
|
||||
recentMessages,
|
||||
text,
|
||||
channelId,
|
||||
threadId,
|
||||
}: BotMessage) =>
|
||||
withTimeout(
|
||||
@@ -55,9 +56,11 @@ export const runMessage = async ({
|
||||
logger,
|
||||
message: preparedMessage.message,
|
||||
onAction,
|
||||
channelId,
|
||||
threadId,
|
||||
resourceId: installation.org_id,
|
||||
provider: installation.provider,
|
||||
workspaceId: installation.workspace_id,
|
||||
recentMessages,
|
||||
}),
|
||||
);
|
||||
|
||||
4
apps/leaf/src/agent/sandbox/config.ts
Normal file
4
apps/leaf/src/agent/sandbox/config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const sandboxConfig = {
|
||||
enabled: true,
|
||||
sessionTimeoutMs: 10 * 60 * 1000,
|
||||
};
|
||||
83
apps/leaf/src/agent/sandbox/createSandboxTools.ts
Normal file
83
apps/leaf/src/agent/sandbox/createSandboxTools.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { AutumnLogger } from "@autumn/logging";
|
||||
import { createTool } from "@mastra/core/tools";
|
||||
import { z } from "zod";
|
||||
import { logger as rootLogger } from "../../lib/logger.js";
|
||||
import {
|
||||
assertSafeSandboxCommand,
|
||||
sandboxLimits,
|
||||
sanitizeReturnFiles,
|
||||
sanitizeSandboxFiles,
|
||||
truncateSandboxResult,
|
||||
} from "./guardrails.js";
|
||||
import type { SandboxProvider } from "./types.js";
|
||||
|
||||
const sandboxFileSchema = z
|
||||
.object({
|
||||
path: z.string().min(1),
|
||||
content: z.string(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const createSandboxTools = ({
|
||||
logger = rootLogger,
|
||||
onAction,
|
||||
provider,
|
||||
}: {
|
||||
logger?: AutumnLogger;
|
||||
onAction?: (message: string) => Promise<void> | void;
|
||||
provider: SandboxProvider;
|
||||
}): Record<string, ReturnType<typeof createTool>> => ({
|
||||
runSandboxCommand: createTool({
|
||||
id: "runSandboxCommand",
|
||||
description:
|
||||
"Run a short command in an isolated sandbox for parsing, calculations, JSON/CSV transforms, and file analysis. Do not use it for Autumn writes, credentials, secrets, or direct API calls.",
|
||||
inputSchema: z
|
||||
.object({
|
||||
task: z.string().min(1),
|
||||
command: z.string().min(1),
|
||||
files: z
|
||||
.array(sandboxFileSchema)
|
||||
.max(sandboxLimits.maxFiles)
|
||||
.optional(),
|
||||
returnFiles: z
|
||||
.array(z.string().min(1))
|
||||
.max(sandboxLimits.maxFiles)
|
||||
.optional(),
|
||||
})
|
||||
.strict(),
|
||||
execute: async ({ task, command, files = [], returnFiles = [] }) => {
|
||||
await onAction?.("Running sandbox analysis");
|
||||
const startedAt = Date.now();
|
||||
const safeCommand = assertSafeSandboxCommand(command);
|
||||
const safeFiles = sanitizeSandboxFiles(files);
|
||||
const safeReturnFiles = sanitizeReturnFiles(returnFiles);
|
||||
logger.info("Calling sandbox tool", {
|
||||
event: "leaf.sandbox_tool_called",
|
||||
data: {
|
||||
file_count: safeFiles.length,
|
||||
return_file_count: safeReturnFiles.length,
|
||||
command_length: safeCommand.length,
|
||||
task_length: task.length,
|
||||
},
|
||||
});
|
||||
const result = truncateSandboxResult(
|
||||
await provider.run({
|
||||
command: safeCommand,
|
||||
files: safeFiles,
|
||||
returnFiles: safeReturnFiles,
|
||||
timeoutMs: sandboxLimits.timeoutMs,
|
||||
}),
|
||||
);
|
||||
logger.info("Completed sandbox tool", {
|
||||
event: "leaf.sandbox_tool_completed",
|
||||
data: {
|
||||
duration_ms: Date.now() - startedAt,
|
||||
file_count: result.files.length,
|
||||
exit_code: result.exitCode,
|
||||
timed_out: result.timedOut,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
});
|
||||
106
apps/leaf/src/agent/sandbox/guardrails.ts
Normal file
106
apps/leaf/src/agent/sandbox/guardrails.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import path from "node:path";
|
||||
import type { SandboxFile, SandboxRunResult } from "./types.js";
|
||||
|
||||
export const sandboxLimits = {
|
||||
maxFiles: 5,
|
||||
maxInputBytes: 256 * 1024,
|
||||
maxOutputBytes: 24 * 1024,
|
||||
maxReturnedFileBytes: 24 * 1024,
|
||||
timeoutMs: 20_000,
|
||||
workDir: "/work",
|
||||
};
|
||||
|
||||
const secretPatterns = [
|
||||
/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i,
|
||||
/\bxox[baprs]-[A-Za-z0-9-]{10,}/i,
|
||||
/\bsk_(?:live|test|proj)?_[A-Za-z0-9]{12,}/i,
|
||||
/\beyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{10,}\b/,
|
||||
/^\s*[A-Z0-9_]*(?:SECRET|TOKEN|API_KEY|PASSWORD)[A-Z0-9_]*\s*=\s*\S+/im,
|
||||
];
|
||||
|
||||
const byteLength = (value: string) => Buffer.byteLength(value, "utf8");
|
||||
|
||||
const assertNoSecrets = (value: string) => {
|
||||
if (secretPatterns.some((pattern) => pattern.test(value))) {
|
||||
throw new Error("Sandbox input appears to contain a secret or token");
|
||||
}
|
||||
};
|
||||
|
||||
const normalizePath = (filePath: string) => {
|
||||
const normalized = path.posix.normalize(
|
||||
filePath.startsWith("/")
|
||||
? filePath
|
||||
: path.posix.join(sandboxLimits.workDir, filePath),
|
||||
);
|
||||
if (
|
||||
normalized !== sandboxLimits.workDir &&
|
||||
!normalized.startsWith(`${sandboxLimits.workDir}/`)
|
||||
) {
|
||||
throw new Error("Sandbox files must stay under /work");
|
||||
}
|
||||
if (path.posix.basename(normalized).toLowerCase() === ".env") {
|
||||
throw new Error("Sandbox files cannot be named .env");
|
||||
}
|
||||
return normalized;
|
||||
};
|
||||
|
||||
export const sanitizeSandboxFiles = (files: SandboxFile[] = []) => {
|
||||
if (files.length > sandboxLimits.maxFiles) {
|
||||
throw new Error(
|
||||
`Sandbox input cannot exceed ${sandboxLimits.maxFiles} files`,
|
||||
);
|
||||
}
|
||||
|
||||
let bytes = 0;
|
||||
const sanitized = files.map((file) => {
|
||||
assertNoSecrets(file.path);
|
||||
assertNoSecrets(file.content);
|
||||
bytes += byteLength(file.content);
|
||||
if (bytes > sandboxLimits.maxInputBytes) {
|
||||
throw new Error("Sandbox input is too large");
|
||||
}
|
||||
return {
|
||||
path: normalizePath(file.path),
|
||||
content: file.content,
|
||||
};
|
||||
});
|
||||
|
||||
const seen = new Set<string>();
|
||||
for (const file of sanitized) {
|
||||
if (seen.has(file.path))
|
||||
throw new Error(`Duplicate sandbox file: ${file.path}`);
|
||||
seen.add(file.path);
|
||||
}
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
export const sanitizeReturnFiles = (returnFiles: string[] = []) =>
|
||||
returnFiles.map(normalizePath);
|
||||
|
||||
export const assertSafeSandboxCommand = (command: string) => {
|
||||
if (!command.trim()) throw new Error("Sandbox command cannot be empty");
|
||||
assertNoSecrets(command);
|
||||
return command.trim();
|
||||
};
|
||||
|
||||
export const truncateText = (value: string, maxBytes: number) => {
|
||||
if (byteLength(value) <= maxBytes) return value;
|
||||
let output = "";
|
||||
for (const char of value) {
|
||||
if (byteLength(`${output}${char}`) > maxBytes) break;
|
||||
output += char;
|
||||
}
|
||||
return `${output}\n[truncated]`;
|
||||
};
|
||||
|
||||
export const truncateSandboxResult = (
|
||||
result: SandboxRunResult,
|
||||
): SandboxRunResult => ({
|
||||
...result,
|
||||
stdout: truncateText(result.stdout, sandboxLimits.maxOutputBytes),
|
||||
stderr: truncateText(result.stderr, sandboxLimits.maxOutputBytes),
|
||||
files: result.files.map((file) => ({
|
||||
path: file.path,
|
||||
content: truncateText(file.content, sandboxLimits.maxReturnedFileBytes),
|
||||
})),
|
||||
});
|
||||
32
apps/leaf/src/agent/sandbox/types.ts
Normal file
32
apps/leaf/src/agent/sandbox/types.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export type SandboxFile = {
|
||||
path: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export type SandboxRunArgs = {
|
||||
command: string;
|
||||
files: SandboxFile[];
|
||||
returnFiles: string[];
|
||||
timeoutMs: number;
|
||||
};
|
||||
|
||||
export type SandboxRunResult = {
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
exitCode?: number;
|
||||
timedOut: boolean;
|
||||
files: SandboxFile[];
|
||||
};
|
||||
|
||||
export type SandboxSessionContext = {
|
||||
channelId: string;
|
||||
env: string;
|
||||
orgId: string;
|
||||
provider: string;
|
||||
threadId: string;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
export type SandboxProvider = {
|
||||
run(args: SandboxRunArgs): Promise<SandboxRunResult>;
|
||||
};
|
||||
@@ -149,6 +149,7 @@ const runAndReply = async ({
|
||||
onAction: logAction,
|
||||
recentMessages,
|
||||
text,
|
||||
channelId,
|
||||
threadId,
|
||||
});
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ const envSchema = z
|
||||
CHAT_STATE_SECRET: optionalString,
|
||||
CLIENT_URL: z.string().min(1).default("http://localhost:3000"),
|
||||
DATABASE_URL: z.string().min(1),
|
||||
E2B_API_KEY: optionalString,
|
||||
ENCRYPTION_PASSWORD: z.string().min(1),
|
||||
FIRECRAWL_API_KEY: z.string().min(1),
|
||||
MCP_OAUTH_ENVIRONMENT: z.enum(["live", "sandbox"]).default("sandbox"),
|
||||
|
||||
46
apps/leaf/src/providers/e2b/e2bSandboxFiles.ts
Normal file
46
apps/leaf/src/providers/e2b/e2bSandboxFiles.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { SandboxFile } from "../../agent/sandbox/types.js";
|
||||
import type { E2bSandbox } from "./e2bSandboxLifecycle.js";
|
||||
|
||||
export const e2bWorkDir = "/work";
|
||||
|
||||
export const ensureE2bWorkDir = async ({
|
||||
sandbox,
|
||||
}: {
|
||||
sandbox: E2bSandbox;
|
||||
}) => {
|
||||
try {
|
||||
await sandbox.files.makeDir(e2bWorkDir);
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error) || !/exist/i.test(error.message)) throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const writeE2bSandboxFiles = async ({
|
||||
files,
|
||||
sandbox,
|
||||
}: {
|
||||
files: SandboxFile[];
|
||||
sandbox: E2bSandbox;
|
||||
}) => {
|
||||
for (const file of files) {
|
||||
await sandbox.files.write(file.path, file.content);
|
||||
}
|
||||
};
|
||||
|
||||
export const readRequestedE2bFiles = async ({
|
||||
returnFiles,
|
||||
sandbox,
|
||||
}: {
|
||||
returnFiles: string[];
|
||||
sandbox: E2bSandbox;
|
||||
}): Promise<SandboxFile[]> => {
|
||||
const files: SandboxFile[] = [];
|
||||
for (const filePath of returnFiles) {
|
||||
if (!(await sandbox.files.exists(filePath))) continue;
|
||||
files.push({
|
||||
path: filePath,
|
||||
content: await sandbox.files.read(filePath),
|
||||
});
|
||||
}
|
||||
return files;
|
||||
};
|
||||
78
apps/leaf/src/providers/e2b/e2bSandboxLifecycle.ts
Normal file
78
apps/leaf/src/providers/e2b/e2bSandboxLifecycle.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Sandbox } from "e2b";
|
||||
import type { SandboxSessionContext } from "../../agent/sandbox/types.js";
|
||||
import {
|
||||
e2bSandboxLookupMetadata,
|
||||
e2bSandboxMetadata,
|
||||
} from "./e2bSandboxMetadata.js";
|
||||
|
||||
export type E2bSandbox = Awaited<ReturnType<typeof Sandbox.create>>;
|
||||
|
||||
export const findE2bSandbox = async ({
|
||||
apiKey,
|
||||
context,
|
||||
}: {
|
||||
apiKey: string;
|
||||
context: SandboxSessionContext;
|
||||
}) => {
|
||||
const paginator = Sandbox.list({
|
||||
apiKey,
|
||||
query: {
|
||||
metadata: e2bSandboxLookupMetadata({ context }),
|
||||
state: ["running", "paused"],
|
||||
},
|
||||
});
|
||||
const matches = await paginator.nextItems();
|
||||
return matches[0];
|
||||
};
|
||||
|
||||
export const createE2bSandbox = ({
|
||||
apiKey,
|
||||
context,
|
||||
timeoutMs,
|
||||
}: {
|
||||
apiKey: string;
|
||||
context: SandboxSessionContext;
|
||||
timeoutMs: number;
|
||||
}) =>
|
||||
Sandbox.create({
|
||||
allowInternetAccess: false,
|
||||
apiKey,
|
||||
metadata: e2bSandboxMetadata({ context }),
|
||||
network: { allowPublicTraffic: false },
|
||||
timeoutMs,
|
||||
});
|
||||
|
||||
export const connectE2bSandbox = ({
|
||||
apiKey,
|
||||
sandboxId,
|
||||
timeoutMs,
|
||||
}: {
|
||||
apiKey: string;
|
||||
sandboxId: string;
|
||||
timeoutMs: number;
|
||||
}) =>
|
||||
Sandbox.connect(sandboxId, {
|
||||
apiKey,
|
||||
timeoutMs,
|
||||
});
|
||||
|
||||
export const findOrCreateE2bSandbox = async ({
|
||||
apiKey,
|
||||
context,
|
||||
timeoutMs,
|
||||
}: {
|
||||
apiKey: string;
|
||||
context: SandboxSessionContext;
|
||||
timeoutMs: number;
|
||||
}) => {
|
||||
const existing = await findE2bSandbox({ apiKey, context });
|
||||
if (existing) {
|
||||
return connectE2bSandbox({
|
||||
apiKey,
|
||||
sandboxId: existing.sandboxId,
|
||||
timeoutMs,
|
||||
});
|
||||
}
|
||||
|
||||
return createE2bSandbox({ apiKey, context, timeoutMs });
|
||||
};
|
||||
40
apps/leaf/src/providers/e2b/e2bSandboxMetadata.ts
Normal file
40
apps/leaf/src/providers/e2b/e2bSandboxMetadata.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { SandboxSessionContext } from "../../agent/sandbox/types.js";
|
||||
|
||||
const metadataApp = "leaf";
|
||||
|
||||
export const e2bThreadKey = ({ context }: { context: SandboxSessionContext }) =>
|
||||
[
|
||||
context.orgId,
|
||||
context.env,
|
||||
context.provider,
|
||||
context.workspaceId,
|
||||
context.channelId,
|
||||
context.threadId,
|
||||
].join(":");
|
||||
|
||||
export const e2bSandboxMetadata = ({
|
||||
context,
|
||||
}: {
|
||||
context: SandboxSessionContext;
|
||||
}) => ({
|
||||
app: metadataApp,
|
||||
channelId: context.channelId,
|
||||
env: context.env,
|
||||
orgId: context.orgId,
|
||||
provider: context.provider,
|
||||
threadId: context.threadId,
|
||||
threadKey: e2bThreadKey({ context }),
|
||||
workspaceId: context.workspaceId,
|
||||
});
|
||||
|
||||
export const e2bSandboxLookupMetadata = ({
|
||||
context,
|
||||
}: {
|
||||
context: SandboxSessionContext;
|
||||
}) => {
|
||||
const metadata = e2bSandboxMetadata({ context });
|
||||
return {
|
||||
app: metadata.app,
|
||||
threadKey: metadata.threadKey,
|
||||
};
|
||||
};
|
||||
55
apps/leaf/src/providers/e2b/e2bSandboxProvider.ts
Normal file
55
apps/leaf/src/providers/e2b/e2bSandboxProvider.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type {
|
||||
SandboxProvider,
|
||||
SandboxRunResult,
|
||||
SandboxSessionContext,
|
||||
} from "../../agent/sandbox/types.js";
|
||||
import {
|
||||
e2bWorkDir,
|
||||
ensureE2bWorkDir,
|
||||
readRequestedE2bFiles,
|
||||
writeE2bSandboxFiles,
|
||||
} from "./e2bSandboxFiles.js";
|
||||
import { findOrCreateE2bSandbox } from "./e2bSandboxLifecycle.js";
|
||||
|
||||
export const createE2bSandboxProvider = ({
|
||||
apiKey,
|
||||
context,
|
||||
sessionTimeoutMs,
|
||||
}: {
|
||||
apiKey: string;
|
||||
context: SandboxSessionContext;
|
||||
sessionTimeoutMs: number;
|
||||
}): SandboxProvider => ({
|
||||
run: async ({ command, files, returnFiles, timeoutMs }) => {
|
||||
const sandbox = await findOrCreateE2bSandbox({
|
||||
apiKey,
|
||||
context,
|
||||
timeoutMs: sessionTimeoutMs,
|
||||
});
|
||||
try {
|
||||
await ensureE2bWorkDir({ sandbox });
|
||||
await writeE2bSandboxFiles({ files, sandbox });
|
||||
const result = await sandbox.commands.run(command, {
|
||||
cwd: e2bWorkDir,
|
||||
timeoutMs,
|
||||
});
|
||||
return {
|
||||
stdout: result.stdout ?? "",
|
||||
stderr: result.stderr ?? "",
|
||||
exitCode: result.exitCode,
|
||||
timedOut: false,
|
||||
files: await readRequestedE2bFiles({ returnFiles, sandbox }),
|
||||
} satisfies SandboxRunResult;
|
||||
} catch (error) {
|
||||
const timedOut =
|
||||
error instanceof Error && /timeout|timed out/i.test(error.message);
|
||||
if (!timedOut) throw error;
|
||||
return {
|
||||
stdout: "",
|
||||
stderr: "Sandbox command timed out",
|
||||
timedOut: true,
|
||||
files: await readRequestedE2bFiles({ returnFiles, sandbox }),
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -72,6 +72,7 @@ export type BotMessage = {
|
||||
onAction?: (message: string) => Promise<void> | void;
|
||||
recentMessages?: ChatContextMessage[];
|
||||
text: string;
|
||||
channelId: string;
|
||||
threadId: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { createSandboxTools } from "../../../../src/agent/sandbox/createSandboxTools.js";
|
||||
import type { SandboxProvider } from "../../../../src/agent/sandbox/types.js";
|
||||
|
||||
const execute = async (
|
||||
tool: { execute?: (...args: never[]) => Promise<unknown> } | undefined,
|
||||
input: unknown,
|
||||
) => {
|
||||
if (!tool?.execute) throw new Error("Tool is not executable");
|
||||
return tool.execute(input as never, {} as never);
|
||||
};
|
||||
|
||||
describe("sandbox tools", () => {
|
||||
test("calls provider with sanitized files and return paths", async () => {
|
||||
const provider: SandboxProvider = {
|
||||
run: async (args) => {
|
||||
expect(args.command).toBe("python analyze.py");
|
||||
expect(args.files).toEqual([
|
||||
{ path: "/work/input.json", content: '{"ok":true}' },
|
||||
]);
|
||||
expect(args.returnFiles).toEqual(["/work/result.json"]);
|
||||
expect(args.timeoutMs).toBe(20_000);
|
||||
return {
|
||||
stdout: "done",
|
||||
stderr: "",
|
||||
exitCode: 0,
|
||||
timedOut: false,
|
||||
files: [{ path: "/work/result.json", content: '{"done":true}' }],
|
||||
};
|
||||
},
|
||||
};
|
||||
const tools = createSandboxTools({ provider });
|
||||
|
||||
await expect(
|
||||
execute(tools.runSandboxCommand, {
|
||||
task: "analyze json",
|
||||
command: "python analyze.py",
|
||||
files: [{ path: "input.json", content: '{"ok":true}' }],
|
||||
returnFiles: ["result.json"],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
stdout: "done",
|
||||
stderr: "",
|
||||
exitCode: 0,
|
||||
timedOut: false,
|
||||
files: [{ path: "/work/result.json", content: '{"done":true}' }],
|
||||
});
|
||||
});
|
||||
|
||||
test("rejects unsafe input before calling provider", async () => {
|
||||
let called = false;
|
||||
const provider: SandboxProvider = {
|
||||
run: async () => {
|
||||
called = true;
|
||||
throw new Error("should not run");
|
||||
},
|
||||
};
|
||||
const tools = createSandboxTools({ provider });
|
||||
|
||||
await expect(
|
||||
execute(tools.runSandboxCommand, {
|
||||
task: "leak",
|
||||
command: "echo ok",
|
||||
files: [{ path: ".env", content: "API_KEY=secret" }],
|
||||
}),
|
||||
).rejects.toThrow();
|
||||
expect(called).toBe(false);
|
||||
});
|
||||
|
||||
test("truncates provider output", async () => {
|
||||
const provider: SandboxProvider = {
|
||||
run: async () => ({
|
||||
stdout: "a".repeat(25 * 1024),
|
||||
stderr: "",
|
||||
timedOut: false,
|
||||
files: [{ path: "/work/out.txt", content: "b".repeat(25 * 1024) }],
|
||||
}),
|
||||
};
|
||||
const tools = createSandboxTools({ provider });
|
||||
const result = (await execute(tools.runSandboxCommand, {
|
||||
task: "large",
|
||||
command: "cat out.txt",
|
||||
})) as { stdout: string; files: Array<{ content: string }> };
|
||||
|
||||
expect(result.stdout).toEndWith("[truncated]");
|
||||
expect(result.files[0]?.content).toEndWith("[truncated]");
|
||||
});
|
||||
});
|
||||
63
apps/leaf/tests/unit/agent/sandbox/guardrails.test.ts
Normal file
63
apps/leaf/tests/unit/agent/sandbox/guardrails.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
assertSafeSandboxCommand,
|
||||
sanitizeReturnFiles,
|
||||
sanitizeSandboxFiles,
|
||||
truncateText,
|
||||
} from "../../../../src/agent/sandbox/guardrails.js";
|
||||
|
||||
describe("sandbox guardrails", () => {
|
||||
test("normalizes relative file paths under /work", () => {
|
||||
expect(
|
||||
sanitizeSandboxFiles([{ path: "input.json", content: "{}" }]),
|
||||
).toEqual([{ path: "/work/input.json", content: "{}" }]);
|
||||
expect(sanitizeReturnFiles(["result.json"])).toEqual(["/work/result.json"]);
|
||||
});
|
||||
|
||||
test("rejects files outside /work", () => {
|
||||
expect(() =>
|
||||
sanitizeSandboxFiles([{ path: "../secrets.txt", content: "x" }]),
|
||||
).toThrow("Sandbox files must stay under /work");
|
||||
expect(() => sanitizeReturnFiles(["/etc/passwd"])).toThrow(
|
||||
"Sandbox files must stay under /work",
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects .env files", () => {
|
||||
expect(() =>
|
||||
sanitizeSandboxFiles([{ path: ".env", content: "FOO=bar" }]),
|
||||
).toThrow("Sandbox files cannot be named .env");
|
||||
});
|
||||
|
||||
test("rejects token-like input", () => {
|
||||
expect(() =>
|
||||
sanitizeSandboxFiles([
|
||||
{ path: "input.txt", content: "SLACK_BOT_TOKEN=xoxb-1234567890" },
|
||||
]),
|
||||
).toThrow("Sandbox input appears to contain a secret or token");
|
||||
expect(() =>
|
||||
assertSafeSandboxCommand("echo Bearer abcdefghijklmnopqrstuvwxyz"),
|
||||
).toThrow("Sandbox input appears to contain a secret or token");
|
||||
});
|
||||
|
||||
test("enforces file count and total bytes", () => {
|
||||
expect(() =>
|
||||
sanitizeSandboxFiles(
|
||||
Array.from({ length: 6 }, (_, index) => ({
|
||||
path: `file-${index}.txt`,
|
||||
content: "x",
|
||||
})),
|
||||
),
|
||||
).toThrow("Sandbox input cannot exceed 5 files");
|
||||
expect(() =>
|
||||
sanitizeSandboxFiles([
|
||||
{ path: "large.txt", content: "x".repeat(257 * 1024) },
|
||||
]),
|
||||
).toThrow("Sandbox input is too large");
|
||||
});
|
||||
|
||||
test("truncates text by bytes", () => {
|
||||
const result = truncateText("a".repeat(10), 5);
|
||||
expect(result).toBe("aaaaa\n[truncated]");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
e2bSandboxLookupMetadata,
|
||||
e2bSandboxMetadata,
|
||||
e2bThreadKey,
|
||||
} from "../../../../src/providers/e2b/e2bSandboxMetadata.js";
|
||||
|
||||
const context = {
|
||||
channelId: "C123",
|
||||
env: "live",
|
||||
orgId: "org_123",
|
||||
provider: "slack",
|
||||
threadId: "1710000000.000",
|
||||
workspaceId: "T123",
|
||||
};
|
||||
|
||||
describe("E2B sandbox metadata", () => {
|
||||
test("builds stable thread keys", () => {
|
||||
expect(e2bThreadKey({ context })).toBe(
|
||||
"org_123:live:slack:T123:C123:1710000000.000",
|
||||
);
|
||||
});
|
||||
|
||||
test("builds full metadata and lookup metadata", () => {
|
||||
expect(e2bSandboxMetadata({ context })).toEqual({
|
||||
app: "leaf",
|
||||
channelId: "C123",
|
||||
env: "live",
|
||||
orgId: "org_123",
|
||||
provider: "slack",
|
||||
threadId: "1710000000.000",
|
||||
threadKey: "org_123:live:slack:T123:C123:1710000000.000",
|
||||
workspaceId: "T123",
|
||||
});
|
||||
expect(e2bSandboxLookupMetadata({ context })).toEqual({
|
||||
app: "leaf",
|
||||
threadKey: "org_123:live:slack:T123:C123:1710000000.000",
|
||||
});
|
||||
});
|
||||
});
|
||||
101
bun.lock
101
bun.lock
@@ -101,6 +101,7 @@
|
||||
"chat": "^4.29.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"drizzle-orm": "catalog:",
|
||||
"e2b": "^2.8.4",
|
||||
"hono": "4.12.7",
|
||||
"postgres": "catalog:",
|
||||
"zod": "^3.25.23",
|
||||
@@ -1085,6 +1086,10 @@
|
||||
|
||||
"@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
|
||||
|
||||
"@connectrpc/connect": ["@connectrpc/connect@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0" } }, "sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ=="],
|
||||
|
||||
"@connectrpc/connect-web": ["@connectrpc/connect-web@2.0.0-rc.3", "", { "peerDependencies": { "@bufbuild/protobuf": "^2.2.0", "@connectrpc/connect": "2.0.0-rc.3" } }, "sha512-w88P8Lsn5CCsA7MFRl2e6oLY4J/5toiNtJns/YJrlyQaWOy3RO8pDgkz+iIkG98RPMhj2thuBvsd3Cn4DKKCkw=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
"@datadog/datadog-api-client": ["@datadog/datadog-api-client@1.58.0", "", { "dependencies": { "@types/buffer-from": "^1.1.0", "@types/node": "*", "@types/pako": "^1.0.3", "buffer-from": "^1.1.2", "cross-fetch": "^3.1.5", "form-data": "^4.0.4", "loglevel": "^1.8.1", "pako": "^2.0.4" } }, "sha512-aDCMu+qEXjr8PHkT8XvY7FTzVNi2z7yVJy39I4s0lVtb+sdth4ZppMC1aCDwGJQMHK4J0aect+xKScuHy/Xilg=="],
|
||||
@@ -1347,7 +1352,7 @@
|
||||
|
||||
"@ioredis/commands": ["@ioredis/commands@1.5.1", "", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="],
|
||||
|
||||
"@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=="],
|
||||
"@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="],
|
||||
|
||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
@@ -3155,6 +3160,8 @@
|
||||
|
||||
"common-path-prefix": ["common-path-prefix@3.0.0", "", {}, "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="],
|
||||
|
||||
"compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="],
|
||||
|
||||
"component-emitter": ["component-emitter@1.3.1", "", {}, "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
@@ -3425,6 +3432,8 @@
|
||||
|
||||
"dns-socket": ["dns-socket@4.2.2", "", { "dependencies": { "dns-packet": "^5.2.4" } }, "sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg=="],
|
||||
|
||||
"dockerfile-ast": ["dockerfile-ast@0.7.1", "", { "dependencies": { "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3" } }, "sha512-oX/A4I0EhSkGqrFv0YuvPkBUSYp1XiY8O8zAKc8Djglx8ocz+JfOr8gP0ryRMC2myqvDLagmnZaU9ot1vG2ijw=="],
|
||||
|
||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
|
||||
"dogapi": ["dogapi@2.8.4", "", { "dependencies": { "extend": "^3.0.2", "json-bigint": "^1.0.0", "lodash": "^4.17.21", "minimist": "^1.2.5", "rc": "^1.2.8" }, "bin": { "dogapi": "bin/dogapi" } }, "sha512-065fsvu5dB0o4+ENtLjZILvXMClDNH/yA9H6L8nsdcNiz9l0Hzpn7aQaCOPYXxqyzq4CRPOdwkFXUjDOXfRGbg=="],
|
||||
@@ -3451,6 +3460,8 @@
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"e2b": ["e2b@2.27.1", "", { "dependencies": { "@bufbuild/protobuf": "^2.6.2", "@connectrpc/connect": "2.0.0-rc.3", "@connectrpc/connect-web": "2.0.0-rc.3", "chalk": "^5.3.0", "compare-versions": "^6.1.0", "dockerfile-ast": "^0.7.1", "glob": "^11.1.0", "openapi-fetch": "^0.14.1", "platform": "^1.3.6", "tar": "^7.5.11", "undici": "^7.25.0" } }, "sha512-xZ1vXSl4dpWxbvan5vihE2embXzHdlpK1N0CmFUIcj5kdGLpiQXGoQYsz1Dhy8wr9VO724DyRC7Y3iblMElLPQ=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
||||
@@ -3843,7 +3854,7 @@
|
||||
|
||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||
|
||||
"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=="],
|
||||
"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" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
@@ -4221,7 +4232,7 @@
|
||||
|
||||
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
||||
|
||||
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
"jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="],
|
||||
|
||||
"jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="],
|
||||
|
||||
@@ -4793,8 +4804,12 @@
|
||||
|
||||
"openai": ["openai@4.104.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="],
|
||||
|
||||
"openapi-fetch": ["openapi-fetch@0.14.1", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.15" } }, "sha512-l7RarRHxlEZYjMLd/PR0slfMVse2/vvIAGm75/F7J6MlQ8/b9uUQmUF2kCPrQhJqMXSxmYWObVgeYXbFYzZR+A=="],
|
||||
|
||||
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
|
||||
|
||||
"openapi-typescript-helpers": ["openapi-typescript-helpers@0.0.15", "", {}, "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw=="],
|
||||
|
||||
"openid-client": ["openid-client@6.8.2", "", { "dependencies": { "jose": "^6.1.3", "oauth4webapi": "^3.8.4" } }, "sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA=="],
|
||||
|
||||
"opentracing": ["opentracing@0.14.7", "", {}, "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q=="],
|
||||
@@ -4887,7 +4902,7 @@
|
||||
|
||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||
|
||||
"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=="],
|
||||
"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=="],
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="],
|
||||
|
||||
@@ -4947,6 +4962,8 @@
|
||||
|
||||
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||
|
||||
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
||||
|
||||
"playwright": ["playwright@1.60.0", "", { "dependencies": { "playwright-core": "1.60.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA=="],
|
||||
|
||||
"playwright-core": ["playwright-core@1.60.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA=="],
|
||||
@@ -5863,6 +5880,10 @@
|
||||
|
||||
"vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="],
|
||||
|
||||
"vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
|
||||
|
||||
"vscode-languageserver-types": ["vscode-languageserver-types@3.18.0", "", {}, "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g=="],
|
||||
|
||||
"vscode-oniguruma": ["vscode-oniguruma@2.0.1", "", {}, "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="],
|
||||
|
||||
"vscode-textmate": ["vscode-textmate@9.3.2", "", {}, "sha512-n2uGbUcrjhUEBH16uGA0TvUfhWwliFZ1e3+pTjrkim1Mt7ydB41lV08aUvsi70OlzDWp6X7Bx3w/x3fAXIsN0Q=="],
|
||||
@@ -6261,10 +6282,6 @@
|
||||
|
||||
"@inquirer/external-editor/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@isaacs/cliui/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=="],
|
||||
|
||||
"@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
@@ -6715,6 +6732,8 @@
|
||||
|
||||
"@sentry/browser/@sentry/core": ["@sentry/core@10.53.1", "", {}, "sha512-XG4ezlkyuAPjBC5+9kXC94rXXuqYTw9NRhfaDHssbTFaGnqBR8vQX2UUgZfY7ucbeelRDGfBu1sywoU+mB04uA=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/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=="],
|
||||
|
||||
"@sentry/cli/proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||
|
||||
"@sentry/node/@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.7.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OPFBYuXEn1E4ja3Y6eeA7O+ZnLBNcXTV5Cgsn1VaqBZ6hC5FnpZPLBNme1LJY8ZtF4aOujPKFoeWN4ik487KuQ=="],
|
||||
@@ -7283,8 +7302,6 @@
|
||||
|
||||
"glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||
|
||||
"globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
@@ -7371,6 +7388,8 @@
|
||||
|
||||
"mocha/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"mocha/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=="],
|
||||
|
||||
"mocha/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"mocha/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||
@@ -7447,8 +7466,6 @@
|
||||
|
||||
"parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
|
||||
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"pino/pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
|
||||
@@ -7515,8 +7532,6 @@
|
||||
|
||||
"react-email/commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="],
|
||||
|
||||
"react-email/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" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
||||
|
||||
"react-email/log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="],
|
||||
|
||||
"react-email/next": ["next@15.5.18", "", { "dependencies": { "@next/env": "15.5.18", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.18", "@next/swc-darwin-x64": "15.5.18", "@next/swc-linux-arm64-gnu": "15.5.18", "@next/swc-linux-arm64-musl": "15.5.18", "@next/swc-linux-x64-gnu": "15.5.18", "@next/swc-linux-x64-musl": "15.5.18", "@next/swc-win32-arm64-msvc": "15.5.18", "@next/swc-win32-x64-msvc": "15.5.18", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ=="],
|
||||
@@ -7627,8 +7642,6 @@
|
||||
|
||||
"sync-content/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
||||
|
||||
"sync-content/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=="],
|
||||
|
||||
"tempy/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
|
||||
|
||||
"tempy/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
|
||||
@@ -7995,8 +8008,6 @@
|
||||
|
||||
"@inquirer/core/wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"@langchain/langgraph-sdk/p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="],
|
||||
|
||||
"@mintlify/cli/ink/@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.5", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw=="],
|
||||
@@ -8407,6 +8418,14 @@
|
||||
|
||||
"@react-grab/cli/ora/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/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=="],
|
||||
|
||||
"@sentry/node-core/import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="],
|
||||
|
||||
"@sentry/node/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg=="],
|
||||
@@ -8821,8 +8840,6 @@
|
||||
|
||||
"gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||
|
||||
"got/decompress-response/mimic-response": ["mimic-response@4.0.0", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="],
|
||||
|
||||
"gradient-string/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
@@ -8893,6 +8910,12 @@
|
||||
|
||||
"mocha/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"mocha/glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"mocha/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"mocha/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=="],
|
||||
|
||||
"mocha/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"mocha/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||
@@ -9011,12 +9034,6 @@
|
||||
|
||||
"react-email/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"react-email/glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"react-email/glob/jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="],
|
||||
|
||||
"react-email/glob/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=="],
|
||||
|
||||
"react-email/log-symbols/is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
|
||||
|
||||
"react-email/next/@next/env": ["@next/env@15.5.18", "", {}, "sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g=="],
|
||||
@@ -9053,12 +9070,8 @@
|
||||
|
||||
"read-pkg/normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
|
||||
|
||||
"resolve-import/glob/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=="],
|
||||
|
||||
"restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"rimraf/glob/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=="],
|
||||
|
||||
"run-jxa/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
|
||||
|
||||
"run-jxa/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
|
||||
@@ -9439,6 +9452,12 @@
|
||||
|
||||
"@react-grab/cli/ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/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=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"@sentry/node/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@3.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA=="],
|
||||
@@ -9587,8 +9606,6 @@
|
||||
|
||||
"find-cache-dir/pkg-dir/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
||||
|
||||
"glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"gradient-string/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"ink-confirm-input/ink-text-input/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
@@ -9639,6 +9656,10 @@
|
||||
|
||||
"meow/read-pkg-up/read-pkg/normalize-package-data": ["normalize-package-data@3.0.3", "", { "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" } }, "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA=="],
|
||||
|
||||
"mocha/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=="],
|
||||
|
||||
"mocha/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"mocha/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"msw/@inquirer/confirm/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.5", "", {}, "sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw=="],
|
||||
@@ -9693,8 +9714,6 @@
|
||||
|
||||
"puppeteer/puppeteer-core/chromium-bidi/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
|
||||
|
||||
"react-email/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="],
|
||||
|
||||
"react-email/next/postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||
|
||||
"react-email/ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
||||
@@ -9777,6 +9796,12 @@
|
||||
|
||||
"@react-grab/cli/ora/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/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=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/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=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="],
|
||||
|
||||
"@smithy/smithy-client/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="],
|
||||
@@ -9817,6 +9842,10 @@
|
||||
|
||||
"meow/read-pkg-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||
|
||||
"mocha/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=="],
|
||||
|
||||
"mocha/glob/jackspeak/@isaacs/cliui/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=="],
|
||||
|
||||
"ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"ora/log-symbols/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
@@ -9873,6 +9902,8 @@
|
||||
|
||||
"@mintlify/common/sucrase/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"atmn/eslint-plugin-react-hooks/eslint/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"atmn/eslint-plugin-react-hooks/eslint/file-entry-cache/flat-cache/keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
@@ -9887,6 +9918,8 @@
|
||||
|
||||
"meow/read-pkg-up/read-pkg/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||
|
||||
"mocha/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"ora/log-symbols/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"trigger.dev/c12/giget/tar/minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
Reference in New Issue
Block a user