<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Interactive init workflow (create, link-config, link-cloud) with safe
non-interactive behavior; writes/updates project config and .env, and
prints STACK AUTH setup instructions.
  * CLI assistant/agent with a progress UI for long-running tasks.
* Backend AI proxy endpoint that validates and forwards AI requests to
an external provider.

* **Tests**
* End-to-end tests covering all init modes, outputs, env linking, and
error cases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
BilalG1 2026-03-13 10:55:22 -07:00 committed by GitHub
parent 8e03677aad
commit f016cd8993
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1146 additions and 61 deletions

View File

@ -0,0 +1,68 @@
import { handleApiRequest } from "@/route-handlers/smart-route-handler";
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
import { NextRequest } from "next/server";
const OPENROUTER_BASE_URL = "https://openrouter.ai/api";
const OPENROUTER_MODEL = "anthropic/claude-sonnet-4.6";
function sanitizeBody(raw: ArrayBuffer): Uint8Array {
const text = new TextDecoder().decode(raw);
let parsed;
try {
parsed = JSON.parse(text);
} catch {
throw new StatusError(400, "Request body must be valid JSON");
}
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
throw new StatusError(400, "Request body must be a JSON object");
}
parsed.model = OPENROUTER_MODEL;
// OpenRouter limits metadata.user_id to 128 characters
if (parsed.metadata?.user_id && parsed.metadata.user_id.length > 128) {
parsed.metadata.user_id = parsed.metadata.user_id.slice(0, 128);
}
return new TextEncoder().encode(JSON.stringify(parsed));
}
async function proxyToOpenRouter(req: NextRequest, options: { params: Promise<{ path?: string[] }> }) {
const apiKey = getEnvVariable("STACK_OPENROUTER_API_KEY");
const params = await options.params;
const subpath = params.path?.join("/") ?? "";
const targetUrl = `${OPENROUTER_BASE_URL}/${subpath}${req.nextUrl.search}`;
const headers: Record<string, string> = {
"Authorization": `Bearer ${apiKey}`,
"anthropic-version": "2023-06-01",
};
const contentType = req.headers.get("Content-Type");
if (contentType) {
headers["Content-Type"] = contentType;
}
const body = req.method !== "GET" && req.method !== "HEAD"
? Buffer.from(sanitizeBody(await req.arrayBuffer()))
: undefined;
const response = await fetch(targetUrl, {
method: req.method,
headers,
body,
});
return new Response(response.body, {
status: response.status,
headers: {
"Content-Type": response.headers.get("Content-Type") ?? "application/json",
"Cache-Control": "no-cache",
},
});
}
export const GET = handleApiRequest(proxyToOpenRouter);
export const POST = handleApiRequest(proxyToOpenRouter);

View File

@ -345,4 +345,119 @@ describe("Stack CLI", () => {
expect(exitCode).toBe(1);
expect(stderr).toContain("plain `config` object");
});
// --- init command tests ---
it("init create writes stack.config.ts with selected apps", async ({ expect }) => {
const initDir = path.join(tmpDir, "init-create");
fs.mkdirSync(initDir, { recursive: true });
const { stdout, exitCode } = await runCli([
"init", "--mode", "create", "--apps", "authentication,teams", "--output-dir", initDir,
]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Config file written to");
const content = fs.readFileSync(path.join(initDir, "stack.config.ts"), "utf-8");
expect(content).toContain("export const config");
const configMatch = content.match(/export const config = (.+);/s);
expect(configMatch).toBeTruthy();
const parsed = JSON.parse(configMatch![1]);
expect(parsed.apps.installed.authentication).toEqual({ enabled: true });
expect(parsed.apps.installed.teams).toEqual({ enabled: true });
});
it("init create with single app", async ({ expect }) => {
const initDir = path.join(tmpDir, "init-create-single");
fs.mkdirSync(initDir, { recursive: true });
const { stdout, exitCode } = await runCli([
"init", "--mode", "create", "--apps", "authentication", "--output-dir", initDir,
]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Config file written to");
const content = fs.readFileSync(path.join(initDir, "stack.config.ts"), "utf-8");
const configMatch = content.match(/export const config = (.+);/s);
const parsed = JSON.parse(configMatch![1]);
expect(Object.keys(parsed.apps.installed)).toEqual(["authentication"]);
});
it("init link-config with valid path", async ({ expect }) => {
// Create a dummy config file to link to
const dummyConfig = path.join(tmpDir, "dummy-stack.config.ts");
fs.writeFileSync(dummyConfig, "export const config = {};\n");
const { stdout, exitCode } = await runCli([
"init", "--mode", "link-config", "--config-file", dummyConfig,
]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Linked to config file");
expect(stdout).toContain(dummyConfig);
});
it("init link-config with invalid path fails", async ({ expect }) => {
const { stderr, exitCode } = await runCli([
"init", "--mode", "link-config", "--config-file", "/nonexistent/stack.config.ts",
]);
expect(exitCode).toBe(1);
expect(stderr).toContain("File not found");
});
it("init link-cloud creates .env with API keys", async ({ expect }) => {
expect(createdProjectId).toBeDefined();
const initDir = path.join(tmpDir, "init-cloud");
fs.mkdirSync(initDir, { recursive: true });
const { stdout, exitCode } = await runCli([
"init", "--mode", "link-cloud", "--select-project-id", createdProjectId, "--output-dir", initDir,
]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Created .env with Stack Auth keys");
const envContent = fs.readFileSync(path.join(initDir, ".env"), "utf-8");
expect(envContent).toContain("# Stack Auth");
expect(envContent).toContain(`NEXT_PUBLIC_STACK_PROJECT_ID=${createdProjectId}`);
expect(envContent).toContain("NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=");
expect(envContent).toContain("STACK_SECRET_SERVER_KEY=");
});
it("init link-cloud appends to existing .env", async ({ expect }) => {
expect(createdProjectId).toBeDefined();
const initDir = path.join(tmpDir, "init-cloud-append");
fs.mkdirSync(initDir, { recursive: true });
fs.writeFileSync(path.join(initDir, ".env"), "EXISTING_VAR=hello\n");
const { stdout, exitCode } = await runCli([
"init", "--mode", "link-cloud", "--select-project-id", createdProjectId, "--output-dir", initDir,
]);
expect(exitCode).toBe(0);
expect(stdout).toContain("Appended Stack Auth keys to .env");
const envContent = fs.readFileSync(path.join(initDir, ".env"), "utf-8");
expect(envContent).toContain("EXISTING_VAR=hello");
expect(envContent).toContain("# Stack Auth");
expect(envContent).toContain(`NEXT_PUBLIC_STACK_PROJECT_ID=${createdProjectId}`);
});
it("init link-cloud fails with invalid project ID", async ({ expect }) => {
const { stderr, exitCode } = await runCli([
"init", "--mode", "link-cloud", "--select-project-id", "nonexistent-project-id",
]);
expect(exitCode).toBe(1);
expect(stderr).toContain("not found");
});
it("init outputs setup instructions", async ({ expect }) => {
const initDir = path.join(tmpDir, "init-instructions");
fs.mkdirSync(initDir, { recursive: true });
const { stdout, exitCode } = await runCli([
"init", "--mode", "create", "--apps", "authentication", "--output-dir", initDir,
]);
expect(exitCode).toBe(0);
expect(stdout).toContain("STACK AUTH SETUP INSTRUCTIONS");
});
});

View File

@ -26,7 +26,10 @@
"author": "",
"license": "MIT",
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.73",
"@inquirer/prompts": "^7.0.0",
"@stackframe/js": "workspace:*",
"@stackframe/stack-shared": "workspace:*",
"commander": "^13.1.0",
"jiti": "^2.4.2"
},

View File

@ -1,16 +1,299 @@
import { Command } from "commander";
import { execFileSync } from "child_process";
import { select, input, checkbox, confirm } from "@inquirer/prompts";
import * as fs from "fs";
import * as path from "path";
import { StackClientApp } from "@stackframe/js";
import { ALL_APPS } from "@stackframe/stack-shared/dist/apps/apps-config";
import { resolveLoginConfig, resolveSessionAuth, DEFAULT_PUBLISHABLE_CLIENT_KEY } from "../lib/auth.js";
import { getInternalUser } from "../lib/app.js";
import { writeConfigValue } from "../lib/config.js";
import { CliError, AuthError } from "../lib/errors.js";
import { isNonInteractiveEnv } from "../lib/interactive.js";
import { createInitPrompt } from "../lib/init-prompt.js";
import { runClaudeAgent } from "../lib/claude-agent.js";
type InitOptions = {
mode?: "create" | "link-config" | "link-cloud",
apps?: string,
configFile?: string,
selectProjectId?: string,
outputDir?: string,
agent?: boolean,
};
export function registerInitCommand(program: Command) {
program
.command("init")
.description("Initialize Stack Auth in your project (delegates to @stackframe/init-stack)")
.allowUnknownOption(true)
.helpOption(false)
.action((_opts, cmd) => {
const args = cmd.args as string[];
execFileSync("npx", ["@stackframe/init-stack@latest", ...args], {
stdio: "inherit",
});
.description("Initialize Stack Auth in your project")
.option("--mode <mode>", "Mode: create, link-config, or link-cloud (skips interactive prompts)")
.option("--apps <apps>", "Comma-separated app IDs to enable (for create mode)")
.option("--config-file <path>", "Path to existing config file (for link-config mode)")
.option("--select-project-id <id>", "Project ID to link (for link-cloud mode)")
.option("--output-dir <dir>", "Directory to write output files (defaults to cwd)")
.option("--no-agent", "Skip Claude agent and print setup instructions instead")
.action(async (opts: InitOptions) => {
const hasFlags = opts.mode != null;
if (!hasFlags && isNonInteractiveEnv()) {
throw new CliError("stack init requires an interactive terminal. Use --mode flag for non-interactive usage.");
}
try {
await runInit(program, opts);
} catch (error: unknown) {
if (error != null && typeof error === "object" && "name" in error && error.name === "ExitPromptError") {
console.log("\nAborted.");
process.exit(0);
}
throw error;
}
});
}
async function runInit(program: Command, opts: InitOptions) {
const flags = program.opts();
const outputDir = opts.outputDir ? path.resolve(opts.outputDir) : process.cwd();
console.log("Welcome to Stack Auth!\n");
const mode: string = opts.mode ?? await select({
message: "Would you like to link to an existing project, or create a new one?",
choices: [
{ name: "Create a new project (local emulator)", value: "create" as const },
{ name: "Link an existing project", value: "link" as const },
],
});
let configPath: string | undefined;
if (mode === "link" || mode === "link-config" || mode === "link-cloud") {
const result = await handleLink(flags, opts, outputDir);
configPath = result.configPath;
} else if (mode === "create") {
const result = await handleCreate(opts, outputDir);
configPath = result.configPath;
} else {
throw new CliError(`Unknown mode: ${mode}`);
}
const initPrompt = createInitPrompt(false, configPath);
const useAgent = opts.agent !== false && !isNonInteractiveEnv();
if (useAgent) {
const success = await runClaudeAgent({
prompt: `Execute ALL of the following setup steps in my project now. Do not ask questions — just detect the framework and package manager from existing files and proceed.\n\n${initPrompt}`,
cwd: outputDir,
});
if (!success) {
console.log("\nFalling back to manual instructions:\n");
console.log(initPrompt);
}
} else {
console.log("\n" + initPrompt);
}
}
async function handleLink(flags: Record<string, unknown>, opts: InitOptions, outputDir: string): Promise<{ configPath?: string }> {
let source: "config-file" | "cloud";
if (opts.mode === "link-config") {
source = "config-file";
} else if (opts.mode === "link-cloud") {
source = "cloud";
} else {
source = await select({
message: "How would you like to link your project?",
choices: [
{ name: "Link from config file", value: "config-file" as const },
{ name: "Link from app.stack-auth.com", value: "cloud" as const },
],
});
}
if (source === "config-file") {
return await handleLinkFromConfigFile(opts);
}
return await handleLinkFromCloud(flags, opts, outputDir);
}
async function handleLinkFromConfigFile(opts: InitOptions): Promise<{ configPath: string }> {
const filePath = opts.configFile ?? await input({
message: "Path to your existing stack.config.ts:",
validate: (value) => {
const resolved = path.resolve(value);
if (!fs.existsSync(resolved)) {
return `File not found: ${resolved}`;
}
return true;
},
});
const configPath = path.resolve(filePath);
if (!fs.existsSync(configPath)) {
throw new CliError(`File not found: ${configPath}`);
}
console.log(`\nLinked to config file: ${configPath}`);
return { configPath };
}
async function handleLinkFromCloud(flags: Record<string, unknown>, opts: InitOptions, outputDir: string): Promise<{ configPath?: string }> {
let sessionAuth;
try {
sessionAuth = resolveSessionAuth(flags as { projectId?: string });
} catch (e) {
if (e instanceof AuthError) {
if (isNonInteractiveEnv()) {
throw new CliError("Not logged in. Run `stack login` first or set STACK_CLI_REFRESH_TOKEN.");
}
console.log("You need to log in first.\n");
await performLogin(flags);
sessionAuth = resolveSessionAuth(flags as { projectId?: string });
} else {
throw e;
}
}
const user = await getInternalUser(sessionAuth);
const projects = await user.listOwnedProjects();
if (projects.length === 0) {
throw new CliError("You don't own any projects. Create one at app.stack-auth.com first.");
}
let projectId: string;
if (opts.selectProjectId) {
const found = projects.find((p) => p.id === opts.selectProjectId);
if (!found) {
throw new CliError(`Project '${opts.selectProjectId}' not found among your owned projects.`);
}
projectId = opts.selectProjectId;
} else {
projectId = await select({
message: "Select a project:",
choices: projects.map((p) => ({
name: `${p.displayName} (${p.id})`,
value: p.id,
})),
});
}
const project = projects.find((p) => p.id === projectId)!;
const apiKey = await project.app.createInternalApiKey({
description: "Created by CLI init script",
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 200), // 200 years
hasPublishableClientKey: true,
hasSecretServerKey: true,
hasSuperSecretAdminKey: false,
});
const envLines = [
"# Stack Auth",
`NEXT_PUBLIC_STACK_PROJECT_ID=${projectId}`,
`NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=${apiKey.publishableClientKey ?? ""}`,
`STACK_SECRET_SERVER_KEY=${apiKey.secretServerKey ?? ""}`,
].join("\n");
const envPath = path.resolve(outputDir, ".env");
if (fs.existsSync(envPath)) {
const existing = fs.readFileSync(envPath, "utf-8");
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
if (isNonInteractiveEnv()) {
fs.appendFileSync(envPath, separator + envLines + "\n");
console.log("\nAppended Stack Auth keys to .env");
} else {
const shouldAppend = await confirm({
message: `.env file already exists. Append Stack Auth keys?`,
default: true,
});
if (shouldAppend) {
fs.appendFileSync(envPath, separator + envLines + "\n");
console.log("\nAppended Stack Auth keys to .env");
} else {
console.log("\nHere are your environment variables:\n");
console.log(envLines);
}
}
} else {
fs.writeFileSync(envPath, envLines + "\n");
console.log("\nCreated .env with Stack Auth keys");
}
return {};
}
async function performLogin(flags: Record<string, unknown>) {
const config = resolveLoginConfig(flags as { projectId?: string });
const app = new StackClientApp({
projectId: "internal",
publishableClientKey: DEFAULT_PUBLISHABLE_CLIENT_KEY,
baseUrl: config.apiUrl,
tokenStore: "memory",
noAutomaticPrefetch: true,
});
console.log("Waiting for browser authentication...");
const result = await app.promptCliLogin({
appUrl: config.dashboardUrl,
});
if (result.status === "error") {
throw new CliError(`Login failed: ${result.error.message}`);
}
writeConfigValue("STACK_CLI_REFRESH_TOKEN", result.data);
console.log("Login successful!\n");
}
async function handleCreate(opts: InitOptions, outputDir: string): Promise<{ configPath: string }> {
const configPath = path.resolve(outputDir, "stack.config.ts");
console.log(`\nCreating a new config file at ${configPath}!\n`);
let selectedApps: string[];
if (opts.apps) {
selectedApps = opts.apps.split(",").map((s) => s.trim()).filter(Boolean);
const validAppIds = Object.keys(ALL_APPS);
const invalidApps = selectedApps.filter((id) => !validAppIds.includes(id));
if (invalidApps.length > 0) {
throw new CliError(`Unknown app IDs: ${invalidApps.join(", ")}. Valid IDs: ${validAppIds.join(", ")}`);
}
} else {
const stageOrder = { stable: 0, beta: 1 } as const;
const appEntries = Object.entries(ALL_APPS)
.filter(([, app]) => app.stage !== "alpha")
.sort((a, b) => stageOrder[a[1].stage as keyof typeof stageOrder] - stageOrder[b[1].stage as keyof typeof stageOrder]);
selectedApps = await checkbox({
message: "Select apps to enable:",
choices: appEntries.map(([id, app]) => ({
name: `${app.displayName} - ${app.subtitle}${app.stage !== "stable" ? ` (${app.stage})` : ""}`,
value: id,
checked: id === "authentication",
})),
});
}
const installed = Object.fromEntries(
selectedApps.map((appId) => [appId, { enabled: true }])
);
const config = {
apps: {
installed,
},
};
const content = `export const config = ${JSON.stringify(config, null, 2)};\n`;
fs.mkdirSync(path.dirname(configPath), { recursive: true });
fs.writeFileSync(configPath, content);
console.log(`\nConfig file written to ${configPath}`);
return { configPath };
}

View File

@ -0,0 +1,207 @@
import { query } from "@anthropic-ai/claude-agent-sdk";
const DEFAULT_PROXY_URL = "https://api.stack-auth.com/api/v1/integrations/ai-proxy";
const ANTHROPIC_PROXY_BASE_URL: string = process.env.STACK_CLAUDE_PROXY_URL ?? DEFAULT_PROXY_URL;
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
class AgentProgressUI {
private mainLabel: string;
private spinnerFrame = 0;
private spinnerTimer: ReturnType<typeof setInterval> | null = null;
private activeSpinners = new Map<string, string>(); // id -> label
private flushedCount = 0; // number of completed items already printed above the spinner area
private pendingCompleted: string[] = []; // completed items not yet flushed
private lastLineCount = 0;
constructor(mainLabel: string) {
this.mainLabel = mainLabel;
}
start() {
this.spinnerTimer = setInterval(() => {
this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
this.render();
}, 80);
this.render();
}
stop(success: boolean) {
if (this.spinnerTimer) {
clearInterval(this.spinnerTimer);
this.spinnerTimer = null;
}
this.completeAllActive();
this.clearLines();
const icon = success ? "\x1b[32m✔\x1b[0m" : "\x1b[31m✖\x1b[0m";
// Re-print header + all completed items as final output
console.log(`${icon} ${this.mainLabel}`);
for (const label of this.pendingCompleted) {
console.log(` \x1b[32m✔\x1b[0m ${label}`);
}
this.pendingCompleted = [];
}
setSpinner(id: string, label: string) {
this.activeSpinners.set(id, label);
}
complete(id: string, label?: string) {
const existing = this.activeSpinners.get(id);
this.activeSpinners.delete(id);
const finalLabel = label ?? existing;
if (finalLabel) {
this.pendingCompleted.push(finalLabel);
}
}
completeAllActive() {
for (const label of this.activeSpinners.values()) {
this.pendingCompleted.push(label);
}
this.activeSpinners.clear();
}
private clearLines() {
if (this.lastLineCount > 0) {
process.stdout.write(`\x1b[${this.lastLineCount}A\x1b[J`);
}
}
private flushCompleted() {
if (this.pendingCompleted.length === 0) {
return;
}
// Clear the spinner area, print completed items permanently, then re-render spinner below
this.clearLines();
// Re-print the header line if this is the first flush
if (this.flushedCount === 0) {
const frame = SPINNER_FRAMES[this.spinnerFrame];
process.stdout.write(`\x1b[36m${frame}\x1b[0m ${this.mainLabel}\n`);
}
for (const label of this.pendingCompleted) {
process.stdout.write(` \x1b[32m✔\x1b[0m ${label}\n`);
}
this.flushedCount += this.pendingCompleted.length;
this.pendingCompleted = [];
this.lastLineCount = 0; // reset since we printed permanent lines
}
private render() {
this.flushCompleted();
this.clearLines();
const frame = SPINNER_FRAMES[this.spinnerFrame];
const lines: string[] = [];
// Only show header in spinner area if nothing has been flushed yet
if (this.flushedCount === 0) {
lines.push(`\x1b[36m${frame}\x1b[0m ${this.mainLabel}`);
}
for (const label of this.activeSpinners.values()) {
lines.push(` \x1b[36m${frame}\x1b[0m ${label}`);
}
if (lines.length > 0) {
const output = lines.join("\n") + "\n";
process.stdout.write(output);
}
this.lastLineCount = lines.length;
}
}
function getToolLabel(toolName: string, input: Record<string, unknown>): string {
switch (toolName) {
case "Read": {
return `Reading ${input.file_path ?? "file"}`;
}
case "Write": {
return `Writing ${input.file_path ?? "file"}`;
}
case "Edit": {
return `Editing ${input.file_path ?? "file"}`;
}
case "Bash": {
return `Running \`${truncate(String(input.command ?? ""), 40)}\``;
}
case "Glob": {
return `Searching for ${input.pattern ?? "files"}`;
}
case "Grep": {
return `Searching for "${truncate(String(input.pattern ?? ""), 30)}"`;
}
default: {
return toolName;
}
}
}
function truncate(str: string, maxLen: number): string {
return str.length > maxLen ? str.slice(0, maxLen - 1) + "…" : str;
}
function stripClaudeCodeEnv(): Record<string, string> {
const env = { ...process.env };
delete env.CLAUDECODE;
return env as Record<string, string>;
}
export async function runClaudeAgent(options: {
prompt: string,
cwd: string,
}): Promise<boolean> {
const ui = new AgentProgressUI("Setting up Stack Auth...");
ui.start();
try {
let resultText = "";
for await (const message of query({
prompt: options.prompt,
options: {
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
permissionMode: "dontAsk",
cwd: options.cwd,
// stripClaudeCodeEnv removes CLAUDECODE env var to prevent nested agent detection
env: { ...stripClaudeCodeEnv(), ANTHROPIC_BASE_URL: ANTHROPIC_PROXY_BASE_URL, ANTHROPIC_API_KEY: "" },
stderr: (data: string) => { process.stderr.write(data); },
},
})) {
if ("result" in message) {
resultText = message.result;
} else if (message.type === "assistant" && message.parent_tool_use_id === null) {
// New parent assistant turn — previous tools are done
ui.completeAllActive();
// Register new tool calls from this turn
for (const block of message.message.content) {
if (block.type === "tool_use") {
ui.setSpinner(block.id, getToolLabel(block.name, block.input as Record<string, unknown>));
}
}
} else if (message.type === "system") {
// Subagent task lifecycle
const msg = message as Record<string, unknown>;
const taskId = msg.task_id as string | undefined;
if (msg.subtype === "task_started" && taskId) {
ui.setSpinner(taskId, String(msg.description ?? "Working..."));
} else if (msg.subtype === "task_progress" && taskId) {
ui.setSpinner(taskId, String(msg.description ?? "Working..."));
} else if (msg.subtype === "task_notification" && taskId) {
ui.complete(taskId, String(msg.summary ?? msg.description ?? "Done"));
}
}
}
ui.stop(true);
if (resultText) {
console.log(`\n${resultText}`);
}
return true;
} catch (error) {
ui.stop(false);
console.error("\nClaude agent encountered an error:", error instanceof Error ? error.message : error);
return false;
}
}

View File

@ -0,0 +1,122 @@
// TODO: Use configPath in the prompt once local emulator is set up:
// Add "npx @stackframe/stack-cli emulator run --config-file ${configPath}" to project dev command
export const createInitPrompt = (web: boolean, configPath?: string) => `=============================
STACK AUTH SETUP INSTRUCTIONS
=============================
These instructions describe how to set up Stack Auth.
${web ? `
First of all, if you have access to a terminal, it is better to use the \`npx @stackframe/stack-cli init\`` : ""}
Install mcp server from https://mcp.stack-auth.com if not already installed
For reference, questions, or information on Stack Auth, you can query the docs on https://docs.stack-auth.com via curl or any tools that you have
## Setup
### 1) Install the package
Run the install command using whatever package manager the project uses (npm, yarn, pnpm, bun):
| Framework | Package |
|-----------|---------|
| Next.js | \`@stackframe/stack\` |
| React | \`@stackframe/react\` |
| Vanilla JS | \`@stackframe/js\` |
### 2) Create the Stack apps
Depending on whether you're on a client or a server, you will want to create stackClientApp or stackServerApp. Some environments, like Next.js, have both, so create both files.
The stack client app has client-level permissions. It contains most of the useful methods and hooks for your client-side code.
The stack server app has full read and write access to all users. It requires STACK_SECRET_SERVER_KEY env variable and should only be used in secure context
In Next.js, env vars are auto-detected (NEXT_PUBLIC_STACK_PROJECT_ID etc.), so the constructor needs no explicit config. For other frameworks, you must pass projectId and publishableClientKey explicitly using the framework's env var access method.
The tokenStore should be "nextjs-cookie" for Next.js, or "cookie" for all other frameworks.
\`\`\`ts
// src/stack/client.ts
import { StackClientApp } from "@stackframe/stack"; // or "@stackframe/react" or "@stackframe/js"
export const stackClientApp = new StackClientApp({
// Next.js: omit projectId/publishableClientKey (auto-detected from NEXT_PUBLIC_ env vars)
// Other frameworks: pass explicitly, e.g. for Vite:
// projectId: import.meta.env.VITE_STACK_PROJECT_ID,
// publishableClientKey: import.meta.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY,
tokenStore: "nextjs-cookie", // or "cookie" for non-Next.js
});
\`\`\`
If the framework has server-side support (e.g. Next.js), also create a server app:
\`\`\`ts
// src/stack/server.ts
import "server-only";
import { StackServerApp } from "@stackframe/stack";
import { stackClientApp } from "./client";
export const stackServerApp = new StackServerApp({
inheritsFrom: stackClientApp,
});
\`\`\`
### 3) Create the Stack handler (if available in framework)
This sets up pages for sign in, sign up, password reset, etc.
\`\`\`tsx
import { StackHandler } from "@stackframe/stack"; // Next.js
// import { StackHandler } from "@stackframe/react"; // React
export default function Handler() {
return <StackHandler fullPage />;
}
\`\`\`
### 4) Create a Suspense boundary
Suspense is necessary for many stack auth hooks such as useUser to function. Add a loading component with a custom loading indicator for the current project. Don't add if one already exists
For example:
\`\`\`tsx
//src/loading.tsx
export default function Loading() {
return <p>Loading...</p>
}
\`\`\`
### 5) Link environment variables
This is only necessary if not using local emulator. Ensure these are ignored by git.
Rename the env var keys in .env to match the framework's convention for client-exposed variables. For example, Vite requires VITE_ prefix, Next.js uses NEXT_PUBLIC_, etc. The values should stay the same only rename the keys.
The required variables are:
- Project ID (e.g. NEXT_PUBLIC_STACK_PROJECT_ID, VITE_STACK_PROJECT_ID, etc.)
- Publishable client key (e.g. NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY, VITE_STACK_PUBLISHABLE_CLIENT_KEY, etc.)
- Secret server key: STACK_SECRET_SERVER_KEY (only for frameworks with server-side support, no prefix needed)
### 6) React only: Wrap the entire page in a Stack provider
This is used for the useUser and useStackApp hooks.
\`\`\`tsx
import { StackProvider, StackTheme } from "@stackframe/stack";
import { stackClientApp } from "../stack/client"; // adjust relative path
\`\`\`
Then wrap the body content:
\`\`\`tsx
return (
<body>
<StackProvider app={stackClientApp}>
<StackTheme>{children}</StackTheme>
</StackProvider>
</body>
);
\`\`\`
`;

View File

@ -6,6 +6,7 @@ const config: UserConfig = {
clean: false,
dts: true,
outDir: 'dist',
external: ['@anthropic-ai/claude-agent-sdk'],
format: {
esm: {
outExtensions: () => ({ js: '.js', dts: '.d.ts' }),

View File

@ -722,7 +722,7 @@ importers:
version: 1.163.2(crossws@0.4.4(srvx@0.8.16))
nitro:
specifier: ^3.0.0
version: 3.0.0(@electric-sql/pglite@0.3.2)(chokidar@4.0.3)(lru-cache@11.2.2)(mysql2@3.15.3)(rolldown@1.0.0-rc.3)(vite@7.3.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.0))
version: 3.0.0(@electric-sql/pglite@0.3.2)(chokidar@4.0.3)(lru-cache@11.2.2)(mysql2@3.15.3)(vite@7.3.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.0))
react:
specifier: 19.2.1
version: 19.2.1
@ -1471,10 +1471,10 @@ importers:
version: link:../../packages/stack
'@supabase/ssr':
specifier: latest
version: 0.9.0(@supabase/supabase-js@2.98.0)
version: 0.9.0(@supabase/supabase-js@2.99.0)
'@supabase/supabase-js':
specifier: latest
version: 2.98.0
version: 2.99.0
jose:
specifier: ^5.2.2
version: 5.6.3
@ -1906,9 +1906,18 @@ importers:
packages/stack-cli:
dependencies:
'@anthropic-ai/claude-agent-sdk':
specifier: ^0.2.73
version: 0.2.73(zod@4.1.12)
'@inquirer/prompts':
specifier: ^7.0.0
version: 7.10.1(@types/node@20.17.6)
'@stackframe/js':
specifier: workspace:*
version: link:../js
'@stackframe/stack-shared':
specifier: workspace:*
version: link:../stack-shared
commander:
specifier: ^13.1.0
version: 13.1.0
@ -2424,6 +2433,12 @@ packages:
'@antfu/utils@8.1.1':
resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
'@anthropic-ai/claude-agent-sdk@0.2.73':
resolution: {integrity: sha512-JrHeMl93Q5ai9GMPAffQkSisbbDvD1skU2x6sf6WRzEZw0sK6aTG+XSiZHY2F5aSrfd4G2qUogLHEm6Y8obyOQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
zod: ^4.0.0
'@apidevtools/json-schema-ref-parser@11.9.3':
resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==}
engines: {node: '>= 16'}
@ -4763,10 +4778,144 @@ packages:
cpu: [x64]
os: [win32]
'@inquirer/ansi@1.0.2':
resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==}
engines: {node: '>=18'}
'@inquirer/checkbox@4.3.2':
resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/confirm@5.1.21':
resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/core@10.3.2':
resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/editor@4.2.23':
resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/expand@4.0.23':
resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/external-editor@1.0.3':
resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/figures@1.0.15':
resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==}
engines: {node: '>=18'}
'@inquirer/figures@1.0.3':
resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==}
engines: {node: '>=18'}
'@inquirer/input@4.3.1':
resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/number@3.0.23':
resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/password@4.0.23':
resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/prompts@7.10.1':
resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/rawlist@4.1.11':
resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/search@3.2.2':
resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/select@4.4.2':
resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@inquirer/type@3.0.10':
resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
peerDependenciesMeta:
'@types/node':
optional: true
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@ -8386,20 +8535,20 @@ packages:
resolution: {integrity: sha512-SXuhqhuR5FXaYgKTXzZJeqtVA6JKb9IZWaGeEUxHHiOcFy2p51wccO72bYpXwoK4D5pzQOIYLTuAc7etxyMmwg==}
engines: {node: '>=12.16'}
'@supabase/auth-js@2.98.0':
resolution: {integrity: sha512-GBH361T0peHU91AQNzOlIrjUZw9TZbB9YDRiyFgk/3Kvr3/Z1NWUZ2athWTfHhwNNi8IrW00foyFxQD9IO/Trg==}
'@supabase/auth-js@2.99.0':
resolution: {integrity: sha512-tHiIST/OEoLmWBE+3X69xRY5srJM/lL86KltmMlIfDo9ePJLo14vQQV9T4NF+P+MoGhCwQL1GTmk51zuAFMXKw==}
engines: {node: '>=20.0.0'}
'@supabase/functions-js@2.98.0':
resolution: {integrity: sha512-N/xEyiNU5Org+d+PNCpv+TWniAXRzxIURxDYsS/m2I/sfAB/HcM9aM2Dmf5edj5oWb9GxID1OBaZ8NMmPXL+Lg==}
'@supabase/functions-js@2.99.0':
resolution: {integrity: sha512-zA9oad6EqGwMLLu2LfP1bXbqKcJGiotAdbdTfZG7YS7619YZQAEgejj9mp+E5vglKE1yMWbKK+S1J3PbuUtgLg==}
engines: {node: '>=20.0.0'}
'@supabase/postgrest-js@2.98.0':
resolution: {integrity: sha512-v6e9WeZuJijzUut8HyXu6gMqWFepIbaeaMIm1uKzei4yLg9bC9OtEW9O14LE/9ezqNbSAnSLO5GtOLFdm7Bpkg==}
'@supabase/postgrest-js@2.99.0':
resolution: {integrity: sha512-8qfOMi2pu9y0IQhUAeFqjrvR49G4ELGevXCWV9qAHXFQ/h2FFh0I8PYjFQj4rHcHSq6hrpozDnS1vbQU8NAQ/A==}
engines: {node: '>=20.0.0'}
'@supabase/realtime-js@2.98.0':
resolution: {integrity: sha512-rOWt28uGyFipWOSd+n0WVMr9kUXiWaa7J4hvyLCIHjRFqWm1z9CaaKAoYyfYMC1Exn3WT8WePCgiVhlAtWC2yw==}
'@supabase/realtime-js@2.99.0':
resolution: {integrity: sha512-7nFTZhNeANR7FvEY6PfWLCfE8dHqcaJd9SuR7IPEZvBPG9K4uEHMivpjZx4NWRSU7Eji7ZbKy2LG+cJ48DhwHg==}
engines: {node: '>=20.0.0'}
'@supabase/ssr@0.9.0':
@ -8407,12 +8556,12 @@ packages:
peerDependencies:
'@supabase/supabase-js': ^2.97.0
'@supabase/storage-js@2.98.0':
resolution: {integrity: sha512-tzr2mG+v7ILSAZSfZMSL9OPyIH4z1ikgQ8EcQTKfMRz4EwmlFt3UnJaGzSOxyvF5b+fc9So7qdSUWTqGgeLokQ==}
'@supabase/storage-js@2.99.0':
resolution: {integrity: sha512-mAEEbfsght5EEALejYrwAP9k8sFBGjfMZT8n4SyMXk2iYuWVeRMs1kA/uKg0uDMctWdZ0bL+L4jZzksUJpCjMA==}
engines: {node: '>=20.0.0'}
'@supabase/supabase-js@2.98.0':
resolution: {integrity: sha512-Ohc97CtInLwZyiSASz7tT9/Abm/vqnIbO9REp+PivVUII8UZsuI3bngRQnYgJdFoOIwvaEII1fX1qy8x0CyNiw==}
'@supabase/supabase-js@2.99.0':
resolution: {integrity: sha512-SP9Sn9tsHDB7N4u2gT13rdeZJewE4xibAxasG7vOz+fYi92+XkMMbWNx0uGK53zKTnAnvTs16isRooyBy4sn5w==}
engines: {node: '>=20.0.0'}
'@swc/counter@0.1.3':
@ -10010,6 +10159,9 @@ packages:
chardet@0.7.0:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
chardet@2.1.1:
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
check-error@1.0.3:
resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
@ -13374,6 +13526,10 @@ packages:
resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
mute-stream@2.0.0:
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
engines: {node: ^18.17.0 || >=20.5.0}
mysql2@3.15.3:
resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==}
engines: {node: '>= 8.0'}
@ -16647,6 +16803,10 @@ packages:
resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==}
engines: {node: '>=12.20'}
yoctocolors-cjs@2.1.3:
resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
engines: {node: '>=18'}
yup@1.7.1:
resolution: {integrity: sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==}
@ -16806,6 +16966,20 @@ snapshots:
'@antfu/utils@8.1.1': {}
'@anthropic-ai/claude-agent-sdk@0.2.73(zod@4.1.12)':
dependencies:
zod: 4.1.12
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.4
'@img/sharp-darwin-x64': 0.34.4
'@img/sharp-linux-arm': 0.34.4
'@img/sharp-linux-arm64': 0.34.4
'@img/sharp-linux-x64': 0.34.4
'@img/sharp-linuxmusl-arm64': 0.34.4
'@img/sharp-linuxmusl-x64': 0.34.4
'@img/sharp-win32-arm64': 0.34.4
'@img/sharp-win32-x64': 0.34.4
'@apidevtools/json-schema-ref-parser@11.9.3':
dependencies:
'@jsdevtools/ono': 7.1.3
@ -19417,8 +19591,133 @@ snapshots:
'@img/sharp-win32-x64@0.34.4':
optional: true
'@inquirer/ansi@1.0.2': {}
'@inquirer/checkbox@4.3.2(@types/node@20.17.6)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10(@types/node@20.17.6)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/confirm@5.1.21(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/core@10.3.2(@types/node@20.17.6)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10(@types/node@20.17.6)
cli-width: 4.1.0
mute-stream: 2.0.0
signal-exit: 4.1.0
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/editor@4.2.23(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/external-editor': 1.0.3(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/expand@4.0.23(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/external-editor@1.0.3(@types/node@20.17.6)':
dependencies:
chardet: 2.1.1
iconv-lite: 0.7.0
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/figures@1.0.15': {}
'@inquirer/figures@1.0.3': {}
'@inquirer/input@4.3.1(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/number@3.0.23(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/password@4.0.23(@types/node@20.17.6)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/prompts@7.10.1(@types/node@20.17.6)':
dependencies:
'@inquirer/checkbox': 4.3.2(@types/node@20.17.6)
'@inquirer/confirm': 5.1.21(@types/node@20.17.6)
'@inquirer/editor': 4.2.23(@types/node@20.17.6)
'@inquirer/expand': 4.0.23(@types/node@20.17.6)
'@inquirer/input': 4.3.1(@types/node@20.17.6)
'@inquirer/number': 3.0.23(@types/node@20.17.6)
'@inquirer/password': 4.0.23(@types/node@20.17.6)
'@inquirer/rawlist': 4.1.11(@types/node@20.17.6)
'@inquirer/search': 3.2.2(@types/node@20.17.6)
'@inquirer/select': 4.4.2(@types/node@20.17.6)
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/rawlist@4.1.11(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/type': 3.0.10(@types/node@20.17.6)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/search@3.2.2(@types/node@20.17.6)':
dependencies:
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10(@types/node@20.17.6)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/select@4.4.2(@types/node@20.17.6)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2(@types/node@20.17.6)
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10(@types/node@20.17.6)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 20.17.6
'@inquirer/type@3.0.10(@types/node@20.17.6)':
optionalDependencies:
'@types/node': 20.17.6
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@ -24614,19 +24913,19 @@ snapshots:
'@stripe/stripe-js@7.7.0': {}
'@supabase/auth-js@2.98.0':
'@supabase/auth-js@2.99.0':
dependencies:
tslib: 2.8.1
'@supabase/functions-js@2.98.0':
'@supabase/functions-js@2.99.0':
dependencies:
tslib: 2.8.1
'@supabase/postgrest-js@2.98.0':
'@supabase/postgrest-js@2.99.0':
dependencies:
tslib: 2.8.1
'@supabase/realtime-js@2.98.0':
'@supabase/realtime-js@2.99.0':
dependencies:
'@types/phoenix': 1.6.6
'@types/ws': 8.18.1
@ -24636,23 +24935,23 @@ snapshots:
- bufferutil
- utf-8-validate
'@supabase/ssr@0.9.0(@supabase/supabase-js@2.98.0)':
'@supabase/ssr@0.9.0(@supabase/supabase-js@2.99.0)':
dependencies:
'@supabase/supabase-js': 2.98.0
'@supabase/supabase-js': 2.99.0
cookie: 1.0.2
'@supabase/storage-js@2.98.0':
'@supabase/storage-js@2.99.0':
dependencies:
iceberg-js: 0.8.1
tslib: 2.8.1
'@supabase/supabase-js@2.98.0':
'@supabase/supabase-js@2.99.0':
dependencies:
'@supabase/auth-js': 2.98.0
'@supabase/functions-js': 2.98.0
'@supabase/postgrest-js': 2.98.0
'@supabase/realtime-js': 2.98.0
'@supabase/storage-js': 2.98.0
'@supabase/auth-js': 2.99.0
'@supabase/functions-js': 2.99.0
'@supabase/postgrest-js': 2.99.0
'@supabase/realtime-js': 2.99.0
'@supabase/storage-js': 2.99.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@ -26658,6 +26957,8 @@ snapshots:
chardet@0.7.0: {}
chardet@2.1.1: {}
check-error@1.0.3:
dependencies:
get-func-name: 2.0.2
@ -28083,7 +28384,7 @@ snapshots:
debug: 4.4.3
enhanced-resolve: 5.17.1
eslint: 8.57.1
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
fast-glob: 3.3.3
get-tsconfig: 4.8.1
is-bun-module: 1.2.1
@ -28096,25 +28397,6 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
enhanced-resolve: 5.17.1
eslint: 8.57.1
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
fast-glob: 3.3.3
get-tsconfig: 4.8.1
is-bun-module: 1.2.1
is-glob: 4.0.3
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-node
- eslint-import-resolver-webpack
- supports-color
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
@ -28145,7 +28427,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
@ -28223,7 +28505,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@ -31082,6 +31364,8 @@ snapshots:
mute-stream@1.0.0: {}
mute-stream@2.0.0: {}
mysql2@3.15.3:
dependencies:
aws-ssl-profiles: 1.1.2
@ -31424,7 +31708,7 @@ snapshots:
nice-try@1.0.5: {}
nitro@3.0.0(@electric-sql/pglite@0.3.2)(chokidar@4.0.3)(lru-cache@11.2.2)(mysql2@3.15.3)(rolldown@1.0.0-rc.3)(vite@7.3.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.0)):
nitro@3.0.0(@electric-sql/pglite@0.3.2)(chokidar@4.0.3)(lru-cache@11.2.2)(mysql2@3.15.3)(vite@7.3.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.0)):
dependencies:
consola: 3.4.2
cookie-es: 2.0.0
@ -31444,7 +31728,6 @@ snapshots:
unenv: 2.0.0-rc.21
unstorage: 2.0.0-alpha.3(chokidar@4.0.3)(db0@0.3.4(@electric-sql/pglite@0.3.2)(mysql2@3.15.3))(lru-cache@11.2.2)(ofetch@1.5.1)
optionalDependencies:
rolldown: 1.0.0-rc.3
vite: 7.3.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
@ -35142,6 +35425,8 @@ snapshots:
yocto-queue@1.1.1: {}
yoctocolors-cjs@2.1.3: {}
yup@1.7.1:
dependencies:
property-expr: 2.0.6

View File

@ -8,6 +8,7 @@ packages:
minimumReleaseAge: 2880
minimumReleaseAgeExclude:
- '@anthropic-ai/claude-agent-sdk'
- ai
- '@ai-sdk/openai'
- '@ai-sdk/react'