From 5c244b2cc31d5ec833c5535429bd0275c382921e Mon Sep 17 00:00:00 2001 From: ayal Date: Tue, 15 Sep 2026 17:03:16 +0300 Subject: [PATCH 01/73] =?UTF-8?q?feat:=20imported-app=20commands=20?= =?UTF-8?q?=E2=80=94=20create=20(incl.=20--blank),=20chat,=20git,=20previe?= =?UTF-8?q?w,=20pr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit base44 imported create imports a GitHub repo (direct/fork/copy) or starts from scratch (--blank, a fresh private repo via the backend's blank mode), links the directory, and with --prompt waits for the first build. base44 imported chat drives one agent turn over the synchronous /chat/message endpoint; status/commit/discard/pr wrap the imported/git routes (branch-scoped via the global --branch); preview resolves the live sandbox URL. BASE44_FF_OVERRIDE forwards an X-FF-Override header for staging flag testing. Co-Authored-By: Claude Fable 5 --- .../cli/src/cli/commands/imported/chat.ts | 61 +++++ .../cli/src/cli/commands/imported/create.ts | 177 +++++++++++++ packages/cli/src/cli/commands/imported/git.ts | 143 +++++++++++ .../cli/src/cli/commands/imported/index.ts | 24 ++ packages/cli/src/cli/program.ts | 4 + .../cli/src/core/clients/base44-client.ts | 4 + .../cli/src/core/resources/imported/api.ts | 240 ++++++++++++++++++ packages/cli/tests/cli/imported.spec.ts | 194 ++++++++++++++ 8 files changed, 847 insertions(+) create mode 100644 packages/cli/src/cli/commands/imported/chat.ts create mode 100644 packages/cli/src/cli/commands/imported/create.ts create mode 100644 packages/cli/src/cli/commands/imported/git.ts create mode 100644 packages/cli/src/cli/commands/imported/index.ts create mode 100644 packages/cli/src/core/resources/imported/api.ts create mode 100644 packages/cli/tests/cli/imported.spec.ts diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts new file mode 100644 index 000000000..1ef47f99e --- /dev/null +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -0,0 +1,61 @@ +import type { CLIContext, RunCommandResult } from "@/cli/types.js"; +import { Base44Command } from "@/cli/utils/index.js"; +import type { ImportedChatTurn } from "@/core/resources/imported/api.js"; +import { sendImportedChatMessage } from "@/core/resources/imported/api.js"; + +function lastAssistantReply(turn: ImportedChatTurn): string | undefined { + const messages = turn.conversation?.messages ?? []; + for (let i = messages.length - 1; i >= 0; i--) { + const message = messages[i]; + if (message.role !== "assistant") continue; + if (typeof message.content === "string" && message.content.trim()) { + return message.content.trim(); + } + } + return undefined; +} + +async function chatAction( + { log, runTask, jsonMode }: CLIContext, + message: string, +): Promise { + const turn = await runTask("Agent working (a turn can take minutes)", () => + sendImportedChatMessage(message), + ); + + if (turn.queued) { + const note = + "The agent is busy with an earlier message — yours was queued and will run next."; + if (jsonMode) return { stdout: `${JSON.stringify({ queued: true })}\n` }; + return { outroMessage: note }; + } + + const state = turn.status?.state ?? "ready"; + const reply = lastAssistantReply(turn); + if (jsonMode) { + return { + stdout: `${JSON.stringify({ + status: state, + error_source: turn.status?.error_source ?? null, + reply: reply ?? null, + })}\n`, + }; + } + + if (reply) log.message(reply); + if (state === "error") { + return { + outroMessage: `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details.`, + }; + } + return { outroMessage: "Turn finished." }; +} + +export function getImportedChatCommand(): Base44Command { + const command = new Base44Command("chat"); + command + .description("Send a message to the app's agent and wait for the turn") + .argument("", "What you want the agent to do") + .action(chatAction); + return command; +} diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts new file mode 100644 index 000000000..2df384c1f --- /dev/null +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -0,0 +1,177 @@ +import type { CLIContext, RunCommandResult } from "@/cli/types.js"; +import { Base44Command, getDashboardUrl } from "@/cli/utils/index.js"; +import { InvalidInputError } from "@/core/errors.js"; +import { + appConfigExists, + setAppContext, + writeAppConfig, +} from "@/core/project/app-config.js"; +import { + createImportedApp, + getImportedAppState, + getImportedPreviewUrl, +} from "@/core/resources/imported/api.js"; + +const POLL_INTERVAL_MS = 5_000; +const POLL_TIMEOUT_MS = 20 * 60_000; +// The initial turn is scheduled in the background, so "ready" in the first +// moments just means it hasn't started yet. +const MIN_BUILD_MS = 20_000; + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +interface CreateImportedOptions { + blank?: boolean; + repo?: string; + repoName?: string; + mode?: "direct" | "fork" | "copy"; + appName?: string; + fromBranch?: string; + prompt?: string; +} + +async function waitForInitialTurn(appId: string): Promise { + const startedAt = Date.now(); + await sleep(MIN_BUILD_MS); + while (Date.now() - startedAt < POLL_TIMEOUT_MS) { + const { status } = await getImportedAppState(appId); + const state = status?.state ?? "ready"; + if (state !== "processing") return state; + await sleep(POLL_INTERVAL_MS); + } + return "processing"; +} + +async function createImportedAction( + { log, runTask, jsonMode }: CLIContext, + options: CreateImportedOptions, +): Promise { + if (options.blank && options.repo) { + throw new InvalidInputError( + "--blank starts from scratch; drop --repo, or drop --blank to import that repository.", + ); + } + if (options.blank && !options.repoName) { + throw new InvalidInputError( + "--blank needs --repo-name for the fresh GitHub repository.", + ); + } + if (!options.blank && !options.repo) { + throw new InvalidInputError( + "Pass --repo to import a repository, or --blank --repo-name to start from scratch.", + ); + } + if (await appConfigExists(process.cwd())) { + throw new InvalidInputError( + "This directory is already linked to a Base44 app. Run the command from a fresh directory.", + ); + } + + const sourceMode = options.blank ? "blank" : (options.mode ?? "direct"); + const appName = + options.appName ?? + (options.blank + ? (options.repoName as string) + : ((options.repo as string).replace(/\/+$/, "").split("/").pop() ?? + "Imported app")); + + const created = await runTask( + options.blank + ? "Creating your repository and app" + : "Importing the repository", + () => + createImportedApp({ + appName, + sourceMode, + repoUrl: options.repo, + newRepoName: options.repoName, + branch: options.fromBranch, + prompt: options.prompt, + }), + ); + + const configPath = await writeAppConfig(process.cwd(), created.id); + setAppContext({ id: created.id }); + + let finalState: string | undefined; + let previewUrl: string | undefined; + if (options.prompt) { + finalState = await runTask( + "Agent is building (several minutes; safe to Ctrl+C — the build continues)", + () => waitForInitialTurn(created.id), + ); + if (finalState === "ready") { + try { + previewUrl = await runTask("Fetching preview URL", () => + getImportedPreviewUrl(), + ); + } catch { + // Preview may still be booting; the editor shows it when it's up. + } + } + } + + const editorUrl = getDashboardUrl(created.id); + if (jsonMode) { + return { + stdout: `${JSON.stringify({ + id: created.id, + repo_url: created.imported_repo_url ?? null, + editor_url: editorUrl, + preview_url: previewUrl ?? null, + status: finalState ?? "created", + })}\n`, + }; + } + + log.message(`App: ${created.id}`); + if (created.imported_repo_url) + log.message(`Repo: ${created.imported_repo_url}`); + log.message(`Editor: ${editorUrl}`); + if (previewUrl) log.message(`Preview: ${previewUrl}`); + log.message(`Linked this directory (${configPath})`); + if (finalState === "error") { + return { + outroMessage: + "The first build reported an error — open the editor to see what the agent hit.", + }; + } + if (finalState === "processing") { + return { + outroMessage: + "Still building — check progress in the editor or with `base44 imported status`.", + }; + } + return { + outroMessage: options.prompt + ? "First build finished." + : "Imported app created.", + }; +} + +export function getImportedCreateCommand(): Base44Command { + const command = new Base44Command("create", { requireAppContext: false }); + command + .description("Create an imported app from a GitHub repo, or from scratch") + .option( + "--blank", + "Start from scratch in a fresh private GitHub repository", + ) + .option("--repo ", "GitHub repository URL to import") + .option( + "--repo-name ", + "Name for the new GitHub repository (--blank, or with --mode fork/copy)", + ) + .option( + "--mode ", + "How to import --repo: direct, fork, or copy (default: direct)", + ) + .option("--app-name ", "Display name for the Base44 app") + .option("--from-branch ", "Import a specific branch of --repo") + .option( + "--prompt ", + "First message for the agent; the build starts immediately", + ) + .action(createImportedAction); + return command; +} diff --git a/packages/cli/src/cli/commands/imported/git.ts b/packages/cli/src/cli/commands/imported/git.ts new file mode 100644 index 000000000..e1b1a2a3d --- /dev/null +++ b/packages/cli/src/cli/commands/imported/git.ts @@ -0,0 +1,143 @@ +import type { CLIContext, RunCommandResult } from "@/cli/types.js"; +import { Base44Command } from "@/cli/utils/index.js"; +import { InvalidInputError } from "@/core/errors.js"; +import type { ImportedGitStatus } from "@/core/resources/imported/api.js"; +import { + commitImportedChanges, + discardImportedChanges, + getImportedGitStatus, + getImportedPreviewUrl, + openImportedPullRequest, +} from "@/core/resources/imported/api.js"; + +function statusLines(status: ImportedGitStatus): string[] { + const position = [ + status.ahead != null ? `ahead ${status.ahead}` : null, + status.behind != null ? `behind ${status.behind}` : null, + ] + .filter(Boolean) + .join(", "); + const lines = [ + `Branch: ${status.current_branch} (default: ${status.default_branch}${position ? `; ${position}` : ""})`, + `Head: ${status.head ? status.head.slice(0, 10) : "none"}`, + `Tree: ${status.dirty ? `dirty — ${status.dirty_files.length} file(s)` : "clean"}`, + ]; + for (const file of status.dirty_files.slice(0, 20)) lines.push(` ${file}`); + if (status.merge_in_progress) lines.push("Merge in progress!"); + return lines; +} + +function statusResult( + status: ImportedGitStatus, + { log, jsonMode }: CLIContext, + outroMessage: string, +): RunCommandResult { + if (jsonMode) return { stdout: `${JSON.stringify(status)}\n` }; + for (const line of statusLines(status)) log.message(line); + return { outroMessage }; +} + +async function statusAction(ctx: CLIContext): Promise { + const status = await ctx.runTask("Reading sandbox git status", () => + getImportedGitStatus(ctx.branchId), + ); + return statusResult(status, ctx, "Status read."); +} + +async function commitAction( + ctx: CLIContext, + options: { message?: string }, +): Promise { + const status = await ctx.runTask("Committing and pushing", () => + commitImportedChanges(options.message, ctx.branchId), + ); + return statusResult(status, ctx, "Committed and pushed."); +} + +async function discardAction(ctx: CLIContext): Promise { + const status = await ctx.runTask("Discarding uncommitted changes", () => + discardImportedChanges(ctx.branchId), + ); + return statusResult(status, ctx, "Uncommitted changes discarded."); +} + +async function prAction( + ctx: CLIContext, + options: { title?: string; body?: string }, +): Promise { + const title = options.title?.trim(); + if (!title) { + throw new InvalidInputError("--title is required to open a pull request."); + } + const pr = await ctx.runTask("Opening pull request", () => + openImportedPullRequest(title, options.body ?? "", ctx.branchId), + ); + const url = pr.html_url ?? pr.url; + if (ctx.jsonMode) { + return { + stdout: `${JSON.stringify({ url: url ?? null, number: pr.number ?? null, created: pr.created ?? null })}\n`, + }; + } + if (url) ctx.log.message(url); + return { + outroMessage: + pr.created === false + ? "This branch already has a pull request." + : "Pull request opened.", + }; +} + +async function previewAction(ctx: CLIContext): Promise { + const url = await ctx.runTask( + "Resolving preview URL (boots the sandbox if needed)", + () => getImportedPreviewUrl(), + ); + if (ctx.jsonMode) + return { stdout: `${JSON.stringify({ preview_url: url })}\n` }; + ctx.log.message(url); + return { outroMessage: "Preview is live." }; +} + +export function getImportedStatusCommand(): Base44Command { + const command = new Base44Command("status", { supportsBranch: true }); + command + .description("Show the sandbox checkout's git status") + .action(statusAction); + return command; +} + +export function getImportedCommitCommand(): Base44Command { + const command = new Base44Command("commit", { supportsBranch: true }); + command + .description("Commit and push uncommitted sandbox changes") + .option( + "-m, --message ", + "Commit message (generated when omitted)", + ) + .action(commitAction); + return command; +} + +export function getImportedDiscardCommand(): Base44Command { + const command = new Base44Command("discard", { supportsBranch: true }); + command + .description("Throw away uncommitted sandbox changes") + .action(discardAction); + return command; +} + +export function getImportedPrCommand(): Base44Command { + const command = new Base44Command("pr", { supportsBranch: true }); + command + .description("Open a pull request for the app's working branch") + .option("--title ", "Pull request title") + .option("--body <body>", "Pull request body (markdown)") + .action(prAction); + return command; +} + +export function getImportedPreviewCommand(): Base44Command { + const command = new Base44Command("preview"); + command.description("Print the app's live preview URL").action(previewAction); + return command; +} diff --git a/packages/cli/src/cli/commands/imported/index.ts b/packages/cli/src/cli/commands/imported/index.ts new file mode 100644 index 000000000..3b3c1d519 --- /dev/null +++ b/packages/cli/src/cli/commands/imported/index.ts @@ -0,0 +1,24 @@ +import { Command } from "commander"; +import { getImportedChatCommand } from "@/cli/commands/imported/chat.js"; +import { getImportedCreateCommand } from "@/cli/commands/imported/create.js"; +import { + getImportedCommitCommand, + getImportedDiscardCommand, + getImportedPrCommand, + getImportedPreviewCommand, + getImportedStatusCommand, +} from "@/cli/commands/imported/git.js"; + +export function getImportedCommand(): Command { + return new Command("imported") + .description( + "Work with imported apps: your own repo (or a blank one) with a Base44 agent and live preview over it", + ) + .addCommand(getImportedCreateCommand()) + .addCommand(getImportedChatCommand()) + .addCommand(getImportedStatusCommand()) + .addCommand(getImportedCommitCommand()) + .addCommand(getImportedDiscardCommand()) + .addCommand(getImportedPrCommand()) + .addCommand(getImportedPreviewCommand()); +} diff --git a/packages/cli/src/cli/program.ts b/packages/cli/src/cli/program.ts index 070dfc749..4fd82a887 100644 --- a/packages/cli/src/cli/program.ts +++ b/packages/cli/src/cli/program.ts @@ -11,6 +11,7 @@ import { getConnectorsCommand } from "@/cli/commands/connectors/index.js"; import { getDashboardCommand } from "@/cli/commands/dashboard/index.js"; import { getEntitiesPushCommand } from "@/cli/commands/entities/push.js"; import { getFunctionsCommand } from "@/cli/commands/functions/index.js"; +import { getImportedCommand } from "@/cli/commands/imported/index.js"; import { getBuildCommand } from "@/cli/commands/project/build.js"; import { getCreateCommand } from "@/cli/commands/project/create.js"; import { getDeployCommand } from "@/cli/commands/project/deploy.js"; @@ -112,6 +113,9 @@ export function createProgram(context: CLIContext): Command { program.addCommand(getSandboxCommand()); program.addCommand(getBranchesCommand()); + // Register imported-app commands + program.addCommand(getImportedCommand()); + // Register auth config commands program.addCommand(getAuthCommand()); diff --git a/packages/cli/src/core/clients/base44-client.ts b/packages/cli/src/core/clients/base44-client.ts index a50e52fac..0bf02c73a 100644 --- a/packages/cli/src/core/clients/base44-client.ts +++ b/packages/cli/src/core/clients/base44-client.ts @@ -101,6 +101,10 @@ export const base44Client = ky.create({ beforeRequest: [ (request) => { request.headers.set("X-Request-ID", randomUUID()); + // Staging/preview only: lets a dev flip PostHog flags per request + // (e.g. BASE44_FF_OVERRIDE="imported-apps:true"); prod ignores it. + const ffOverride = process.env.BASE44_FF_OVERRIDE; + if (ffOverride) request.headers.set("X-FF-Override", ffOverride); }, captureRequestBody, async (request) => { diff --git a/packages/cli/src/core/resources/imported/api.ts b/packages/cli/src/core/resources/imported/api.ts new file mode 100644 index 000000000..02cc8c2ac --- /dev/null +++ b/packages/cli/src/core/resources/imported/api.ts @@ -0,0 +1,240 @@ +import type { KyResponse } from "ky"; +import { z } from "zod"; +import { base44Client, getAppClient } from "@/core/clients/index.js"; +import { ApiError, SchemaValidationError } from "@/core/errors.js"; + +const GitStatusSchema = z.object({ + current_branch: z.string(), + default_branch: z.string(), + head: z.string().nullish(), + dirty: z.boolean().default(false), + dirty_files: z.array(z.string()).default([]), + ahead: z.number().nullish(), + behind: z.number().nullish(), + sync_mode: z.string().default("synced"), + merge_in_progress: z.boolean().default(false), +}); +export type ImportedGitStatus = z.infer<typeof GitStatusSchema>; + +const CreatedAppSchema = z.object({ + id: z.string().min(1), + name: z.string().nullish(), + imported_repo_url: z.string().nullish(), +}); +type CreatedImportedApp = z.infer<typeof CreatedAppSchema>; + +const AppStateSchema = z.object({ + id: z.string(), + status: z + .object({ + state: z.string().nullish(), + message: z.string().nullish(), + }) + .nullish(), +}); +type ImportedAppState = z.infer<typeof AppStateSchema>; + +const ChatTurnSchema = z.object({ + queued: z.boolean().optional(), + status: z + .object({ + state: z.string().nullish(), + message: z.string().nullish(), + error_source: z.string().nullish(), + }) + .nullish(), + conversation: z + .object({ + messages: z + .array( + z.object({ + role: z.string().nullish(), + content: z.unknown().nullish(), + }), + ) + .nullish(), + }) + .nullish(), +}); +export type ImportedChatTurn = z.infer<typeof ChatTurnSchema>; + +const PullRequestSchema = z.object({ + html_url: z.string().nullish(), + url: z.string().nullish(), + number: z.number().nullish(), + created: z.boolean().nullish(), +}); +type ImportedPullRequest = z.infer<typeof PullRequestSchema>; + +const PreviewUrlSchema = z.object({ + preview_url: z.string().min(1), +}); + +function parseOrThrow<T>( + schema: z.ZodType<T>, + payload: unknown, + what: string, +): T { + const result = schema.safeParse(payload); + if (!result.success) { + throw new SchemaValidationError( + `Invalid ${what} response from server`, + result.error, + ); + } + return result.data; +} + +function branchScope(branchId?: string): Record<string, string> { + return branchId ? { branch_id: branchId } : {}; +} + +interface CreateImportedAppOptions { + appName: string; + sourceMode: "blank" | "direct" | "fork" | "copy"; + repoUrl?: string; + newRepoName?: string; + branch?: string; + prompt?: string; +} + +export async function createImportedApp( + options: CreateImportedAppOptions, +): Promise<CreatedImportedApp> { + let response: KyResponse; + try { + // Creation can fork/copy a repo on GitHub and classify the source, so it + // legitimately outlives ky's default timeout. + response = await base44Client.post("api/apps", { + timeout: false, + json: { + app_type: "imported_app", + name: options.appName, + imported_source_mode: options.sourceMode, + ...(options.repoUrl ? { imported_repo_url: options.repoUrl } : {}), + ...(options.newRepoName + ? { imported_new_repo_name: options.newRepoName } + : {}), + ...(options.branch ? { imported_branch: options.branch } : {}), + ...(options.prompt + ? { initial_message: { content: options.prompt } } + : {}), + }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "creating imported app"); + } + return parseOrThrow(CreatedAppSchema, await response.json(), "imported app"); +} + +export async function getImportedAppState( + appId: string, +): Promise<ImportedAppState> { + let response: KyResponse; + try { + response = await base44Client.get(`api/apps/${appId}`, { + searchParams: { fields: "id,status" }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "reading app status"); + } + return parseOrThrow(AppStateSchema, await response.json(), "app status"); +} + +export async function sendImportedChatMessage( + content: string, +): Promise<ImportedChatTurn> { + let response: KyResponse; + try { + // The request stays open for the whole agent turn (minutes on big changes). + response = await getAppClient().post("chat/message", { + timeout: false, + searchParams: { conversation_messages: "current_turn" }, + json: { content }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "sending chat message"); + } + return parseOrThrow(ChatTurnSchema, await response.json(), "chat turn"); +} + +export async function getImportedGitStatus( + branchId?: string, +): Promise<ImportedGitStatus> { + let response: KyResponse; + try { + // A cold sandbox is re-provisioned before git can answer. + response = await getAppClient().get("imported/git/status", { + timeout: false, + searchParams: branchScope(branchId), + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "reading git status"); + } + return parseOrThrow(GitStatusSchema, await response.json(), "git status"); +} + +export async function commitImportedChanges( + message?: string, + branchId?: string, +): Promise<ImportedGitStatus> { + let response: KyResponse; + try { + response = await getAppClient().post("imported/git/commit", { + timeout: false, + searchParams: branchScope(branchId), + json: { ...(message ? { message } : {}) }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "committing changes"); + } + return parseOrThrow(GitStatusSchema, await response.json(), "git status"); +} + +export async function discardImportedChanges( + branchId?: string, +): Promise<ImportedGitStatus> { + let response: KyResponse; + try { + response = await getAppClient().post("imported/git/discard", { + timeout: false, + searchParams: branchScope(branchId), + json: {}, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "discarding changes"); + } + return parseOrThrow(GitStatusSchema, await response.json(), "git status"); +} + +export async function openImportedPullRequest( + title: string, + body: string, + branchId?: string, +): Promise<ImportedPullRequest> { + let response: KyResponse; + try { + response = await getAppClient().post("imported/git/pull-request", { + timeout: false, + searchParams: branchScope(branchId), + json: { title, body }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "opening pull request"); + } + return parseOrThrow(PullRequestSchema, await response.json(), "pull request"); +} + +export async function getImportedPreviewUrl(): Promise<string> { + let response: KyResponse; + try { + // Rehydrates a dead sandbox before answering, so it can take a while. + response = await getAppClient().get("sandbox/preview-url", { + timeout: false, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "fetching preview URL"); + } + return parseOrThrow(PreviewUrlSchema, await response.json(), "preview URL") + .preview_url; +} diff --git a/packages/cli/tests/cli/imported.spec.ts b/packages/cli/tests/cli/imported.spec.ts new file mode 100644 index 000000000..00df570ab --- /dev/null +++ b/packages/cli/tests/cli/imported.spec.ts @@ -0,0 +1,194 @@ +import { describe, expect, it } from "vitest"; +import { setupCLITests } from "./testkit/index.js"; + +const GIT_STATUS = { + current_branch: "base44/setup", + default_branch: "main", + head: "4c7e1f90ab3d5628e1a0f7b24c9d8e6350a1b2c4", + dirty: true, + dirty_files: ["api/config.py"], + ahead: 3, + behind: 0, + sync_mode: "synced", + merge_in_progress: false, +}; + +describe("imported", () => { + const t = setupCLITests(); + + it("status renders the sandbox checkout's git state", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockRoute( + "GET", + "/api/apps/test-app-id/imported/git/status", + (_req, res) => res.json(GIT_STATUS), + ); + const result = await t.run( + "imported", + "status", + "--app-id", + "test-app-id", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(JSON.parse(result.stdout)).toMatchObject({ + current_branch: "base44/setup", + dirty: true, + ahead: 3, + }); + }); + + it("commit posts the message and reports the pushed state", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + let sentBody: unknown; + t.api.mockRoute( + "POST", + "/api/apps/test-app-id/imported/git/commit", + (req, res) => { + sentBody = req.body; + return res.json({ ...GIT_STATUS, dirty: false, dirty_files: [] }); + }, + ); + const result = await t.run( + "imported", + "commit", + "-m", + "tweak config", + "--app-id", + "test-app-id", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(sentBody).toEqual({ message: "tweak config" }); + expect(JSON.parse(result.stdout)).toMatchObject({ dirty: false }); + }); + + it("pr requires a title before calling the API", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + const result = await t.run( + "imported", + "pr", + "--app-id", + "test-app-id", + "--json", + ); + t.expectResult(result).toFail(); + expect(JSON.parse(result.stdout).error).toContain("--title"); + }); + + it("pr opens a pull request and prints its URL", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockRoute( + "POST", + "/api/apps/test-app-id/imported/git/pull-request", + (_req, res) => + res.json({ + html_url: "https://github.com/acme/app/pull/7", + number: 7, + created: true, + }), + ); + const result = await t.run( + "imported", + "pr", + "--title", + "Recipe manager v1", + "--app-id", + "test-app-id", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(JSON.parse(result.stdout)).toEqual({ + url: "https://github.com/acme/app/pull/7", + number: 7, + created: true, + }); + }); + + it("chat sends the message and surfaces the assistant reply", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockRoute("POST", "/api/apps/test-app-id/chat/message", (_req, res) => + res.json({ + id: "test-app-id", + status: { state: "ready" }, + conversation: { + id: "conv-1", + messages: [ + { role: "user", content: "add login" }, + { role: "assistant", content: "Added session-based login." }, + ], + }, + }), + ); + const result = await t.run( + "imported", + "chat", + "add login", + "--app-id", + "test-app-id", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(JSON.parse(result.stdout)).toEqual({ + status: "ready", + error_source: null, + reply: "Added session-based login.", + }); + }); + + it("chat reports a queued turn instead of inventing a reply", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockRoute("POST", "/api/apps/test-app-id/chat/message", (_req, res) => + res.json({ queued: true }), + ); + const result = await t.run( + "imported", + "chat", + "one more thing", + "--app-id", + "test-app-id", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(JSON.parse(result.stdout)).toEqual({ queued: true }); + }); + + it("create --blank requires a repo name and sends the blank payload", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + + const missingName = await t.run("imported", "create", "--blank", "--json"); + t.expectResult(missingName).toFail(); + expect(JSON.parse(missingName.stdout).error).toContain("--repo-name"); + + let sentBody: Record<string, unknown> | undefined; + t.api.mockRoute("POST", "/api/apps", (req, res) => { + sentBody = req.body as Record<string, unknown>; + return res.json({ + id: "new-app-1", + name: "recipe-box", + imported_repo_url: "https://github.com/tester/recipe-box", + }); + }); + const result = await t.run( + "imported", + "create", + "--blank", + "--repo-name", + "recipe-box", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(sentBody).toMatchObject({ + app_type: "imported_app", + imported_source_mode: "blank", + imported_new_repo_name: "recipe-box", + name: "recipe-box", + }); + expect(sentBody).not.toHaveProperty("imported_repo_url"); + expect(JSON.parse(result.stdout)).toMatchObject({ + id: "new-app-1", + repo_url: "https://github.com/tester/recipe-box", + status: "created", + }); + }); +}); From 023b03a456ba854bb46633ae7101866eb343a70c Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 19:24:29 +0300 Subject: [PATCH 02/73] feat(imported): live turn stream for chat and create --prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chat and the create kickoff now print the agent's activity as it happens — thinking, text, tool calls and their results — by polling the app's chat/full-conversation endpoint (messages persist per agent round) while the turn request is open. The diff engine is pure and unit-tested; --json keeps the single-document contract and skips streaming. chat also scopes to the app's sole active branch so messages land on the working branch (an unscoped send hits the unpushable main line) and accepts --branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 63 +++++--- .../cli/src/cli/commands/imported/create.ts | 20 ++- .../cli/src/core/resources/imported/api.ts | 61 +++++++- .../cli/src/core/resources/imported/stream.ts | 141 ++++++++++++++++++ packages/cli/tests/cli/imported.spec.ts | 41 +++-- .../cli/tests/core/imported-stream.spec.ts | 114 ++++++++++++++ 6 files changed, 400 insertions(+), 40 deletions(-) create mode 100644 packages/cli/src/core/resources/imported/stream.ts create mode 100644 packages/cli/tests/core/imported-stream.spec.ts diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 1ef47f99e..8379b073a 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,7 +1,11 @@ import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import type { ImportedChatTurn } from "@/core/resources/imported/api.js"; -import { sendImportedChatMessage } from "@/core/resources/imported/api.js"; +import { + sendImportedChatMessage, + soleActiveBranchId, +} from "@/core/resources/imported/api.js"; +import { streamConversationDuring } from "@/core/resources/imported/stream.js"; function lastAssistantReply(turn: ImportedChatTurn): string | undefined { const messages = turn.conversation?.messages ?? []; @@ -15,46 +19,61 @@ function lastAssistantReply(turn: ImportedChatTurn): string | undefined { return undefined; } +function turnOutro(turn: ImportedChatTurn): string { + const state = turn.status?.state ?? "ready"; + if (state === "error") { + return `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details.`; + } + return "Turn finished."; +} + async function chatAction( - { log, runTask, jsonMode }: CLIContext, + { log, runTask, jsonMode, branchId: explicitBranchId }: CLIContext, message: string, ): Promise<RunCommandResult> { - const turn = await runTask("Agent working (a turn can take minutes)", () => - sendImportedChatMessage(message), - ); + // Messages must land on the app's working branch: an unscoped send goes to + // the main line, whose sandbox is separate and never pushed. + const branchId = + explicitBranchId ?? (await soleActiveBranchId().catch(() => undefined)); + + let turn: ImportedChatTurn; + if (jsonMode) { + turn = await runTask("Agent working (a turn can take minutes)", () => + sendImportedChatMessage(message, branchId), + ); + } else { + log.message("Agent working — live from the sandbox:"); + turn = await streamConversationDuring( + () => sendImportedChatMessage(message, branchId), + (line) => log.message(line), + { branchId }, + ); + } if (turn.queued) { - const note = - "The agent is busy with an earlier message — yours was queued and will run next."; if (jsonMode) return { stdout: `${JSON.stringify({ queued: true })}\n` }; - return { outroMessage: note }; + return { + outroMessage: + "The agent is busy with an earlier message — yours was queued and will run next.", + }; } - const state = turn.status?.state ?? "ready"; - const reply = lastAssistantReply(turn); if (jsonMode) { return { stdout: `${JSON.stringify({ - status: state, + status: turn.status?.state ?? "ready", error_source: turn.status?.error_source ?? null, - reply: reply ?? null, + reply: lastAssistantReply(turn) ?? null, })}\n`, }; } - - if (reply) log.message(reply); - if (state === "error") { - return { - outroMessage: `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details.`, - }; - } - return { outroMessage: "Turn finished." }; + return { outroMessage: turnOutro(turn) }; } export function getImportedChatCommand(): Base44Command { - const command = new Base44Command("chat"); + const command = new Base44Command("chat", { supportsBranch: true }); command - .description("Send a message to the app's agent and wait for the turn") + .description("Send a message to the app's agent and watch the turn live") .argument("<message>", "What you want the agent to do") .action(chatAction); return command; diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 2df384c1f..7916d6756 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -10,7 +10,9 @@ import { createImportedApp, getImportedAppState, getImportedPreviewUrl, + soleActiveBranchId, } from "@/core/resources/imported/api.js"; +import { streamConversationDuring } from "@/core/resources/imported/stream.js"; const POLL_INTERVAL_MS = 5_000; const POLL_TIMEOUT_MS = 20 * 60_000; @@ -96,10 +98,20 @@ async function createImportedAction( let finalState: string | undefined; let previewUrl: string | undefined; if (options.prompt) { - finalState = await runTask( - "Agent is building (several minutes; safe to Ctrl+C — the build continues)", - () => waitForInitialTurn(created.id), - ); + if (jsonMode) { + finalState = await waitForInitialTurn(created.id); + } else { + // The kickoff turn runs on the app's setup branch conversation. + const branchId = await soleActiveBranchId().catch(() => undefined); + log.message( + "Agent is building — live (several minutes; safe to Ctrl+C, the build continues):", + ); + finalState = await streamConversationDuring( + () => waitForInitialTurn(created.id), + (line) => log.message(line), + { branchId }, + ); + } if (finalState === "ready") { try { previewUrl = await runTask("Fetching preview URL", () => diff --git a/packages/cli/src/core/resources/imported/api.ts b/packages/cli/src/core/resources/imported/api.ts index 02cc8c2ac..f563563b8 100644 --- a/packages/cli/src/core/resources/imported/api.ts +++ b/packages/cli/src/core/resources/imported/api.ts @@ -2,6 +2,7 @@ import type { KyResponse } from "ky"; import { z } from "zod"; import { base44Client, getAppClient } from "@/core/clients/index.js"; import { ApiError, SchemaValidationError } from "@/core/errors.js"; +import { listBranches } from "@/core/resources/branch/api.js"; const GitStatusSchema = z.object({ current_branch: z.string(), @@ -70,6 +71,30 @@ const PreviewUrlSchema = z.object({ preview_url: z.string().min(1), }); +const ConversationMessageSchema = z.object({ + id: z.string(), + role: z.string(), + hidden: z.boolean().nullish(), + content: z.unknown().nullish(), + reasoning: z.object({ content: z.string().nullish() }).nullish(), + tool_calls: z + .array( + z.object({ + id: z.string(), + name: z.string(), + arguments_string: z.string().nullish(), + status: z.string().nullish(), + results: z.unknown().nullish(), + }), + ) + .nullish(), +}); +export type ConversationMessage = z.infer<typeof ConversationMessageSchema>; + +const FullConversationSchema = z.object({ + messages: z.array(ConversationMessageSchema).default([]), +}); + function parseOrThrow<T>( schema: z.ZodType<T>, payload: unknown, @@ -143,13 +168,17 @@ export async function getImportedAppState( export async function sendImportedChatMessage( content: string, + branchId?: string, ): Promise<ImportedChatTurn> { let response: KyResponse; try { // The request stays open for the whole agent turn (minutes on big changes). response = await getAppClient().post("chat/message", { timeout: false, - searchParams: { conversation_messages: "current_turn" }, + searchParams: { + conversation_messages: "current_turn", + ...branchScope(branchId), + }, json: { content }, }); } catch (error) { @@ -158,6 +187,36 @@ export async function sendImportedChatMessage( return parseOrThrow(ChatTurnSchema, await response.json(), "chat turn"); } +export async function getFullConversation( + limit: number, + branchId?: string, +): Promise<ConversationMessage[]> { + let response: KyResponse; + try { + response = await getAppClient().get("chat/full-conversation", { + timeout: 30_000, + searchParams: { limit: String(limit), ...branchScope(branchId) }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "reading the conversation"); + } + return parseOrThrow( + FullConversationSchema, + await response.json(), + "conversation", + ).messages; +} + +/** + * The branch the app's work is actually happening on, when unambiguous — a + * fresh import works on its single setup branch, and messages sent without a + * scope would land on the (unpushable) main line instead. + */ +export async function soleActiveBranchId(): Promise<string | undefined> { + const branches = await listBranches(); + return branches.length === 1 ? branches[0].id : undefined; +} + export async function getImportedGitStatus( branchId?: string, ): Promise<ImportedGitStatus> { diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts new file mode 100644 index 000000000..6ccf5f4bb --- /dev/null +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -0,0 +1,141 @@ +import type { ConversationMessage } from "@/core/resources/imported/api.js"; +import { getFullConversation } from "@/core/resources/imported/api.js"; + +interface MessageProgress { + contentLength: number; + reasoningLength: number; + announcedTools: Set<string>; + settledTools: Set<string>; +} + +interface StreamState { + perMessage: Map<string, MessageProgress>; +} + +export function newStreamState(): StreamState { + return { perMessage: new Map() }; +} + +const TOOL_SETTLED = new Set(["success", "error", "stopped"]); + +function oneLine(value: unknown, max: number): string { + const text = + typeof value === "string" + ? value + : value == null + ? "" + : JSON.stringify(value); + const flat = text.replace(/\s+/g, " ").trim(); + return flat.length > max ? `${flat.slice(0, max)}…` : flat; +} + +function progressFor(state: StreamState, id: string): MessageProgress { + let progress = state.perMessage.get(id); + if (!progress) { + progress = { + contentLength: 0, + reasoningLength: 0, + announcedTools: new Set(), + settledTools: new Set(), + }; + state.perMessage.set(id, progress); + } + return progress; +} + +/** + * Diff a fresh conversation snapshot against what was already shown and return + * the new lines to print. Mutates `state`. Pure aside from that — no I/O — so + * the rendering rules are unit-testable. + */ +export function renderConversationDelta( + state: StreamState, + messages: ConversationMessage[], +): string[] { + const lines: string[] = []; + for (const message of messages) { + if (message.role !== "assistant" || message.hidden) continue; + const progress = progressFor(state, message.id); + + const reasoning = message.reasoning?.content ?? ""; + if (reasoning.length > progress.reasoningLength) { + const delta = reasoning.slice(progress.reasoningLength).trim(); + if (delta) lines.push(`✻ ${oneLine(delta, 300)}`); + progress.reasoningLength = reasoning.length; + } + + if ( + typeof message.content === "string" && + message.content.length > progress.contentLength + ) { + const delta = message.content.slice(progress.contentLength).trim(); + if (delta) lines.push(delta); + progress.contentLength = message.content.length; + } + + for (const tool of message.tool_calls ?? []) { + if (!progress.announcedTools.has(tool.id)) { + progress.announcedTools.add(tool.id); + const args = oneLine(tool.arguments_string ?? "", 110); + lines.push(`→ ${tool.name}${args ? ` ${args}` : ""}`); + } + const status = tool.status ?? "running"; + if (TOOL_SETTLED.has(status) && !progress.settledTools.has(tool.id)) { + progress.settledTools.add(tool.id); + const mark = status === "success" ? "✓" : "✗"; + const result = oneLine(tool.results, 140); + lines.push(`${mark} ${tool.name}${result ? ` — ${result}` : ""}`); + } + } + } + return lines; +} + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Run `start` while live-printing the conversation it drives. + * + * The current snapshot is consumed FIRST (so earlier turns are never + * replayed), then `start` fires, and the conversation is polled until its + * promise settles — with one final read so nothing between the last tick and + * settlement is lost. Poll failures are skipped (transient); `start`'s result + * or rejection passes through untouched. + */ +export async function streamConversationDuring<T>( + start: () => Promise<T>, + print: (line: string) => void, + options: { branchId?: string; intervalMs?: number } = {}, +): Promise<T> { + const intervalMs = options.intervalMs ?? 2_000; + const state = newStreamState(); + + const poll = async (prime = false) => { + try { + const messages = await getFullConversation(30, options.branchId); + const lines = renderConversationDelta(state, messages); + if (!prime) for (const line of lines) print(line); + } catch { + // Transient read failure — the next tick retries. + } + }; + + await poll(true); + const work = start(); + let pending = true; + const settled = work.then( + () => { + pending = false; + }, + () => { + pending = false; + }, + ); + while (pending) { + await Promise.race([sleep(intervalMs), settled]); + if (!pending) break; + await poll(); + } + await poll(); + return work; +} diff --git a/packages/cli/tests/cli/imported.spec.ts b/packages/cli/tests/cli/imported.spec.ts index 00df570ab..2d0fe463b 100644 --- a/packages/cli/tests/cli/imported.spec.ts +++ b/packages/cli/tests/cli/imported.spec.ts @@ -105,20 +105,31 @@ describe("imported", () => { }); }); - it("chat sends the message and surfaces the assistant reply", async () => { + it("chat scopes to the sole active branch and surfaces the reply", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); - t.api.mockRoute("POST", "/api/apps/test-app-id/chat/message", (_req, res) => - res.json({ - id: "test-app-id", - status: { state: "ready" }, - conversation: { - id: "conv-1", - messages: [ - { role: "user", content: "add login" }, - { role: "assistant", content: "Added session-based login." }, - ], - }, - }), + t.api.mockRoute("GET", "/api/apps/test-app-id/branches", (_req, res) => + res.json([ + { id: "b1", branch_name: "base44/setup-abc", status: "active" }, + ]), + ); + let sentBranchId: unknown; + t.api.mockRoute( + "POST", + "/api/apps/test-app-id/chat/message", + (req, res) => { + sentBranchId = req.query.branch_id; + return res.json({ + id: "test-app-id", + status: { state: "ready" }, + conversation: { + id: "conv-1", + messages: [ + { role: "user", content: "add login" }, + { role: "assistant", content: "Added session-based login." }, + ], + }, + }); + }, ); const result = await t.run( "imported", @@ -129,6 +140,7 @@ describe("imported", () => { "--json", ); t.expectResult(result).toSucceed(); + expect(sentBranchId).toBe("b1"); expect(JSON.parse(result.stdout)).toEqual({ status: "ready", error_source: null, @@ -138,6 +150,9 @@ describe("imported", () => { it("chat reports a queued turn instead of inventing a reply", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockRoute("GET", "/api/apps/test-app-id/branches", (_req, res) => + res.json([]), + ); t.api.mockRoute("POST", "/api/apps/test-app-id/chat/message", (_req, res) => res.json({ queued: true }), ); diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts new file mode 100644 index 000000000..754bfa50b --- /dev/null +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -0,0 +1,114 @@ +import { describe, expect, it } from "vitest"; +import type { ConversationMessage } from "@/core/resources/imported/api.js"; +import { + newStreamState, + renderConversationDelta, +} from "@/core/resources/imported/stream.js"; + +const assistant = ( + overrides: Partial<ConversationMessage> & { id: string }, +): ConversationMessage => ({ + role: "assistant", + content: null, + ...overrides, +}); + +describe("renderConversationDelta", () => { + it("prints each item once across polls: announce, then settle, then nothing", () => { + const state = newStreamState(); + const running = assistant({ + id: "m1", + reasoning: { content: "Choosing FastAPI." }, + tool_calls: [ + { + id: "t1", + name: "run_shell_command", + arguments_string: '{"command": "docker compose up -d"}', + status: "running", + results: null, + }, + ], + }); + + const first = renderConversationDelta(state, [running]); + expect(first).toEqual([ + "✻ Choosing FastAPI.", + '→ run_shell_command {"command": "docker compose up -d"}', + ]); + + const settled = assistant({ + ...running, + content: "The stack is up.", + tool_calls: [ + { + ...running.tool_calls?.[0], + status: "success", + results: "3 containers started", + }, + ], + } as ConversationMessage); + const second = renderConversationDelta(state, [settled]); + expect(second).toEqual([ + "The stack is up.", + "✓ run_shell_command — 3 containers started", + ]); + + expect(renderConversationDelta(state, [settled])).toEqual([]); + }); + + it("prints only the newly appended part of growing text", () => { + const state = newStreamState(); + renderConversationDelta(state, [ + assistant({ id: "m1", content: "Scaffolding the backend." }), + ]); + const delta = renderConversationDelta(state, [ + assistant({ + id: "m1", + content: "Scaffolding the backend. Now the frontend.", + }), + ]); + expect(delta).toEqual(["Now the frontend."]); + }); + + it("marks a failed tool distinctly and flattens structured results", () => { + const state = newStreamState(); + const lines = renderConversationDelta(state, [ + assistant({ + id: "m1", + tool_calls: [ + { + id: "t1", + name: "edit_repo_file", + arguments_string: null, + status: "error", + results: { error: "File not found" }, + }, + ], + }), + ]); + expect(lines).toEqual([ + "→ edit_repo_file", + '✗ edit_repo_file — {"error":"File not found"}', + ]); + }); + + it("ignores user and hidden messages", () => { + const state = newStreamState(); + const lines = renderConversationDelta(state, [ + { id: "u1", role: "user", content: "add login" }, + assistant({ id: "h1", hidden: true, content: "internal" }), + ]); + expect(lines).toEqual([]); + }); + + it("a primed state suppresses history but streams what comes after", () => { + const state = newStreamState(); + const history = assistant({ id: "m0", content: "Earlier turn summary." }); + renderConversationDelta(state, [history]); // prime + const lines = renderConversationDelta(state, [ + history, + assistant({ id: "m1", content: "New turn begins." }), + ]); + expect(lines).toEqual(["New turn begins."]); + }); +}); From 3ed5c4231a0f2f0567fede0d1a0ee79014e869e0 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 19:34:18 +0300 Subject: [PATCH 03/73] fix(imported): pretty turn stream + authoritative completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stream now renders like the editor transcript: dim thinking, plain prose, tool lines showing the salient argument (the command, the path, the PR title — parsed from arguments_string, never the raw JSON blob), and a dim one-line result under each settled tool with failures in red. The diff engine emits typed events; the command layer styles them. create --prompt no longer trusts the app status field, which flaps mid-turn and ended the wait after the first round ('First build finished' 30 seconds in). Completion is now the outcome stamp the backend writes onto the turn's user message at end-of-loop, on success and error alike. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 3 +- .../cli/src/cli/commands/imported/create.ts | 46 ++--- .../cli/src/cli/commands/imported/render.ts | 24 +++ .../cli/src/core/resources/imported/api.ts | 1 + .../cli/src/core/resources/imported/stream.ts | 159 +++++++++++++---- .../cli/tests/core/imported-stream.spec.ts | 166 ++++++++++++------ 6 files changed, 285 insertions(+), 114 deletions(-) create mode 100644 packages/cli/src/cli/commands/imported/render.ts diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 8379b073a..604e6dba2 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,3 +1,4 @@ +import { renderStreamEvent } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import type { ImportedChatTurn } from "@/core/resources/imported/api.js"; @@ -45,7 +46,7 @@ async function chatAction( log.message("Agent working — live from the sandbox:"); turn = await streamConversationDuring( () => sendImportedChatMessage(message, branchId), - (line) => log.message(line), + (event) => log.message(renderStreamEvent(event)), { branchId }, ); } diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 7916d6756..202697616 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,3 +1,4 @@ +import { renderStreamEvent } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command, getDashboardUrl } from "@/cli/utils/index.js"; import { InvalidInputError } from "@/core/errors.js"; @@ -12,15 +13,9 @@ import { getImportedPreviewUrl, soleActiveBranchId, } from "@/core/resources/imported/api.js"; -import { streamConversationDuring } from "@/core/resources/imported/stream.js"; +import { streamConversationUntilSettled } from "@/core/resources/imported/stream.js"; -const POLL_INTERVAL_MS = 5_000; const POLL_TIMEOUT_MS = 20 * 60_000; -// The initial turn is scheduled in the background, so "ready" in the first -// moments just means it hasn't started yet. -const MIN_BUILD_MS = 20_000; - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); interface CreateImportedOptions { blank?: boolean; @@ -32,18 +27,6 @@ interface CreateImportedOptions { prompt?: string; } -async function waitForInitialTurn(appId: string): Promise<string> { - const startedAt = Date.now(); - await sleep(MIN_BUILD_MS); - while (Date.now() - startedAt < POLL_TIMEOUT_MS) { - const { status } = await getImportedAppState(appId); - const state = status?.state ?? "ready"; - if (state !== "processing") return state; - await sleep(POLL_INTERVAL_MS); - } - return "processing"; -} - async function createImportedAction( { log, runTask, jsonMode }: CLIContext, options: CreateImportedOptions, @@ -98,20 +81,25 @@ async function createImportedAction( let finalState: string | undefined; let previewUrl: string | undefined; if (options.prompt) { - if (jsonMode) { - finalState = await waitForInitialTurn(created.id); - } else { - // The kickoff turn runs on the app's setup branch conversation. - const branchId = await soleActiveBranchId().catch(() => undefined); + // The kickoff turn runs on the app's setup branch conversation. Completion + // is the outcome stamp on the turn's user message — the app status field + // flaps mid-turn and cannot be trusted. + const branchId = await soleActiveBranchId().catch(() => undefined); + if (!jsonMode) { log.message( "Agent is building — live (several minutes; safe to Ctrl+C, the build continues):", ); - finalState = await streamConversationDuring( - () => waitForInitialTurn(created.id), - (line) => log.message(line), - { branchId }, - ); } + const settled = await streamConversationUntilSettled( + (event) => { + if (!jsonMode) log.message(renderStreamEvent(event)); + }, + { branchId, timeoutMs: POLL_TIMEOUT_MS }, + ); + finalState = + settled === "timeout" + ? "processing" + : ((await getImportedAppState(created.id)).status?.state ?? "ready"); if (finalState === "ready") { try { previewUrl = await runTask("Fetching preview URL", () => diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts new file mode 100644 index 000000000..48c8add8a --- /dev/null +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -0,0 +1,24 @@ +import { theme } from "@/cli/utils/index.js"; +import type { StreamEvent } from "@/core/resources/imported/stream.js"; + +/** One styled terminal line per stream event, editor-transcript style. */ +export function renderStreamEvent(event: StreamEvent): string { + switch (event.kind) { + case "thinking": + return theme.styles.dim(`✻ ${event.text}`); + case "text": + return event.text; + case "tool_start": { + const name = theme.styles.info(event.name); + return event.summary + ? `${theme.styles.dim("●")} ${name} ${theme.styles.dim(event.summary)}` + : `${theme.styles.dim("●")} ${name}`; + } + case "tool_end": { + const mark = event.ok + ? theme.styles.dim(" ↳ ok") + : theme.styles.error(" ↳ failed"); + return event.result ? `${mark} ${theme.styles.dim(event.result)}` : mark; + } + } +} diff --git a/packages/cli/src/core/resources/imported/api.ts b/packages/cli/src/core/resources/imported/api.ts index f563563b8..f90aaf17e 100644 --- a/packages/cli/src/core/resources/imported/api.ts +++ b/packages/cli/src/core/resources/imported/api.ts @@ -75,6 +75,7 @@ const ConversationMessageSchema = z.object({ id: z.string(), role: z.string(), hidden: z.boolean().nullish(), + outcome: z.unknown().nullish(), content: z.unknown().nullish(), reasoning: z.object({ content: z.string().nullish() }).nullish(), tool_calls: z diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index 6ccf5f4bb..4b6458bc6 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -1,6 +1,12 @@ import type { ConversationMessage } from "@/core/resources/imported/api.js"; import { getFullConversation } from "@/core/resources/imported/api.js"; +export type StreamEvent = + | { kind: "thinking"; text: string } + | { kind: "text"; text: string } + | { kind: "tool_start"; name: string; summary: string } + | { kind: "tool_end"; name: string; ok: boolean; result: string }; + interface MessageProgress { contentLength: number; reasoningLength: number; @@ -29,6 +35,42 @@ function oneLine(value: unknown, max: number): string { return flat.length > max ? `${flat.slice(0, max)}…` : flat; } +/** The one argument a human wants to see for each tool, not the JSON blob. */ +export function toolSummary( + name: string, + argumentsString: string | null | undefined, +): string { + let args: Record<string, unknown> = {}; + try { + const parsed: unknown = JSON.parse(argumentsString ?? ""); + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + args = parsed as Record<string, unknown>; + } + } catch { + return oneLine(argumentsString ?? "", 90); + } + const pick = (key: string): string | undefined => + typeof args[key] === "string" && (args[key] as string).trim() + ? (args[key] as string) + : undefined; + const salient: Record<string, string | undefined> = { + run_shell_command: pick("command"), + read_repo_file: pick("path") ?? pick("file_path"), + write_repo_file: pick("path") ?? pick("file_path"), + edit_repo_file: pick("path") ?? pick("file_path"), + create_pull_request: pick("title"), + reload_preview: "", + }; + const summary = + salient[name] ?? + pick("summary") ?? + Object.values(args).find( + (v): v is string => typeof v === "string" && v.trim().length > 0, + ) ?? + ""; + return oneLine(summary, 90); +} + function progressFor(state: StreamState, id: string): MessageProgress { let progress = state.perMessage.get(id); if (!progress) { @@ -44,15 +86,15 @@ function progressFor(state: StreamState, id: string): MessageProgress { } /** - * Diff a fresh conversation snapshot against what was already shown and return - * the new lines to print. Mutates `state`. Pure aside from that — no I/O — so - * the rendering rules are unit-testable. + * Diff a fresh conversation snapshot against what was already emitted and + * return the new events. Mutates `state`; otherwise pure — no I/O — so the + * streaming rules are unit-testable. */ -export function renderConversationDelta( +export function diffConversation( state: StreamState, messages: ConversationMessage[], -): string[] { - const lines: string[] = []; +): StreamEvent[] { + const events: StreamEvent[] = []; for (const message of messages) { if (message.role !== "assistant" || message.hidden) continue; const progress = progressFor(state, message.id); @@ -60,7 +102,7 @@ export function renderConversationDelta( const reasoning = message.reasoning?.content ?? ""; if (reasoning.length > progress.reasoningLength) { const delta = reasoning.slice(progress.reasoningLength).trim(); - if (delta) lines.push(`✻ ${oneLine(delta, 300)}`); + if (delta) events.push({ kind: "thinking", text: oneLine(delta, 300) }); progress.reasoningLength = reasoning.length; } @@ -69,57 +111,87 @@ export function renderConversationDelta( message.content.length > progress.contentLength ) { const delta = message.content.slice(progress.contentLength).trim(); - if (delta) lines.push(delta); + if (delta) events.push({ kind: "text", text: delta }); progress.contentLength = message.content.length; } for (const tool of message.tool_calls ?? []) { if (!progress.announcedTools.has(tool.id)) { progress.announcedTools.add(tool.id); - const args = oneLine(tool.arguments_string ?? "", 110); - lines.push(`→ ${tool.name}${args ? ` ${args}` : ""}`); + events.push({ + kind: "tool_start", + name: tool.name, + summary: toolSummary(tool.name, tool.arguments_string), + }); } const status = tool.status ?? "running"; if (TOOL_SETTLED.has(status) && !progress.settledTools.has(tool.id)) { progress.settledTools.add(tool.id); - const mark = status === "success" ? "✓" : "✗"; - const result = oneLine(tool.results, 140); - lines.push(`${mark} ${tool.name}${result ? ` — ${result}` : ""}`); + events.push({ + kind: "tool_end", + name: tool.name, + ok: status === "success", + result: oneLine(tool.results, 110), + }); } } } - return lines; + return events; } -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - /** - * Run `start` while live-printing the conversation it drives. - * - * The current snapshot is consumed FIRST (so earlier turns are never - * replayed), then `start` fires, and the conversation is polled until its - * promise settles — with one final read so nothing between the last tick and - * settlement is lost. Poll failures are skipped (transient); `start`'s result - * or rejection passes through untouched. + * Whether the newest user message's turn has finished: the backend stamps + * `outcome` onto the turn's user message at end-of-loop, on success and error + * alike. Authoritative, unlike the app's status field, which flaps mid-turn. */ -export async function streamConversationDuring<T>( - start: () => Promise<T>, - print: (line: string) => void, - options: { branchId?: string; intervalMs?: number } = {}, -): Promise<T> { - const intervalMs = options.intervalMs ?? 2_000; - const state = newStreamState(); +export function turnSettled(messages: ConversationMessage[]): boolean { + for (let i = messages.length - 1; i >= 0; i--) { + const message = messages[i]; + if (message.role === "user" && !message.hidden) { + return message.outcome != null; + } + } + return false; +} + +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +interface StreamOptions { + branchId?: string; + intervalMs?: number; +} - const poll = async (prime = false) => { +function makePoller( + onEvent: (event: StreamEvent) => void, + options: StreamOptions, +) { + const state = newStreamState(); + return async (prime = false): Promise<ConversationMessage[]> => { try { const messages = await getFullConversation(30, options.branchId); - const lines = renderConversationDelta(state, messages); - if (!prime) for (const line of lines) print(line); + const events = diffConversation(state, messages); + if (!prime) for (const event of events) onEvent(event); + return messages; } catch { - // Transient read failure — the next tick retries. + return []; // Transient read failure — the next tick retries. } }; +} +/** + * Run `start` while live-emitting the conversation it drives. The current + * snapshot is consumed FIRST (earlier turns are never replayed), then `start` + * fires, and the conversation is polled until its promise settles — with one + * final read so nothing between the last tick and settlement is lost. + * `start`'s result or rejection passes through untouched. + */ +export async function streamConversationDuring<T>( + start: () => Promise<T>, + onEvent: (event: StreamEvent) => void, + options: StreamOptions = {}, +): Promise<T> { + const intervalMs = options.intervalMs ?? 2_000; + const poll = makePoller(onEvent, options); await poll(true); const work = start(); let pending = true; @@ -139,3 +211,22 @@ export async function streamConversationDuring<T>( await poll(); return work; } + +/** + * Live-emit a turn that is already running server-side (the create kickoff), + * until its user message carries an outcome — or the deadline passes. + */ +export async function streamConversationUntilSettled( + onEvent: (event: StreamEvent) => void, + options: StreamOptions & { timeoutMs?: number } = {}, +): Promise<"settled" | "timeout"> { + const intervalMs = options.intervalMs ?? 2_000; + const deadline = Date.now() + (options.timeoutMs ?? 20 * 60_000); + const poll = makePoller(onEvent, options); + while (Date.now() < deadline) { + const messages = await poll(); + if (messages.length > 0 && turnSettled(messages)) return "settled"; + await sleep(intervalMs); + } + return "timeout"; +} diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 754bfa50b..580b13fa1 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -1,8 +1,10 @@ import { describe, expect, it } from "vitest"; import type { ConversationMessage } from "@/core/resources/imported/api.js"; import { + diffConversation, newStreamState, - renderConversationDelta, + toolSummary, + turnSettled, } from "@/core/resources/imported/stream.js"; const assistant = ( @@ -13,8 +15,8 @@ const assistant = ( ...overrides, }); -describe("renderConversationDelta", () => { - it("prints each item once across polls: announce, then settle, then nothing", () => { +describe("diffConversation", () => { + it("emits each item once across polls: announce, then settle, then nothing", () => { const state = newStreamState(); const running = assistant({ id: "m1", @@ -30,10 +32,13 @@ describe("renderConversationDelta", () => { ], }); - const first = renderConversationDelta(state, [running]); - expect(first).toEqual([ - "✻ Choosing FastAPI.", - '→ run_shell_command {"command": "docker compose up -d"}', + expect(diffConversation(state, [running])).toEqual([ + { kind: "thinking", text: "Choosing FastAPI." }, + { + kind: "tool_start", + name: "run_shell_command", + summary: "docker compose up -d", + }, ]); const settled = assistant({ @@ -47,68 +52,129 @@ describe("renderConversationDelta", () => { }, ], } as ConversationMessage); - const second = renderConversationDelta(state, [settled]); - expect(second).toEqual([ - "The stack is up.", - "✓ run_shell_command — 3 containers started", + expect(diffConversation(state, [settled])).toEqual([ + { kind: "text", text: "The stack is up." }, + { + kind: "tool_end", + name: "run_shell_command", + ok: true, + result: "3 containers started", + }, ]); - expect(renderConversationDelta(state, [settled])).toEqual([]); + expect(diffConversation(state, [settled])).toEqual([]); }); - it("prints only the newly appended part of growing text", () => { + it("emits only the newly appended part of growing text", () => { const state = newStreamState(); - renderConversationDelta(state, [ + diffConversation(state, [ assistant({ id: "m1", content: "Scaffolding the backend." }), ]); - const delta = renderConversationDelta(state, [ - assistant({ - id: "m1", - content: "Scaffolding the backend. Now the frontend.", - }), - ]); - expect(delta).toEqual(["Now the frontend."]); + expect( + diffConversation(state, [ + assistant({ + id: "m1", + content: "Scaffolding the backend. Now the frontend.", + }), + ]), + ).toEqual([{ kind: "text", text: "Now the frontend." }]); }); - it("marks a failed tool distinctly and flattens structured results", () => { + it("marks a failed tool and flattens structured results", () => { const state = newStreamState(); - const lines = renderConversationDelta(state, [ - assistant({ - id: "m1", - tool_calls: [ - { - id: "t1", - name: "edit_repo_file", - arguments_string: null, - status: "error", - results: { error: "File not found" }, - }, - ], - }), - ]); - expect(lines).toEqual([ - "→ edit_repo_file", - '✗ edit_repo_file — {"error":"File not found"}', + expect( + diffConversation(state, [ + assistant({ + id: "m1", + tool_calls: [ + { + id: "t1", + name: "edit_repo_file", + arguments_string: '{"path": "backend/app/db.py"}', + status: "error", + results: { error: "File not found" }, + }, + ], + }), + ]), + ).toEqual([ + { + kind: "tool_start", + name: "edit_repo_file", + summary: "backend/app/db.py", + }, + { + kind: "tool_end", + name: "edit_repo_file", + ok: false, + result: '{"error":"File not found"}', + }, ]); }); it("ignores user and hidden messages", () => { const state = newStreamState(); - const lines = renderConversationDelta(state, [ - { id: "u1", role: "user", content: "add login" }, - assistant({ id: "h1", hidden: true, content: "internal" }), - ]); - expect(lines).toEqual([]); + expect( + diffConversation(state, [ + { id: "u1", role: "user", content: "add login" }, + assistant({ id: "h1", hidden: true, content: "internal" }), + ]), + ).toEqual([]); }); it("a primed state suppresses history but streams what comes after", () => { const state = newStreamState(); const history = assistant({ id: "m0", content: "Earlier turn summary." }); - renderConversationDelta(state, [history]); // prime - const lines = renderConversationDelta(state, [ - history, - assistant({ id: "m1", content: "New turn begins." }), - ]); - expect(lines).toEqual(["New turn begins."]); + diffConversation(state, [history]); // prime + expect( + diffConversation(state, [ + history, + assistant({ id: "m1", content: "New turn begins." }), + ]), + ).toEqual([{ kind: "text", text: "New turn begins." }]); + }); +}); + +describe("toolSummary", () => { + it("extracts the salient argument per tool", () => { + expect( + toolSummary("run_shell_command", '{"command":"ls -la","summary":"list"}'), + ).toBe("ls -la"); + expect( + toolSummary("write_repo_file", '{"path":"a.py","content":"…"}'), + ).toBe("a.py"); + expect( + toolSummary("create_pull_request", '{"title":"Add auth","body":"x"}'), + ).toBe("Add auth"); + }); + + it("falls back to summary, then the first string, and survives non-JSON", () => { + expect(toolSummary("set_secrets", '{"summary":"3 secrets declared"}')).toBe( + "3 secrets declared", + ); + expect(toolSummary("unknown_tool", '{"n":1,"target":"web"}')).toBe("web"); + expect(toolSummary("unknown_tool", "not json")).toBe("not json"); + }); + + it("truncates long values to one line", () => { + const long = `{"command":"${"x".repeat(200)}"}`; + expect(toolSummary("run_shell_command", long)).toHaveLength(91); // 90 + ellipsis + }); +}); + +describe("turnSettled", () => { + const user = (id: string, outcome: unknown): ConversationMessage => ({ + id, + role: "user", + content: "do it", + outcome, + }); + + it("keys off the NEWEST user message's outcome stamp", () => { + const done = user("u1", { backend_status: "pending" }); + const open = user("u2", null); + expect(turnSettled([done, assistant({ id: "m1" }), open])).toBe(false); + expect(turnSettled([open, assistant({ id: "m1" }), done])).toBe(true); + expect(turnSettled([assistant({ id: "m1" })])).toBe(false); }); }); From 5448b554c77e1dbb0811a3e72b3e8b8977181743 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 19:42:07 +0300 Subject: [PATCH 04/73] =?UTF-8?q?feat(imported):=20positional=20create=20?= =?UTF-8?q?=E2=80=94=20one=20name=20for=20directory,=20repo,=20and=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `imported create <name>` is from-scratch mode without ceremony: it makes ./<name>, links it, names the fresh GitHub repo and the app after it, and hints the cd. --blank/--repo-name keep working; a bare name with no --repo implies blank. Name is validated against directory/repo-safe characters. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 72 ++++++++++++------- packages/cli/tests/cli/imported.spec.ts | 25 +++++++ 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 202697616..cd8129dd2 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,3 +1,5 @@ +import { mkdir } from "node:fs/promises"; +import { join } from "node:path"; import { renderStreamEvent } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command, getDashboardUrl } from "@/cli/utils/index.js"; @@ -29,53 +31,66 @@ interface CreateImportedOptions { async function createImportedAction( { log, runTask, jsonMode }: CLIContext, + name: string | undefined, options: CreateImportedOptions, ): Promise<RunCommandResult> { - if (options.blank && options.repo) { + // The positional name is the whole identity: directory, GitHub repo, app. + const repoName = options.repoName ?? name; + // A bare name means "from scratch" — --blank stays for explicitness. + const blank = options.blank || (Boolean(name) && !options.repo); + if (blank && options.repo) { throw new InvalidInputError( - "--blank starts from scratch; drop --repo, or drop --blank to import that repository.", + "A from-scratch create takes no --repo; drop it, or drop --blank to import that repository.", ); } - if (options.blank && !options.repoName) { + if (blank && !repoName) { throw new InvalidInputError( - "--blank needs --repo-name <name> for the fresh GitHub repository.", + "Starting from scratch needs a name: `imported create <name>` (or --repo-name <name>).", ); } - if (!options.blank && !options.repo) { + if (!blank && !options.repo) { throw new InvalidInputError( - "Pass --repo <github-url> to import a repository, or --blank --repo-name <name> to start from scratch.", + "Pass a <name> to start from scratch, or --repo <github-url> to import a repository.", ); } - if (await appConfigExists(process.cwd())) { + if (name && !/^[A-Za-z0-9._-]+$/.test(name)) { throw new InvalidInputError( - "This directory is already linked to a Base44 app. Run the command from a fresh directory.", + "The name becomes a directory and a GitHub repository — letters, digits, dots, dashes and underscores only.", ); } - const sourceMode = options.blank ? "blank" : (options.mode ?? "direct"); + const targetDir = name ? join(process.cwd(), name) : process.cwd(); + if (name) await mkdir(targetDir, { recursive: true }); + if (await appConfigExists(targetDir)) { + throw new InvalidInputError( + name + ? `./${name} is already linked to a Base44 app. Pick another name.` + : "This directory is already linked to a Base44 app. Run the command from a fresh directory.", + ); + } + + const sourceMode = blank ? "blank" : (options.mode ?? "direct"); const appName = options.appName ?? - (options.blank - ? (options.repoName as string) + (blank + ? (repoName as string) : ((options.repo as string).replace(/\/+$/, "").split("/").pop() ?? "Imported app")); const created = await runTask( - options.blank - ? "Creating your repository and app" - : "Importing the repository", + blank ? "Creating your repository and app" : "Importing the repository", () => createImportedApp({ appName, sourceMode, repoUrl: options.repo, - newRepoName: options.repoName, + newRepoName: repoName, branch: options.fromBranch, prompt: options.prompt, }), ); - const configPath = await writeAppConfig(process.cwd(), created.id); + const configPath = await writeAppConfig(targetDir, created.id); setAppContext({ id: created.id }); let finalState: string | undefined; @@ -129,30 +144,39 @@ async function createImportedAction( log.message(`Repo: ${created.imported_repo_url}`); log.message(`Editor: ${editorUrl}`); if (previewUrl) log.message(`Preview: ${previewUrl}`); - log.message(`Linked this directory (${configPath})`); + log.message( + name + ? `Linked ./${name} (${configPath})` + : `Linked this directory (${configPath})`, + ); + const cdHint = name ? ` Next: cd ${name}` : ""; if (finalState === "error") { return { - outroMessage: - "The first build reported an error — open the editor to see what the agent hit.", + outroMessage: `The first build reported an error — open the editor to see what the agent hit.${cdHint}`, }; } if (finalState === "processing") { return { - outroMessage: - "Still building — check progress in the editor or with `base44 imported status`.", + outroMessage: `Still building — check progress in the editor or with \`base44 imported status\`.${cdHint}`, }; } return { outroMessage: options.prompt - ? "First build finished." - : "Imported app created.", + ? `First build finished.${cdHint}` + : `Imported app created.${cdHint}`, }; } export function getImportedCreateCommand(): Base44Command { const command = new Base44Command("create", { requireAppContext: false }); command - .description("Create an imported app from a GitHub repo, or from scratch") + .description( + "Create an imported app: `create <name>` starts from scratch in ./<name>, or import with --repo", + ) + .argument( + "[name]", + "One name for everything: the directory (created for you), the fresh GitHub repo, and the app", + ) .option( "--blank", "Start from scratch in a fresh private GitHub repository", diff --git a/packages/cli/tests/cli/imported.spec.ts b/packages/cli/tests/cli/imported.spec.ts index 2d0fe463b..c71443008 100644 --- a/packages/cli/tests/cli/imported.spec.ts +++ b/packages/cli/tests/cli/imported.spec.ts @@ -168,6 +168,31 @@ describe("imported", () => { expect(JSON.parse(result.stdout)).toEqual({ queued: true }); }); + it("create <name> is blank mode: one name for repo, app, and directory", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + let sentBody: Record<string, unknown> | undefined; + t.api.mockRoute("POST", "/api/apps", (req, res) => { + sentBody = req.body as Record<string, unknown>; + return res.json({ + id: "new-app-2", + name: "recipe-box-4", + imported_repo_url: "https://github.com/tester/recipe-box-4", + }); + }); + const result = await t.run("imported", "create", "recipe-box-4", "--json"); + t.expectResult(result).toSucceed(); + expect(sentBody).toMatchObject({ + app_type: "imported_app", + imported_source_mode: "blank", + imported_new_repo_name: "recipe-box-4", + name: "recipe-box-4", + }); + expect(JSON.parse(result.stdout)).toMatchObject({ id: "new-app-2" }); + + const badName = await t.run("imported", "create", "no/slashes", "--json"); + t.expectResult(badName).toFail(); + }); + it("create --blank requires a repo name and sends the blank payload", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); From dd31b243e465342863c6ca226bf565fbffd7763d Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 19:44:30 +0300 Subject: [PATCH 05/73] fix(imported): a turn settles on a TERMINAL outcome, not the pending stamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MessageOutcome is written with backend_status="pending" at turn START — the previous check settled on the first poll, before the assistant message even existed (no stream, instant 'First build finished'). Settle now requires a terminal backend_status, which also keeps the stream alive through post-turn auto-fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/core/resources/imported/stream.ts | 16 ++++++++++++---- packages/cli/tests/core/imported-stream.spec.ts | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index 4b6458bc6..650e2a6d1 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -140,15 +140,23 @@ export function diffConversation( } /** - * Whether the newest user message's turn has finished: the backend stamps - * `outcome` onto the turn's user message at end-of-loop, on success and error - * alike. Authoritative, unlike the app's status field, which flaps mid-turn. + * Whether the newest user message's turn has finished. The backend stamps + * `outcome` onto the turn's user message with backend_status "pending" at turn + * START and flips it to a terminal value (success_build, error_build, + * error_backend, stopped, success_no_generation) at end-of-loop — through + * auto-fix, whose activity we keep streaming meanwhile. Authoritative, unlike + * the app's status field, which flaps mid-turn. */ export function turnSettled(messages: ConversationMessage[]): boolean { for (let i = messages.length - 1; i >= 0; i--) { const message = messages[i]; if (message.role === "user" && !message.hidden) { - return message.outcome != null; + const outcome = message.outcome as { backend_status?: string } | null; + return ( + outcome != null && + typeof outcome === "object" && + outcome.backend_status !== "pending" + ); } } return false; diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 580b13fa1..e19537a38 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -170,11 +170,17 @@ describe("turnSettled", () => { outcome, }); - it("keys off the NEWEST user message's outcome stamp", () => { - const done = user("u1", { backend_status: "pending" }); + it("keys off the NEWEST user message's TERMINAL outcome", () => { + const done = user("u1", { backend_status: "success_build" }); const open = user("u2", null); + // outcome is stamped "pending" at turn START — that must not read as done. + const started = user("u3", { backend_status: "pending" }); expect(turnSettled([done, assistant({ id: "m1" }), open])).toBe(false); + expect(turnSettled([done, assistant({ id: "m1" }), started])).toBe(false); expect(turnSettled([open, assistant({ id: "m1" }), done])).toBe(true); + expect(turnSettled([user("u4", { backend_status: "error_build" })])).toBe( + true, + ); expect(turnSettled([assistant({ id: "m1" })])).toBe(false); }); }); From 323e9d9b3c75a20c21941eda64c8d23f51edb466 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 19:56:50 +0300 Subject: [PATCH 06/73] =?UTF-8?q?feat(imported):=20agentic=20turn=20UX=20?= =?UTF-8?q?=E2=80=94=20live=20status=20line,=20aliases,=20compact=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stream now reads like an agent transcript: a single spinner status line at the bottom shows what is running right now (aliased tool + salient arg + elapsed seconds, '+N more' for parallel calls) while settled items print as compact one-liners above it — ✓ write path, ✓ bash cmd with a dim one-line result, ✗ in red with the error. Tool names are aliased (run_shell_command→ bash, *_repo_file→read/write/edit, …) and quiet tools (read/write/edit/ reload) print no result on success. Poll interval down to 1s. Non-TTY and --json keep plain output with no cursor codes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 17 ++- .../cli/src/cli/commands/imported/create.ts | 20 ++- .../cli/src/cli/commands/imported/render.ts | 135 ++++++++++++++++-- .../cli/src/core/resources/imported/stream.ts | 29 ++-- .../cli/tests/core/imported-stream.spec.ts | 68 +++++++++ 5 files changed, 234 insertions(+), 35 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 604e6dba2..a592e87cb 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,4 +1,4 @@ -import { renderStreamEvent } from "@/cli/commands/imported/render.js"; +import { createTurnStream } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import type { ImportedChatTurn } from "@/core/resources/imported/api.js"; @@ -44,11 +44,16 @@ async function chatAction( ); } else { log.message("Agent working — live from the sandbox:"); - turn = await streamConversationDuring( - () => sendImportedChatMessage(message, branchId), - (event) => log.message(renderStreamEvent(event)), - { branchId }, - ); + const stream = createTurnStream(process.stdout.isTTY === true); + try { + turn = await streamConversationDuring( + () => sendImportedChatMessage(message, branchId), + stream.onEvent, + { branchId }, + ); + } finally { + stream.stop(); + } } if (turn.queued) { diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index cd8129dd2..68731d36c 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,6 +1,6 @@ import { mkdir } from "node:fs/promises"; import { join } from "node:path"; -import { renderStreamEvent } from "@/cli/commands/imported/render.js"; +import { createTurnStream } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command, getDashboardUrl } from "@/cli/utils/index.js"; import { InvalidInputError } from "@/core/errors.js"; @@ -105,12 +105,18 @@ async function createImportedAction( "Agent is building — live (several minutes; safe to Ctrl+C, the build continues):", ); } - const settled = await streamConversationUntilSettled( - (event) => { - if (!jsonMode) log.message(renderStreamEvent(event)); - }, - { branchId, timeoutMs: POLL_TIMEOUT_MS }, - ); + const stream = createTurnStream(!jsonMode && process.stdout.isTTY === true); + let settled: "settled" | "timeout"; + try { + settled = await streamConversationUntilSettled( + (event) => { + if (!jsonMode) stream.onEvent(event); + }, + { branchId, timeoutMs: POLL_TIMEOUT_MS }, + ); + } finally { + stream.stop(); + } finalState = settled === "timeout" ? "processing" diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index 48c8add8a..d29e1651a 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -1,24 +1,131 @@ -import { theme } from "@/cli/utils/index.js"; +import chalk from "chalk"; import type { StreamEvent } from "@/core/resources/imported/stream.js"; -/** One styled terminal line per stream event, editor-transcript style. */ -export function renderStreamEvent(event: StreamEvent): string { +const TOOL_ALIASES: Record<string, string> = { + run_shell_command: "bash", + read_repo_file: "read", + write_repo_file: "write", + edit_repo_file: "edit", + set_secrets: "secrets", + generate_development_secrets: "secrets", + create_pull_request: "pr", + merge_pull_request: "merge", + list_pr_threads: "pr threads", + reply_to_pr_thread: "pr reply", + comment_on_pr: "pr comment", + resolve_pr_thread: "pr resolve", + reload_preview: "reload", + preview_execute_code: "preview js", + preview_screenshot: "screenshot", + connect_github_account: "github", +}; + +// Verbose result text adds nothing for these; the path in the summary does. +const QUIET_OK_RESULTS = new Set(["read", "write", "edit", "reload"]); + +function toolAlias(name: string): string { + return TOOL_ALIASES[name] ?? name; +} + +/** + * The finished line for an event, or null when it only affects the live + * status (a tool starting). Plain string + chalk; no layout gutter. + */ +export function eventLine(event: StreamEvent): string | null { switch (event.kind) { case "thinking": - return theme.styles.dim(`✻ ${event.text}`); + return chalk.dim(`✻ ${event.text}`); case "text": return event.text; - case "tool_start": { - const name = theme.styles.info(event.name); - return event.summary - ? `${theme.styles.dim("●")} ${name} ${theme.styles.dim(event.summary)}` - : `${theme.styles.dim("●")} ${name}`; - } + case "tool_start": + return null; case "tool_end": { - const mark = event.ok - ? theme.styles.dim(" ↳ ok") - : theme.styles.error(" ↳ failed"); - return event.result ? `${mark} ${theme.styles.dim(event.result)}` : mark; + const alias = toolAlias(event.name); + const head = event.ok + ? `${chalk.green("✓")} ${chalk.bold(alias)}` + : `${chalk.red("✗")} ${chalk.bold(alias)}`; + const summary = event.summary ? ` ${chalk.dim(event.summary)}` : ""; + if (event.ok && (QUIET_OK_RESULTS.has(alias) || !event.result)) { + return `${head}${summary}`; + } + const result = event.ok + ? chalk.dim(event.result) + : chalk.red(event.result); + return `${head}${summary}${event.result ? `\n ${result}` : ""}`; } } } + +const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; + +interface RunningTool { + alias: string; + summary: string; + startedAt: number; +} + +export interface TurnStream { + onEvent: (event: StreamEvent) => void; + stop: () => void; +} + +/** + * Claude-Code-style turn view: completed items print as compact lines while a + * single live status line at the bottom shows the spinner and whatever is + * running right now (with elapsed seconds). Non-interactive mode skips the + * status line and just prints settled lines. + */ +export function createTurnStream( + interactive: boolean, + write: (text: string) => void = (text) => process.stdout.write(text), +): TurnStream { + const running = new Map<string, RunningTool>(); + let frame = 0; + let stopped = false; + + const statusLabel = (): string => { + if (running.size === 0) return "waiting for the agent…"; + const newest = [...running.values()].at(-1) as RunningTool; + const elapsed = Math.round((Date.now() - newest.startedAt) / 1000); + const others = running.size > 1 ? ` (+${running.size - 1} more)` : ""; + const summary = newest.summary ? ` ${newest.summary}` : ""; + return `${newest.alias}${summary}${others} · ${elapsed}s`; + }; + + const drawStatus = () => { + if (!interactive || stopped) return; + frame = (frame + 1) % FRAMES.length; + write(`\r\x1b[2K${chalk.dim(`${FRAMES[frame]} ${statusLabel()}`)}`); + }; + + const timer = interactive ? setInterval(drawStatus, 120) : null; + if (timer) timer.unref?.(); + + return { + onEvent(event: StreamEvent) { + if (event.kind === "tool_start") { + running.set(event.id, { + alias: toolAlias(event.name), + summary: event.summary, + startedAt: Date.now(), + }); + drawStatus(); + return; + } + if (event.kind === "tool_end") running.delete(event.id); + const line = eventLine(event); + if (line == null) return; + if (interactive) { + write(`\r\x1b[2K${line}\n`); + drawStatus(); + } else { + write(`${line}\n`); + } + }, + stop() { + stopped = true; + if (timer) clearInterval(timer); + if (interactive) write("\r\x1b[2K"); + }, + }; +} diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index 650e2a6d1..d894e70cb 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -4,13 +4,20 @@ import { getFullConversation } from "@/core/resources/imported/api.js"; export type StreamEvent = | { kind: "thinking"; text: string } | { kind: "text"; text: string } - | { kind: "tool_start"; name: string; summary: string } - | { kind: "tool_end"; name: string; ok: boolean; result: string }; + | { kind: "tool_start"; id: string; name: string; summary: string } + | { + kind: "tool_end"; + id: string; + name: string; + summary: string; + ok: boolean; + result: string; + }; interface MessageProgress { contentLength: number; reasoningLength: number; - announcedTools: Set<string>; + announcedTools: Map<string, string>; // tool id -> summary settledTools: Set<string>; } @@ -77,7 +84,7 @@ function progressFor(state: StreamState, id: string): MessageProgress { progress = { contentLength: 0, reasoningLength: 0, - announcedTools: new Set(), + announcedTools: new Map(), settledTools: new Set(), }; state.perMessage.set(id, progress); @@ -117,11 +124,15 @@ export function diffConversation( for (const tool of message.tool_calls ?? []) { if (!progress.announcedTools.has(tool.id)) { - progress.announcedTools.add(tool.id); + progress.announcedTools.set( + tool.id, + toolSummary(tool.name, tool.arguments_string), + ); events.push({ kind: "tool_start", + id: tool.id, name: tool.name, - summary: toolSummary(tool.name, tool.arguments_string), + summary: progress.announcedTools.get(tool.id) ?? "", }); } const status = tool.status ?? "running"; @@ -129,7 +140,9 @@ export function diffConversation( progress.settledTools.add(tool.id); events.push({ kind: "tool_end", + id: tool.id, name: tool.name, + summary: progress.announcedTools.get(tool.id) ?? "", ok: status === "success", result: oneLine(tool.results, 110), }); @@ -198,7 +211,7 @@ export async function streamConversationDuring<T>( onEvent: (event: StreamEvent) => void, options: StreamOptions = {}, ): Promise<T> { - const intervalMs = options.intervalMs ?? 2_000; + const intervalMs = options.intervalMs ?? 1_000; const poll = makePoller(onEvent, options); await poll(true); const work = start(); @@ -228,7 +241,7 @@ export async function streamConversationUntilSettled( onEvent: (event: StreamEvent) => void, options: StreamOptions & { timeoutMs?: number } = {}, ): Promise<"settled" | "timeout"> { - const intervalMs = options.intervalMs ?? 2_000; + const intervalMs = options.intervalMs ?? 1_000; const deadline = Date.now() + (options.timeoutMs ?? 20 * 60_000); const poll = makePoller(onEvent, options); while (Date.now() < deadline) { diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index e19537a38..b378f088d 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -1,4 +1,6 @@ +import stripAnsi from "strip-ansi"; import { describe, expect, it } from "vitest"; +import { createTurnStream, eventLine } from "@/cli/commands/imported/render.js"; import type { ConversationMessage } from "@/core/resources/imported/api.js"; import { diffConversation, @@ -36,6 +38,7 @@ describe("diffConversation", () => { { kind: "thinking", text: "Choosing FastAPI." }, { kind: "tool_start", + id: "t1", name: "run_shell_command", summary: "docker compose up -d", }, @@ -56,7 +59,9 @@ describe("diffConversation", () => { { kind: "text", text: "The stack is up." }, { kind: "tool_end", + id: "t1", name: "run_shell_command", + summary: "docker compose up -d", ok: true, result: "3 containers started", }, @@ -100,12 +105,15 @@ describe("diffConversation", () => { ).toEqual([ { kind: "tool_start", + id: "t1", name: "edit_repo_file", summary: "backend/app/db.py", }, { kind: "tool_end", + id: "t1", name: "edit_repo_file", + summary: "backend/app/db.py", ok: false, result: '{"error":"File not found"}', }, @@ -162,6 +170,66 @@ describe("toolSummary", () => { }); }); +describe("render", () => { + it("aliases tool names and keeps quiet on boring ok results", () => { + expect( + stripAnsi( + eventLine({ + kind: "tool_end", + id: "t1", + name: "write_repo_file", + summary: "frontend/src/App.jsx", + ok: true, + result: "Wrote frontend/src/App.jsx", + }) ?? "", + ), + ).toBe("✓ write frontend/src/App.jsx"); + expect( + stripAnsi( + eventLine({ + kind: "tool_end", + id: "t2", + name: "run_shell_command", + summary: "docker compose ps", + ok: true, + result: "3 containers running", + }) ?? "", + ), + ).toBe("✓ bash docker compose ps\n 3 containers running"); + expect( + eventLine({ + kind: "tool_start", + id: "t3", + name: "run_shell_command", + summary: "ls", + }), + ).toBeNull(); + }); + + it("non-interactive stream prints settled lines only, no ANSI cursor codes", () => { + const out: string[] = []; + const stream = createTurnStream(false, (text) => out.push(text)); + stream.onEvent({ + kind: "tool_start", + id: "t1", + name: "write_repo_file", + summary: "a.py", + }); + stream.onEvent({ + kind: "tool_end", + id: "t1", + name: "write_repo_file", + summary: "a.py", + ok: true, + result: "Wrote a.py", + }); + stream.stop(); + const joined = stripAnsi(out.join("")); + expect(joined).toBe("✓ write a.py\n"); + expect(out.join("")).not.toContain("\r"); + }); +}); + describe("turnSettled", () => { const user = (id: string, outcome: unknown): ConversationMessage => ({ id, From eadbabf1274e314e56e0e4bc02eacd32c70629a7 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 19:57:35 +0300 Subject: [PATCH 07/73] chore: unexport internal TurnStream interface (knip) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/render.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index d29e1651a..ef82aabfd 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -64,7 +64,7 @@ interface RunningTool { startedAt: number; } -export interface TurnStream { +interface TurnStream { onEvent: (event: StreamEvent) => void; stop: () => void; } From 4988c4f0ba40b34bb97a9fa92adff2213c8dbb08 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 20:03:47 +0300 Subject: [PATCH 08/73] feat(imported): iteration mode after runs + clickable preview + editor deep link After create --prompt or chat finishes (TTY), the CLI stays in the session and keeps taking prompts, each run as a streamed turn on the same working branch; empty input or cancel ends it. The preview URL gets its https:// scheme (imported apps return a bare proxied host), and the editor link now lands on the preview tab (/editor/preview) instead of the workspace overview. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 9 +++- .../cli/src/cli/commands/imported/create.ts | 13 +++++- .../cli/src/cli/commands/imported/iterate.ts | 46 +++++++++++++++++++ .../cli/src/core/resources/imported/api.ts | 9 +++- 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 packages/cli/src/cli/commands/imported/iterate.ts diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index a592e87cb..82fc2f426 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,3 +1,4 @@ +import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; import { createTurnStream } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; @@ -73,7 +74,13 @@ async function chatAction( })}\n`, }; } - return { outroMessage: turnOutro(turn) }; + + log.message(turnOutro(turn)); + // Stay in the session: keep taking prompts on the same working branch. + if (process.stdout.isTTY === true) { + await runIterationLoop(log, branchId); + } + return { outroMessage: "Session ended." }; } export function getImportedChatCommand(): Base44Command { diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 68731d36c..a5f1ef52f 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,8 +1,10 @@ import { mkdir } from "node:fs/promises"; import { join } from "node:path"; +import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; import { createTurnStream } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; -import { Base44Command, getDashboardUrl } from "@/cli/utils/index.js"; +import { Base44Command } from "@/cli/utils/index.js"; +import { getBase44ApiUrl } from "@/core/config.js"; import { InvalidInputError } from "@/core/errors.js"; import { appConfigExists, @@ -95,11 +97,13 @@ async function createImportedAction( let finalState: string | undefined; let previewUrl: string | undefined; + let workBranchId: string | undefined; if (options.prompt) { // The kickoff turn runs on the app's setup branch conversation. Completion // is the outcome stamp on the turn's user message — the app status field // flaps mid-turn and cannot be trusted. const branchId = await soleActiveBranchId().catch(() => undefined); + workBranchId = branchId; if (!jsonMode) { log.message( "Agent is building — live (several minutes; safe to Ctrl+C, the build continues):", @@ -132,7 +136,7 @@ async function createImportedAction( } } - const editorUrl = getDashboardUrl(created.id); + const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; if (jsonMode) { return { stdout: `${JSON.stringify({ @@ -155,6 +159,11 @@ async function createImportedAction( ? `Linked ./${name} (${configPath})` : `Linked this directory (${configPath})`, ); + + // Stay in the session: keep taking prompts on the same working branch. + if (options.prompt && process.stdout.isTTY === true) { + await runIterationLoop(log, workBranchId); + } const cdHint = name ? ` Next: cd ${name}` : ""; if (finalState === "error") { return { diff --git a/packages/cli/src/cli/commands/imported/iterate.ts b/packages/cli/src/cli/commands/imported/iterate.ts new file mode 100644 index 000000000..09033c100 --- /dev/null +++ b/packages/cli/src/cli/commands/imported/iterate.ts @@ -0,0 +1,46 @@ +import { isCancel, text } from "@clack/prompts"; +import { createTurnStream } from "@/cli/commands/imported/render.js"; +import type { CLIContext } from "@/cli/types.js"; +import { sendImportedChatMessage } from "@/core/resources/imported/api.js"; +import { streamConversationDuring } from "@/core/resources/imported/stream.js"; + +/** + * Post-run iteration mode: keep taking prompts and running streamed turns on + * the same branch until the user submits nothing (or cancels). TTY only — + * callers gate on interactivity. + */ +export async function runIterationLoop( + log: CLIContext["log"], + branchId: string | undefined, +): Promise<void> { + for (;;) { + const reply = await text({ + message: "What next? (Enter with no text to finish)", + placeholder: "e.g. add user login with sessions", + defaultValue: "", + }); + if (isCancel(reply) || !String(reply ?? "").trim()) return; + + const stream = createTurnStream(process.stdout.isTTY === true); + let turn: Awaited<ReturnType<typeof sendImportedChatMessage>>; + try { + turn = await streamConversationDuring( + () => sendImportedChatMessage(String(reply).trim(), branchId), + stream.onEvent, + { branchId }, + ); + } finally { + stream.stop(); + } + if (turn.queued) { + log.message("Queued behind an earlier message — it will run next."); + continue; + } + const state = turn.status?.state ?? "ready"; + log.message( + state === "error" + ? `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details.` + : "Turn finished.", + ); + } +} diff --git a/packages/cli/src/core/resources/imported/api.ts b/packages/cli/src/core/resources/imported/api.ts index f90aaf17e..1d3470c59 100644 --- a/packages/cli/src/core/resources/imported/api.ts +++ b/packages/cli/src/core/resources/imported/api.ts @@ -295,6 +295,11 @@ export async function getImportedPreviewUrl(): Promise<string> { } catch (error) { throw await ApiError.fromHttpError(error, "fetching preview URL"); } - return parseOrThrow(PreviewUrlSchema, await response.json(), "preview URL") - .preview_url; + const url = parseOrThrow( + PreviewUrlSchema, + await response.json(), + "preview URL", + ).preview_url; + // Imported apps get a bare proxied host back — make it clickable. + return /^https?:\/\//.test(url) ? url : `https://${url}`; } From 13d4bd3093c2dbb329ac3295e3c4ce232478614f Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 20:10:00 +0300 Subject: [PATCH 09/73] feat(imported): rotating idle musings on the status line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quiet gaps between agent rounds now show a rotating gerund (Shmoozing…, Percolating…, Noodling…) instead of a static waiting label — random start, new word every 6s; a running tool still takes over the line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/render.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index ef82aabfd..eed885e7a 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -58,6 +58,35 @@ export function eventLine(event: StreamEvent): string | null { const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; +// Idle-gap gerunds, one at a time, rotating every few seconds. +const MUSINGS = [ + "Shmoozing", + "Shmoogling", + "Percolating", + "Noodling", + "Marinating", + "Brewing", + "Simmering", + "Conjuring", + "Tinkering", + "Scheming", + "Pondering", + "Mulling", + "Whirring", + "Crunching", + "Weaving", + "Sketching", + "Hatching", + "Riffing", + "Cooking", + "Composting ideas", + "Rummaging", + "Vibing responsibly", + "Untangling", + "Squinting at the repo", +]; +const MUSING_ROTATE_MS = 6_000; + interface RunningTool { alias: string; summary: string; @@ -82,9 +111,15 @@ export function createTurnStream( const running = new Map<string, RunningTool>(); let frame = 0; let stopped = false; + const musingSeed = Math.floor(Math.random() * MUSINGS.length); const statusLabel = (): string => { - if (running.size === 0) return "waiting for the agent…"; + if (running.size === 0) { + const index = + (musingSeed + Math.floor(Date.now() / MUSING_ROTATE_MS)) % + MUSINGS.length; + return `${MUSINGS[index]}…`; + } const newest = [...running.values()].at(-1) as RunningTool; const elapsed = Math.round((Date.now() - newest.startedAt) / 1000); const others = running.size > 1 ? ` (+${running.size - 1} more)` : ""; From a2597a12075905969151fd39625233dabb53dae1 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 20:44:13 +0300 Subject: [PATCH 10/73] =?UTF-8?q?feat(imported):=20compact=20create=20outp?= =?UTF-8?q?ut=20=E2=80=94=20identity=20up=20front,=20agent=20keeps=20the?= =?UTF-8?q?=20close?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repo/editor/linked lines print dimmed right after creation (the editor is usable during the build); the end of the turn adds only the preview URL, so the agent's final message keeps the attention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index a5f1ef52f..3625a19d6 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,5 +1,6 @@ import { mkdir } from "node:fs/promises"; import { join } from "node:path"; +import chalk from "chalk"; import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; import { createTurnStream } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; @@ -95,6 +96,18 @@ async function createImportedAction( const configPath = await writeAppConfig(targetDir, created.id); setAppContext({ id: created.id }); + // Identity up front, dimmed — the editor is usable while the build runs, and + // the end of the turn stays with the agent's closing message. + const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; + if (!jsonMode) { + if (created.imported_repo_url) + log.message(chalk.dim(`repo ${created.imported_repo_url}`)); + log.message(chalk.dim(`editor ${editorUrl}`)); + log.message( + chalk.dim(name ? `linked ./${name}` : `linked ${configPath}`), + ); + } + let finalState: string | undefined; let previewUrl: string | undefined; let workBranchId: string | undefined; @@ -104,11 +117,6 @@ async function createImportedAction( // flaps mid-turn and cannot be trusted. const branchId = await soleActiveBranchId().catch(() => undefined); workBranchId = branchId; - if (!jsonMode) { - log.message( - "Agent is building — live (several minutes; safe to Ctrl+C, the build continues):", - ); - } const stream = createTurnStream(!jsonMode && process.stdout.isTTY === true); let settled: "settled" | "timeout"; try { @@ -136,7 +144,6 @@ async function createImportedAction( } } - const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; if (jsonMode) { return { stdout: `${JSON.stringify({ @@ -149,16 +156,7 @@ async function createImportedAction( }; } - log.message(`App: ${created.id}`); - if (created.imported_repo_url) - log.message(`Repo: ${created.imported_repo_url}`); - log.message(`Editor: ${editorUrl}`); - if (previewUrl) log.message(`Preview: ${previewUrl}`); - log.message( - name - ? `Linked ./${name} (${configPath})` - : `Linked this directory (${configPath})`, - ); + if (previewUrl) log.message(`preview ${previewUrl}`); // Stay in the session: keep taking prompts on the same working branch. if (options.prompt && process.stdout.isTTY === true) { From b4cb9dd45ba561bed7b462096d0e4c8c8018ce2b Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 20:50:30 +0300 Subject: [PATCH 11/73] feat(imported): sticky link footer + truncated-args salvage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repo/editor(/preview) links ride as a pinned footer under the live stream — always visible and clickable while the agent works — and are printed permanently when the stream ends, including through iteration-mode turns. Tool summaries now salvage the salient key (path/command/title) out of arguments JSON the wire truncated mid-string, so a big write shows the file path instead of a raw half-JSON blob. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 19 +++++-- .../cli/src/cli/commands/imported/create.ts | 24 ++++++--- .../cli/src/cli/commands/imported/iterate.ts | 5 +- .../cli/src/cli/commands/imported/render.ts | 54 +++++++++++++++---- .../cli/src/core/resources/imported/stream.ts | 17 +++++- .../cli/tests/core/imported-stream.spec.ts | 17 ++++++ 6 files changed, 114 insertions(+), 22 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 82fc2f426..fac0201c0 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,7 +1,10 @@ +import chalk from "chalk"; import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; import { createTurnStream } from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; +import { getBase44ApiUrl } from "@/core/config.js"; +import { getAppContext } from "@/core/project/app-config.js"; import type { ImportedChatTurn } from "@/core/resources/imported/api.js"; import { sendImportedChatMessage, @@ -21,6 +24,15 @@ function lastAssistantReply(turn: ImportedChatTurn): string | undefined { return undefined; } +function chatFooter(): string[] { + try { + const editorUrl = `${getBase44ApiUrl()}/apps/${getAppContext().id}/editor/preview`; + return [chalk.dim(`editor ${editorUrl}`)]; + } catch { + return []; + } +} + function turnOutro(turn: ImportedChatTurn): string { const state = turn.status?.state ?? "ready"; if (state === "error") { @@ -44,8 +56,9 @@ async function chatAction( sendImportedChatMessage(message, branchId), ); } else { - log.message("Agent working — live from the sandbox:"); - const stream = createTurnStream(process.stdout.isTTY === true); + const stream = createTurnStream(process.stdout.isTTY === true, undefined, { + footer: chatFooter(), + }); try { turn = await streamConversationDuring( () => sendImportedChatMessage(message, branchId), @@ -78,7 +91,7 @@ async function chatAction( log.message(turnOutro(turn)); // Stay in the session: keep taking prompts on the same working branch. if (process.stdout.isTTY === true) { - await runIterationLoop(log, branchId); + await runIterationLoop(log, branchId, chatFooter()); } return { outroMessage: "Session ended." }; } diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 3625a19d6..f0d6d8002 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -96,13 +96,19 @@ async function createImportedAction( const configPath = await writeAppConfig(targetDir, created.id); setAppContext({ id: created.id }); - // Identity up front, dimmed — the editor is usable while the build runs, and - // the end of the turn stays with the agent's closing message. + // The links ride as a sticky footer under the stream (always clickable) and + // are printed permanently when it ends; non-interactive output gets them up + // front instead. const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; + const interactive = !jsonMode && process.stdout.isTTY === true; + const footer = [ + ...(created.imported_repo_url + ? [chalk.dim(`repo ${created.imported_repo_url}`)] + : []), + chalk.dim(`editor ${editorUrl}`), + ]; if (!jsonMode) { - if (created.imported_repo_url) - log.message(chalk.dim(`repo ${created.imported_repo_url}`)); - log.message(chalk.dim(`editor ${editorUrl}`)); + if (!interactive) for (const line of footer) log.message(line); log.message( chalk.dim(name ? `linked ./${name}` : `linked ${configPath}`), ); @@ -117,7 +123,7 @@ async function createImportedAction( // flaps mid-turn and cannot be trusted. const branchId = await soleActiveBranchId().catch(() => undefined); workBranchId = branchId; - const stream = createTurnStream(!jsonMode && process.stdout.isTTY === true); + const stream = createTurnStream(interactive, undefined, { footer }); let settled: "settled" | "timeout"; try { settled = await streamConversationUntilSettled( @@ -160,7 +166,11 @@ async function createImportedAction( // Stay in the session: keep taking prompts on the same working branch. if (options.prompt && process.stdout.isTTY === true) { - await runIterationLoop(log, workBranchId); + const sessionFooter = [ + ...footer, + ...(previewUrl ? [chalk.dim(`preview ${previewUrl}`)] : []), + ]; + await runIterationLoop(log, workBranchId, sessionFooter); } const cdHint = name ? ` Next: cd ${name}` : ""; if (finalState === "error") { diff --git a/packages/cli/src/cli/commands/imported/iterate.ts b/packages/cli/src/cli/commands/imported/iterate.ts index 09033c100..51731d0f5 100644 --- a/packages/cli/src/cli/commands/imported/iterate.ts +++ b/packages/cli/src/cli/commands/imported/iterate.ts @@ -12,6 +12,7 @@ import { streamConversationDuring } from "@/core/resources/imported/stream.js"; export async function runIterationLoop( log: CLIContext["log"], branchId: string | undefined, + footer?: string[], ): Promise<void> { for (;;) { const reply = await text({ @@ -21,7 +22,9 @@ export async function runIterationLoop( }); if (isCancel(reply) || !String(reply ?? "").trim()) return; - const stream = createTurnStream(process.stdout.isTTY === true); + const stream = createTurnStream(process.stdout.isTTY === true, undefined, { + footer, + }); let turn: Awaited<ReturnType<typeof sendImportedChatMessage>>; try { turn = await streamConversationDuring( diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index eed885e7a..4fbffd66e 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -98,19 +98,30 @@ interface TurnStream { stop: () => void; } +interface TurnStreamOptions { + /** Lines pinned under the stream (repo/editor/preview links) — always the + * bottom of the terminal while streaming, printed permanently on stop. Keep + * each line under a typical terminal width: a soft-wrapped footer line + * breaks the redraw arithmetic. */ + footer?: string[]; +} + /** * Claude-Code-style turn view: completed items print as compact lines while a - * single live status line at the bottom shows the spinner and whatever is - * running right now (with elapsed seconds). Non-interactive mode skips the - * status line and just prints settled lines. + * live block at the bottom shows the pinned footer links and a spinner status + * line (running tool + elapsed seconds). Non-interactive mode skips the live + * block and just prints settled lines. */ export function createTurnStream( interactive: boolean, write: (text: string) => void = (text) => process.stdout.write(text), + options: TurnStreamOptions = {}, ): TurnStream { const running = new Map<string, RunningTool>(); + const footer = options.footer ?? []; let frame = 0; let stopped = false; + let drawnLines = 0; const musingSeed = Math.floor(Math.random() * MUSINGS.length); const statusLabel = (): string => { @@ -127,13 +138,28 @@ export function createTurnStream( return `${newest.alias}${summary}${others} · ${elapsed}s`; }; - const drawStatus = () => { + const clearBlock = () => { + if (!drawnLines) return; + write("\r\x1b[2K"); + for (let i = 1; i < drawnLines; i++) write("\x1b[1A\r\x1b[2K"); + drawnLines = 0; + }; + + const drawBlock = () => { + if (!interactive || stopped) return; + const lines = [...footer, chalk.dim(`${FRAMES[frame]} ${statusLabel()}`)]; + write(lines.join("\n")); + drawnLines = lines.length; + }; + + const tick = () => { if (!interactive || stopped) return; frame = (frame + 1) % FRAMES.length; - write(`\r\x1b[2K${chalk.dim(`${FRAMES[frame]} ${statusLabel()}`)}`); + clearBlock(); + drawBlock(); }; - const timer = interactive ? setInterval(drawStatus, 120) : null; + const timer = interactive ? setInterval(tick, 120) : null; if (timer) timer.unref?.(); return { @@ -144,23 +170,31 @@ export function createTurnStream( summary: event.summary, startedAt: Date.now(), }); - drawStatus(); + if (interactive) { + clearBlock(); + drawBlock(); + } return; } if (event.kind === "tool_end") running.delete(event.id); const line = eventLine(event); if (line == null) return; if (interactive) { - write(`\r\x1b[2K${line}\n`); - drawStatus(); + clearBlock(); + write(`${line}\n`); + drawBlock(); } else { write(`${line}\n`); } }, stop() { + if (interactive) { + clearBlock(); + // The links outlive the stream — leave them printed for clicking. + if (footer.length) write(`${footer.join("\n")}\n`); + } stopped = true; if (timer) clearInterval(timer); - if (interactive) write("\r\x1b[2K"); }, }; } diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index d894e70cb..55dd8922c 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -42,6 +42,20 @@ function oneLine(value: unknown, max: number): string { return flat.length > max ? `${flat.slice(0, max)}…` : flat; } +const SALIENT_KEYS = ["command", "path", "file_path", "title", "summary"]; + +/** Pull a salient value out of TRUNCATED arguments JSON (the wire cuts big + * payloads mid-string, so JSON.parse fails while the key we want survived). */ +function salvageFromTruncated(raw: string): string | undefined { + for (const key of SALIENT_KEYS) { + const match = raw.match( + new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`), + ); + if (match?.[1]) return match[1].replace(/\\(.)/g, "$1"); + } + return undefined; +} + /** The one argument a human wants to see for each tool, not the JSON blob. */ export function toolSummary( name: string, @@ -54,7 +68,8 @@ export function toolSummary( args = parsed as Record<string, unknown>; } } catch { - return oneLine(argumentsString ?? "", 90); + const salvaged = salvageFromTruncated(argumentsString ?? ""); + return oneLine(salvaged ?? argumentsString ?? "", 90); } const pick = (key: string): string | undefined => typeof args[key] === "string" && (args[key] as string).trim() diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index b378f088d..73140de87 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -164,6 +164,23 @@ describe("toolSummary", () => { expect(toolSummary("unknown_tool", "not json")).toBe("not json"); }); + it("salvages the salient key from truncated arguments JSON", () => { + // Big payloads arrive cut mid-string on the wire — JSON.parse fails, but + // the path key survived and must render instead of the raw blob. + expect( + toolSummary( + "write_repo_file", + '{"file_path": "the-sewer-vault/src/pages/shop.astro", "content": "<html>… trunc', + ), + ).toBe("the-sewer-vault/src/pages/shop.astro"); + expect( + toolSummary( + "run_shell_command", + '{"command": "docker compose up -d", "summary": "boot the st', + ), + ).toBe("docker compose up -d"); + }); + it("truncates long values to one line", () => { const long = `{"command":"${"x".repeat(200)}"}`; expect(toolSummary("run_shell_command", long)).toHaveLength(91); // 90 + ellipsis From af68599e078e7b86757bc95cd3541e26b0f5b5c8 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 20:58:11 +0300 Subject: [PATCH 12/73] feat(imported): editor-parity tool titles, per-tool durations, turn totals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every builder tool call carries a human title in its `summary` argument — the same one the editor shows ('Confirmed Wix login', 'Built plan') — and the stream now leads with it: ✓ Title alias: salient-arg · 12s, with the raw argument as dim detail and a duration when a call ran 3s or longer. The spinner shows the running tool's title too. Turn ends print the total (Turn finished · 4m 32s; First build finished · 6m 02s). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 13 ++- .../cli/src/cli/commands/imported/create.ts | 13 ++- .../cli/src/cli/commands/imported/iterate.ts | 12 +- .../cli/src/cli/commands/imported/render.ts | 54 +++++++-- .../cli/src/core/resources/imported/stream.ts | 66 +++++++---- .../cli/tests/core/imported-stream.spec.ts | 105 ++++++++++++------ 6 files changed, 188 insertions(+), 75 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index fac0201c0..9eb9114ee 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,6 +1,9 @@ import chalk from "chalk"; import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; -import { createTurnStream } from "@/cli/commands/imported/render.js"; +import { + createTurnStream, + formatDuration, +} from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import { getBase44ApiUrl } from "@/core/config.js"; @@ -51,11 +54,13 @@ async function chatAction( explicitBranchId ?? (await soleActiveBranchId().catch(() => undefined)); let turn: ImportedChatTurn; + let turnStartedAt: number | undefined; if (jsonMode) { turn = await runTask("Agent working (a turn can take minutes)", () => sendImportedChatMessage(message, branchId), ); } else { + turnStartedAt = Date.now(); const stream = createTurnStream(process.stdout.isTTY === true, undefined, { footer: chatFooter(), }); @@ -88,7 +93,11 @@ async function chatAction( }; } - log.message(turnOutro(turn)); + const took = + turnStartedAt != null + ? ` ${chalk.dim(`· ${formatDuration(Date.now() - turnStartedAt)}`)}` + : ""; + log.message(`${turnOutro(turn)}${took}`); // Stay in the session: keep taking prompts on the same working branch. if (process.stdout.isTTY === true) { await runIterationLoop(log, branchId, chatFooter()); diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index f0d6d8002..d7fb8946d 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -2,7 +2,10 @@ import { mkdir } from "node:fs/promises"; import { join } from "node:path"; import chalk from "chalk"; import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; -import { createTurnStream } from "@/cli/commands/imported/render.js"; +import { + createTurnStream, + formatDuration, +} from "@/cli/commands/imported/render.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import { getBase44ApiUrl } from "@/core/config.js"; @@ -117,12 +120,14 @@ async function createImportedAction( let finalState: string | undefined; let previewUrl: string | undefined; let workBranchId: string | undefined; + let buildStartedAt: number | undefined; if (options.prompt) { // The kickoff turn runs on the app's setup branch conversation. Completion // is the outcome stamp on the turn's user message — the app status field // flaps mid-turn and cannot be trusted. const branchId = await soleActiveBranchId().catch(() => undefined); workBranchId = branchId; + buildStartedAt = Date.now(); const stream = createTurnStream(interactive, undefined, { footer }); let settled: "settled" | "timeout"; try { @@ -183,9 +188,13 @@ async function createImportedAction( outroMessage: `Still building — check progress in the editor or with \`base44 imported status\`.${cdHint}`, }; } + const buildTook = + buildStartedAt != null + ? ` · ${formatDuration(Date.now() - buildStartedAt)}` + : ""; return { outroMessage: options.prompt - ? `First build finished.${cdHint}` + ? `First build finished${buildTook}.${cdHint}` : `Imported app created.${cdHint}`, }; } diff --git a/packages/cli/src/cli/commands/imported/iterate.ts b/packages/cli/src/cli/commands/imported/iterate.ts index 51731d0f5..e5e7d98d0 100644 --- a/packages/cli/src/cli/commands/imported/iterate.ts +++ b/packages/cli/src/cli/commands/imported/iterate.ts @@ -1,5 +1,9 @@ import { isCancel, text } from "@clack/prompts"; -import { createTurnStream } from "@/cli/commands/imported/render.js"; +import chalk from "chalk"; +import { + createTurnStream, + formatDuration, +} from "@/cli/commands/imported/render.js"; import type { CLIContext } from "@/cli/types.js"; import { sendImportedChatMessage } from "@/core/resources/imported/api.js"; import { streamConversationDuring } from "@/core/resources/imported/stream.js"; @@ -22,6 +26,7 @@ export async function runIterationLoop( }); if (isCancel(reply) || !String(reply ?? "").trim()) return; + const turnStartedAt = Date.now(); const stream = createTurnStream(process.stdout.isTTY === true, undefined, { footer, }); @@ -40,10 +45,11 @@ export async function runIterationLoop( continue; } const state = turn.status?.state ?? "ready"; + const took = chalk.dim(`· ${formatDuration(Date.now() - turnStartedAt)}`); log.message( state === "error" - ? `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details.` - : "Turn finished.", + ? `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details. ${took}` + : `Turn finished ${took}`, ); } } diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index 4fbffd66e..d1999c534 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -27,11 +27,22 @@ function toolAlias(name: string): string { return TOOL_ALIASES[name] ?? name; } +export function formatDuration(ms: number): string { + const seconds = Math.round(ms / 1000); + if (seconds < 90) return `${seconds}s`; + return `${Math.floor(seconds / 60)}m ${String(seconds % 60).padStart(2, "0")}s`; +} + /** * The finished line for an event, or null when it only affects the live - * status (a tool starting). Plain string + chalk; no layout gutter. + * status (a tool starting). The tool's own human title (its `summary` + * argument, same as the editor shows) leads; the raw salient argument is the + * dim detail. Plain string + chalk; no layout gutter. */ -export function eventLine(event: StreamEvent): string | null { +export function eventLine( + event: StreamEvent, + elapsedMs?: number, +): string | null { switch (event.kind) { case "thinking": return chalk.dim(`✻ ${event.text}`); @@ -41,17 +52,27 @@ export function eventLine(event: StreamEvent): string | null { return null; case "tool_end": { const alias = toolAlias(event.name); - const head = event.ok - ? `${chalk.green("✓")} ${chalk.bold(alias)}` - : `${chalk.red("✗")} ${chalk.bold(alias)}`; - const summary = event.summary ? ` ${chalk.dim(event.summary)}` : ""; + const mark = event.ok ? chalk.green("✓") : chalk.red("✗"); + const title = chalk.bold(event.label || alias); + const detail = event.label + ? event.summary + ? ` ${chalk.dim(`${alias}: ${event.summary}`)}` + : ` ${chalk.dim(alias)}` + : event.summary + ? ` ${chalk.dim(event.summary)}` + : ""; + const took = + elapsedMs != null && elapsedMs >= 3000 + ? ` ${chalk.dim(`· ${formatDuration(elapsedMs)}`)}` + : ""; + const head = `${mark} ${title}${detail}${took}`; if (event.ok && (QUIET_OK_RESULTS.has(alias) || !event.result)) { - return `${head}${summary}`; + return head; } const result = event.ok ? chalk.dim(event.result) : chalk.red(event.result); - return `${head}${summary}${event.result ? `\n ${result}` : ""}`; + return `${head}${event.result ? `\n ${result}` : ""}`; } } } @@ -89,6 +110,7 @@ const MUSING_ROTATE_MS = 6_000; interface RunningTool { alias: string; + label: string; summary: string; startedAt: number; } @@ -134,8 +156,10 @@ export function createTurnStream( const newest = [...running.values()].at(-1) as RunningTool; const elapsed = Math.round((Date.now() - newest.startedAt) / 1000); const others = running.size > 1 ? ` (+${running.size - 1} more)` : ""; - const summary = newest.summary ? ` ${newest.summary}` : ""; - return `${newest.alias}${summary}${others} · ${elapsed}s`; + const what = + newest.label || + `${newest.alias}${newest.summary ? ` ${newest.summary}` : ""}`; + return `${what}${others} · ${elapsed}s`; }; const clearBlock = () => { @@ -167,6 +191,7 @@ export function createTurnStream( if (event.kind === "tool_start") { running.set(event.id, { alias: toolAlias(event.name), + label: event.label, summary: event.summary, startedAt: Date.now(), }); @@ -176,8 +201,13 @@ export function createTurnStream( } return; } - if (event.kind === "tool_end") running.delete(event.id); - const line = eventLine(event); + let elapsedMs: number | undefined; + if (event.kind === "tool_end") { + const started = running.get(event.id)?.startedAt; + if (started != null) elapsedMs = Date.now() - started; + running.delete(event.id); + } + const line = eventLine(event, elapsedMs); if (line == null) return; if (interactive) { clearBlock(); diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index 55dd8922c..c4c9eea0c 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -4,20 +4,34 @@ import { getFullConversation } from "@/core/resources/imported/api.js"; export type StreamEvent = | { kind: "thinking"; text: string } | { kind: "text"; text: string } - | { kind: "tool_start"; id: string; name: string; summary: string } + | { + kind: "tool_start"; + id: string; + name: string; + /** The tool's human title (its `summary` argument), "" when absent. */ + label: string; + /** The salient argument: the command, the path, the PR title. */ + summary: string; + } | { kind: "tool_end"; id: string; name: string; + label: string; summary: string; ok: boolean; result: string; }; +interface AnnouncedTool { + label: string; + summary: string; +} + interface MessageProgress { contentLength: number; reasoningLength: number; - announcedTools: Map<string, string>; // tool id -> summary + announcedTools: Map<string, AnnouncedTool>; settledTools: Set<string>; } @@ -42,12 +56,12 @@ function oneLine(value: unknown, max: number): string { return flat.length > max ? `${flat.slice(0, max)}…` : flat; } -const SALIENT_KEYS = ["command", "path", "file_path", "title", "summary"]; +const SALIENT_KEYS = ["command", "path", "file_path", "title"]; -/** Pull a salient value out of TRUNCATED arguments JSON (the wire cuts big +/** Pull one key's value out of TRUNCATED arguments JSON (the wire cuts big * payloads mid-string, so JSON.parse fails while the key we want survived). */ -function salvageFromTruncated(raw: string): string | undefined { - for (const key of SALIENT_KEYS) { +function salvageKey(raw: string, keys: string[]): string | undefined { + for (const key of keys) { const match = raw.match( new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`), ); @@ -56,20 +70,25 @@ function salvageFromTruncated(raw: string): string | undefined { return undefined; } -/** The one argument a human wants to see for each tool, not the JSON blob. */ -export function toolSummary( +/** Both faces of a tool call: the human title the model wrote (`summary` + * argument — what the editor shows) and the salient raw argument (the + * command, the path, the PR title). */ +export function toolMeta( name: string, argumentsString: string | null | undefined, -): string { +): AnnouncedTool { + const raw = argumentsString ?? ""; let args: Record<string, unknown> = {}; try { - const parsed: unknown = JSON.parse(argumentsString ?? ""); + const parsed: unknown = JSON.parse(raw); if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { args = parsed as Record<string, unknown>; } } catch { - const salvaged = salvageFromTruncated(argumentsString ?? ""); - return oneLine(salvaged ?? argumentsString ?? "", 90); + return { + label: oneLine(salvageKey(raw, ["summary"]) ?? "", 90), + summary: oneLine(salvageKey(raw, SALIENT_KEYS) ?? raw, 90), + }; } const pick = (key: string): string | undefined => typeof args[key] === "string" && (args[key] as string).trim() @@ -85,12 +104,15 @@ export function toolSummary( }; const summary = salient[name] ?? - pick("summary") ?? - Object.values(args).find( - (v): v is string => typeof v === "string" && v.trim().length > 0, - ) ?? + Object.entries(args).find( + ([key, v]) => + key !== "summary" && typeof v === "string" && v.trim().length > 0, + )?.[1] ?? ""; - return oneLine(summary, 90); + return { + label: oneLine(pick("summary") ?? "", 90), + summary: oneLine(summary as string, 90), + }; } function progressFor(state: StreamState, id: string): MessageProgress { @@ -141,23 +163,27 @@ export function diffConversation( if (!progress.announcedTools.has(tool.id)) { progress.announcedTools.set( tool.id, - toolSummary(tool.name, tool.arguments_string), + toolMeta(tool.name, tool.arguments_string), ); + const meta = progress.announcedTools.get(tool.id) as AnnouncedTool; events.push({ kind: "tool_start", id: tool.id, name: tool.name, - summary: progress.announcedTools.get(tool.id) ?? "", + label: meta.label, + summary: meta.summary, }); } const status = tool.status ?? "running"; if (TOOL_SETTLED.has(status) && !progress.settledTools.has(tool.id)) { progress.settledTools.add(tool.id); + const meta = progress.announcedTools.get(tool.id) as AnnouncedTool; events.push({ kind: "tool_end", id: tool.id, name: tool.name, - summary: progress.announcedTools.get(tool.id) ?? "", + label: meta.label, + summary: meta.summary, ok: status === "success", result: oneLine(tool.results, 110), }); diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 73140de87..18a5fe350 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -1,11 +1,15 @@ import stripAnsi from "strip-ansi"; import { describe, expect, it } from "vitest"; -import { createTurnStream, eventLine } from "@/cli/commands/imported/render.js"; +import { + createTurnStream, + eventLine, + formatDuration, +} from "@/cli/commands/imported/render.js"; import type { ConversationMessage } from "@/core/resources/imported/api.js"; import { diffConversation, newStreamState, - toolSummary, + toolMeta, turnSettled, } from "@/core/resources/imported/stream.js"; @@ -27,7 +31,8 @@ describe("diffConversation", () => { { id: "t1", name: "run_shell_command", - arguments_string: '{"command": "docker compose up -d"}', + arguments_string: + '{"command": "docker compose up -d", "summary": "Boot the stack"}', status: "running", results: null, }, @@ -40,6 +45,7 @@ describe("diffConversation", () => { kind: "tool_start", id: "t1", name: "run_shell_command", + label: "Boot the stack", summary: "docker compose up -d", }, ]); @@ -61,6 +67,7 @@ describe("diffConversation", () => { kind: "tool_end", id: "t1", name: "run_shell_command", + label: "Boot the stack", summary: "docker compose up -d", ok: true, result: "3 containers started", @@ -107,12 +114,14 @@ describe("diffConversation", () => { kind: "tool_start", id: "t1", name: "edit_repo_file", + label: "", summary: "backend/app/db.py", }, { kind: "tool_end", id: "t1", name: "edit_repo_file", + label: "", summary: "backend/app/db.py", ok: false, result: '{"error":"File not found"}', @@ -143,51 +152,78 @@ describe("diffConversation", () => { }); }); -describe("toolSummary", () => { - it("extracts the salient argument per tool", () => { +describe("toolMeta", () => { + it("separates the human title (summary arg) from the salient argument", () => { expect( - toolSummary("run_shell_command", '{"command":"ls -la","summary":"list"}'), - ).toBe("ls -la"); + toolMeta( + "run_shell_command", + '{"command":"ls -la","summary":"List the tree"}', + ), + ).toEqual({ label: "List the tree", summary: "ls -la" }); expect( - toolSummary("write_repo_file", '{"path":"a.py","content":"…"}'), - ).toBe("a.py"); + toolMeta("write_repo_file", '{"path":"a.py","content":"…"}'), + ).toEqual({ label: "", summary: "a.py" }); expect( - toolSummary("create_pull_request", '{"title":"Add auth","body":"x"}'), - ).toBe("Add auth"); + toolMeta("create_pull_request", '{"title":"Add auth","body":"x"}'), + ).toEqual({ label: "", summary: "Add auth" }); }); - it("falls back to summary, then the first string, and survives non-JSON", () => { - expect(toolSummary("set_secrets", '{"summary":"3 secrets declared"}')).toBe( - "3 secrets declared", + it("falls back to the first non-summary string and survives non-JSON", () => { + expect(toolMeta("set_secrets", '{"summary":"3 secrets declared"}')).toEqual( + { label: "3 secrets declared", summary: "" }, ); - expect(toolSummary("unknown_tool", '{"n":1,"target":"web"}')).toBe("web"); - expect(toolSummary("unknown_tool", "not json")).toBe("not json"); + expect(toolMeta("unknown_tool", '{"n":1,"target":"web"}')).toEqual({ + label: "", + summary: "web", + }); + expect(toolMeta("unknown_tool", "not json").summary).toBe("not json"); }); - it("salvages the salient key from truncated arguments JSON", () => { + it("salvages keys from truncated arguments JSON", () => { // Big payloads arrive cut mid-string on the wire — JSON.parse fails, but - // the path key survived and must render instead of the raw blob. + // keys that survived must render instead of the raw blob. expect( - toolSummary( + toolMeta( "write_repo_file", '{"file_path": "the-sewer-vault/src/pages/shop.astro", "content": "<html>… trunc', ), - ).toBe("the-sewer-vault/src/pages/shop.astro"); + ).toEqual({ label: "", summary: "the-sewer-vault/src/pages/shop.astro" }); expect( - toolSummary( + toolMeta( "run_shell_command", - '{"command": "docker compose up -d", "summary": "boot the st', + '{"summary": "Boot the stack", "command": "docker compose up -d", "timeout": 60', ), - ).toBe("docker compose up -d"); + ).toEqual({ label: "Boot the stack", summary: "docker compose up -d" }); }); it("truncates long values to one line", () => { const long = `{"command":"${"x".repeat(200)}"}`; - expect(toolSummary("run_shell_command", long)).toHaveLength(91); // 90 + ellipsis + expect(toolMeta("run_shell_command", long).summary).toHaveLength(91); // 90 + ellipsis }); }); describe("render", () => { + it("title-first line: label leads, alias+arg is the dim detail, duration shown", () => { + expect( + stripAnsi( + eventLine( + { + kind: "tool_end", + id: "t1", + name: "run_shell_command", + label: "Confirmed Wix login", + summary: "cd /tmp && node bootstrap.mjs", + ok: true, + result: '{"event":"logged_in"}', + }, + 4000, + ) ?? "", + ), + ).toBe( + '✓ Confirmed Wix login bash: cd /tmp && node bootstrap.mjs · 4s\n {"event":"logged_in"}', + ); + }); + it("aliases tool names and keeps quiet on boring ok results", () => { expect( stripAnsi( @@ -195,34 +231,29 @@ describe("render", () => { kind: "tool_end", id: "t1", name: "write_repo_file", + label: "", summary: "frontend/src/App.jsx", ok: true, result: "Wrote frontend/src/App.jsx", }) ?? "", ), ).toBe("✓ write frontend/src/App.jsx"); - expect( - stripAnsi( - eventLine({ - kind: "tool_end", - id: "t2", - name: "run_shell_command", - summary: "docker compose ps", - ok: true, - result: "3 containers running", - }) ?? "", - ), - ).toBe("✓ bash docker compose ps\n 3 containers running"); expect( eventLine({ kind: "tool_start", id: "t3", name: "run_shell_command", + label: "", summary: "ls", }), ).toBeNull(); }); + it("formats durations for humans", () => { + expect(formatDuration(4_000)).toBe("4s"); + expect(formatDuration(272_000)).toBe("4m 32s"); + }); + it("non-interactive stream prints settled lines only, no ANSI cursor codes", () => { const out: string[] = []; const stream = createTurnStream(false, (text) => out.push(text)); @@ -230,12 +261,14 @@ describe("render", () => { kind: "tool_start", id: "t1", name: "write_repo_file", + label: "", summary: "a.py", }); stream.onEvent({ kind: "tool_end", id: "t1", name: "write_repo_file", + label: "", summary: "a.py", ok: true, result: "Wrote a.py", From 8c0fb7fc9627f25dc8452d1e1694a7b2a6221090 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:07:15 +0300 Subject: [PATCH 13/73] =?UTF-8?q?feat:=20base44=20target=20+=20top-level?= =?UTF-8?q?=20new=20=E2=80=94=20the=20CLI=20carries=20its=20own=20sugar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `base44 target <url> [--ff <override>]` persists a staging/preview host (~/.base44/target.json) that every command then hits — env vars still win, --clear returns to production, bare `target` shows the active host. `base44 new <name> ["<prompt>"]` is the from-scratch flow as a first-class command: directory + fresh private repo + app + streamed first build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 16 ++++ packages/cli/src/cli/commands/target.ts | 86 +++++++++++++++++++ packages/cli/src/cli/program.ts | 6 ++ .../cli/src/core/clients/base44-client.ts | 7 +- packages/cli/src/core/config.ts | 59 ++++++++++++- packages/cli/tests/cli/imported.spec.ts | 51 +++++++++++ packages/cli/tests/cli/target.spec.ts | 38 ++++++++ 7 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 packages/cli/src/cli/commands/target.ts create mode 100644 packages/cli/tests/cli/target.spec.ts diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index d7fb8946d..434a4be26 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -199,6 +199,22 @@ async function createImportedAction( }; } +/** Top-level sugar: `base44 new <name> ["<prompt>"]` — blank mode with the + * prompt as a positional, no flags to remember. */ +export function getNewCommand(): Base44Command { + const command = new Base44Command("new", { requireAppContext: false }); + command + .description( + "Start a blank app: makes ./<name>, a fresh private GitHub repo named <name>, and builds from your prompt", + ) + .argument("<name>", "One name for the directory, GitHub repo, and app") + .argument("[prompt]", "First message for the agent; the build streams live") + .action((ctx: CLIContext, name: string, prompt: string | undefined) => + createImportedAction(ctx, name, { prompt }), + ); + return command; +} + export function getImportedCreateCommand(): Base44Command { const command = new Base44Command("create", { requireAppContext: false }); command diff --git a/packages/cli/src/cli/commands/target.ts b/packages/cli/src/cli/commands/target.ts new file mode 100644 index 000000000..f7e9508fd --- /dev/null +++ b/packages/cli/src/cli/commands/target.ts @@ -0,0 +1,86 @@ +import type { CLIContext, RunCommandResult } from "@/cli/types.js"; +import { Base44Command } from "@/cli/utils/index.js"; +import { + clearStoredTarget, + getBase44ApiUrl, + getTargetFilePath, + readStoredTarget, + writeStoredTarget, +} from "@/core/config.js"; +import { InvalidInputError } from "@/core/errors.js"; + +interface TargetOptions { + ff?: string; + clear?: boolean; +} + +async function targetAction( + { log, jsonMode }: CLIContext, + url: string | undefined, + options: TargetOptions, +): Promise<RunCommandResult> { + if (options.clear) { + clearStoredTarget(); + return { + outroMessage: "Target cleared — commands hit production again.", + }; + } + + if (!url && !options.ff) { + const stored = readStoredTarget(); + const active = getBase44ApiUrl(); + if (jsonMode) { + return { + stdout: `${JSON.stringify({ + active_api_url: active, + stored_api_url: stored.apiUrl ?? null, + ff_override: stored.ffOverride ?? null, + })}\n`, + }; + } + log.message( + `active ${active}${stored.apiUrl ? "" : " (production default)"}`, + ); + if (stored.ffOverride) log.message(`ff ${stored.ffOverride}`); + return { + outroMessage: stored.apiUrl + ? `Stored in ${getTargetFilePath()} — clear with \`base44 target --clear\`.` + : "No stored target.", + }; + } + + if (url && !/^https?:\/\//.test(url)) { + throw new InvalidInputError( + "Target must be a full URL, e.g. https://docker-pr-24793.velino.org", + ); + } + const previous = readStoredTarget(); + const next = { + apiUrl: url ?? previous.apiUrl, + ffOverride: options.ff ?? previous.ffOverride, + }; + writeStoredTarget(next); + const ff = next.ffOverride ? ` (X-FF-Override: ${next.ffOverride})` : ""; + return { + outroMessage: `Every base44 command now targets ${next.apiUrl ?? getBase44ApiUrl()}${ff}. Back to production: \`base44 target --clear\`.`, + }; +} + +export function getTargetCommand(): Base44Command { + const command = new Base44Command("target", { + requireAuth: false, + requireAppContext: false, + }); + command + .description( + "Point every command at a staging/preview host (persisted). No arguments shows the current target; --clear returns to production", + ) + .argument("[url]", "API base URL, e.g. https://docker-pr-24793.velino.org") + .option( + "--ff <override>", + 'Feature-flag override header sent on every request (staging only), e.g. "imported-apps:true"', + ) + .option("--clear", "Remove the stored target (back to production)") + .action(targetAction); + return command; +} diff --git a/packages/cli/src/cli/program.ts b/packages/cli/src/cli/program.ts index 4fd82a887..5181fab81 100644 --- a/packages/cli/src/cli/program.ts +++ b/packages/cli/src/cli/program.ts @@ -11,6 +11,7 @@ import { getConnectorsCommand } from "@/cli/commands/connectors/index.js"; import { getDashboardCommand } from "@/cli/commands/dashboard/index.js"; import { getEntitiesPushCommand } from "@/cli/commands/entities/push.js"; import { getFunctionsCommand } from "@/cli/commands/functions/index.js"; +import { getNewCommand } from "@/cli/commands/imported/create.js"; import { getImportedCommand } from "@/cli/commands/imported/index.js"; import { getBuildCommand } from "@/cli/commands/project/build.js"; import { getCreateCommand } from "@/cli/commands/project/create.js"; @@ -22,6 +23,7 @@ import { getVisibilityCommand } from "@/cli/commands/project/visibility.js"; import { getSandboxCommand } from "@/cli/commands/sandbox/index.js"; import { getSecretsCommand } from "@/cli/commands/secrets/index.js"; import { getSiteCommand } from "@/cli/commands/site/index.js"; +import { getTargetCommand } from "@/cli/commands/target.js"; import { getTypesCommand } from "@/cli/commands/types/index.js"; import { getWorkflowsCommand } from "@/cli/commands/workflows/index.js"; import { getWorkspaceCommand } from "@/cli/commands/workspace/index.js"; @@ -115,6 +117,10 @@ export function createProgram(context: CLIContext): Command { // Register imported-app commands program.addCommand(getImportedCommand()); + program.addCommand(getNewCommand()); + + // Register the target command (staging/preview host selection) + program.addCommand(getTargetCommand()); // Register auth config commands program.addCommand(getAuthCommand()); diff --git a/packages/cli/src/core/clients/base44-client.ts b/packages/cli/src/core/clients/base44-client.ts index 0bf02c73a..c413bcbcf 100644 --- a/packages/cli/src/core/clients/base44-client.ts +++ b/packages/cli/src/core/clients/base44-client.ts @@ -14,7 +14,7 @@ import { readAuth, refreshAndSaveTokens, } from "@/core/auth/config.js"; -import { getBase44ApiUrl } from "@/core/config.js"; +import { getBase44ApiUrl, getFfOverride } from "@/core/config.js"; import { getAppContext } from "@/core/project/index.js"; // Track requests that have already been retried to prevent infinite loops @@ -102,8 +102,9 @@ export const base44Client = ky.create({ (request) => { request.headers.set("X-Request-ID", randomUUID()); // Staging/preview only: lets a dev flip PostHog flags per request - // (e.g. BASE44_FF_OVERRIDE="imported-apps:true"); prod ignores it. - const ffOverride = process.env.BASE44_FF_OVERRIDE; + // (BASE44_FF_OVERRIDE env, or the persisted `base44 target --ff`); + // prod ignores the header. + const ffOverride = getFfOverride(); if (ffOverride) request.headers.set("X-FF-Override", ffOverride); }, captureRequestBody, diff --git a/packages/cli/src/core/config.ts b/packages/cli/src/core/config.ts index 6e2af15b1..0586eef34 100644 --- a/packages/cli/src/core/config.ts +++ b/packages/cli/src/core/config.ts @@ -1,5 +1,6 @@ +import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; -import { join } from "node:path"; +import { dirname, join } from "node:path"; import { PROJECT_SUBDIR, TYPES_FILENAME, @@ -26,8 +27,62 @@ export function getTypesOutputPath(projectRoot: string): string { return join(projectRoot, PROJECT_SUBDIR, TYPES_OUTPUT_SUBDIR, TYPES_FILENAME); } +export interface StoredTarget { + apiUrl?: string; + ffOverride?: string; +} + +export function getTargetFilePath(): string { + return join(getBase44GlobalDir(), "target.json"); +} + +/** The persisted non-default target set by `base44 target` — a staging or + * preview host every command should hit instead of production. Synchronous: + * `getBase44ApiUrl` runs at module evaluation, before any async context. */ +export function readStoredTarget(): StoredTarget { + try { + const parsed: unknown = JSON.parse( + readFileSync(getTargetFilePath(), "utf8"), + ); + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + const target = parsed as Record<string, unknown>; + return { + apiUrl: typeof target.apiUrl === "string" ? target.apiUrl : undefined, + ffOverride: + typeof target.ffOverride === "string" ? target.ffOverride : undefined, + }; + } + } catch { + // No stored target — production defaults apply. + } + return {}; +} + +export function writeStoredTarget(target: StoredTarget): string { + const path = getTargetFilePath(); + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, `${JSON.stringify(target, null, 2)}\n`); + return path; +} + +export function clearStoredTarget(): void { + try { + unlinkSync(getTargetFilePath()); + } catch { + // Already absent. + } +} + export function getBase44ApiUrl(): string { - return process.env.BASE44_API_URL || "https://app.base44.com"; + return ( + process.env.BASE44_API_URL || + readStoredTarget().apiUrl || + "https://app.base44.com" + ); +} + +export function getFfOverride(): string | undefined { + return process.env.BASE44_FF_OVERRIDE || readStoredTarget().ffOverride; } export function getTestOverrides(): TestOverrides | null { diff --git a/packages/cli/tests/cli/imported.spec.ts b/packages/cli/tests/cli/imported.spec.ts index c71443008..cc69e76e7 100644 --- a/packages/cli/tests/cli/imported.spec.ts +++ b/packages/cli/tests/cli/imported.spec.ts @@ -193,6 +193,57 @@ describe("imported", () => { t.expectResult(badName).toFail(); }); + it("top-level new <name> <prompt> creates blank, waits for the settled turn, returns the preview", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + let sentBody: Record<string, unknown> | undefined; + t.api.mockRoute("POST", "/api/apps", (req, res) => { + sentBody = req.body as Record<string, unknown>; + return res.json({ id: "new-app-3", name: "tmnt-2" }); + }); + t.api.mockRoute("GET", "/api/apps/new-app-3/branches", (_req, res) => + res.json([{ id: "b1", branch_name: "base44/setup-x", status: "active" }]), + ); + // The turn's user message already carries a terminal outcome → settles on + // the first poll. + t.api.mockRoute( + "GET", + "/api/apps/new-app-3/chat/full-conversation", + (_req, res) => + res.json({ + messages: [ + { + id: "u1", + role: "user", + content: "sell tmnt figures", + outcome: { backend_status: "success_build" }, + }, + ], + }), + ); + t.api.mockRoute("GET", "/api/apps/new-app-3", (_req, res) => + res.json({ id: "new-app-3", status: { state: "ready" } }), + ); + t.api.mockRoute( + "GET", + "/api/apps/new-app-3/sandbox/preview-url", + (_req, res) => res.json({ preview_url: "3000-x.e2b.app" }), + ); + + const result = await t.run("new", "tmnt-2", "sell tmnt figures", "--json"); + t.expectResult(result).toSucceed(); + expect(sentBody).toMatchObject({ + app_type: "imported_app", + imported_source_mode: "blank", + imported_new_repo_name: "tmnt-2", + initial_message: { content: "sell tmnt figures" }, + }); + expect(JSON.parse(result.stdout)).toMatchObject({ + id: "new-app-3", + status: "ready", + preview_url: "https://3000-x.e2b.app", + }); + }); + it("create --blank requires a repo name and sends the blank payload", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); diff --git a/packages/cli/tests/cli/target.spec.ts b/packages/cli/tests/cli/target.spec.ts new file mode 100644 index 000000000..e5bdcb3a5 --- /dev/null +++ b/packages/cli/tests/cli/target.spec.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; +import { setupCLITests } from "./testkit/index.js"; + +describe("target", () => { + const t = setupCLITests(); + + it("stores a target with a flag override, shows it, and clears it", async () => { + const set = await t.run( + "target", + "https://docker-pr-24793.velino.org", + "--ff", + "imported-apps:true", + "--json", + ); + t.expectResult(set).toSucceed(); + + const show = await t.run("target", "--json"); + t.expectResult(show).toSucceed(); + expect(JSON.parse(show.stdout)).toMatchObject({ + stored_api_url: "https://docker-pr-24793.velino.org", + ff_override: "imported-apps:true", + }); + + const clear = await t.run("target", "--clear", "--json"); + t.expectResult(clear).toSucceed(); + const after = await t.run("target", "--json"); + expect(JSON.parse(after.stdout)).toMatchObject({ + stored_api_url: null, + ff_override: null, + }); + }); + + it("rejects a bare hostname", async () => { + const result = await t.run("target", "docker-pr-1.velino.org", "--json"); + t.expectResult(result).toFail(); + expect(JSON.parse(result.stdout).error).toContain("full URL"); + }); +}); From 9261fa5e9ef94d5dd463b0252dcaf5c8f18a659f Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:07:57 +0300 Subject: [PATCH 14/73] chore: unexport internal StoredTarget interface (knip) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/core/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/core/config.ts b/packages/cli/src/core/config.ts index 0586eef34..e0f6f58f0 100644 --- a/packages/cli/src/core/config.ts +++ b/packages/cli/src/core/config.ts @@ -27,7 +27,7 @@ export function getTypesOutputPath(projectRoot: string): string { return join(projectRoot, PROJECT_SUBDIR, TYPES_OUTPUT_SUBDIR, TYPES_FILENAME); } -export interface StoredTarget { +interface StoredTarget { apiUrl?: string; ffOverride?: string; } From 6723c213b43441cfe5e0480fe38d50683a92545f Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:14:20 +0300 Subject: [PATCH 15/73] =?UTF-8?q?feat(imported):=20unified=20live=20footer?= =?UTF-8?q?=20=E2=80=94=20preview=20joins=20the=20pinned=20links?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The preview URL is fetched while the stream still ticks and pushed into the live footer array, so stop() prints one complete, visually separated block (blank line, then repo/editor/preview) instead of links glued to the prose plus a separate clack spinner and orphan preview line. The pinned block also carries a leading blank line while streaming. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 42 +++++++++---------- .../cli/src/cli/commands/imported/render.ts | 18 +++++--- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 434a4be26..7dae8fc69 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -129,30 +129,30 @@ async function createImportedAction( workBranchId = branchId; buildStartedAt = Date.now(); const stream = createTurnStream(interactive, undefined, { footer }); - let settled: "settled" | "timeout"; try { - settled = await streamConversationUntilSettled( + const settled = await streamConversationUntilSettled( (event) => { if (!jsonMode) stream.onEvent(event); }, { branchId, timeoutMs: POLL_TIMEOUT_MS }, ); + finalState = + settled === "timeout" + ? "processing" + : ((await getImportedAppState(created.id)).status?.state ?? "ready"); + if (finalState === "ready") { + try { + // Fetched while the stream still ticks; the footer array is live, so + // the link joins the pinned block and persists with it on stop. + previewUrl = await getImportedPreviewUrl(); + footer.push(chalk.dim(`preview ${previewUrl}`)); + } catch { + // Preview may still be booting; the editor shows it when it's up. + } + } } finally { stream.stop(); } - finalState = - settled === "timeout" - ? "processing" - : ((await getImportedAppState(created.id)).status?.state ?? "ready"); - if (finalState === "ready") { - try { - previewUrl = await runTask("Fetching preview URL", () => - getImportedPreviewUrl(), - ); - } catch { - // Preview may still be booting; the editor shows it when it's up. - } - } } if (jsonMode) { @@ -167,15 +167,13 @@ async function createImportedAction( }; } - if (previewUrl) log.message(`preview ${previewUrl}`); + // Non-interactive runs never draw the pinned block — print the link plainly. + if (previewUrl && !interactive) log.message(`preview ${previewUrl}`); - // Stay in the session: keep taking prompts on the same working branch. + // Stay in the session: keep taking prompts on the same working branch. The + // footer already carries the preview link pushed above. if (options.prompt && process.stdout.isTTY === true) { - const sessionFooter = [ - ...footer, - ...(previewUrl ? [chalk.dim(`preview ${previewUrl}`)] : []), - ]; - await runIterationLoop(log, workBranchId, sessionFooter); + await runIterationLoop(log, workBranchId, footer); } const cdHint = name ? ` Next: cd ${name}` : ""; if (finalState === "error") { diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index d1999c534..5f616ede4 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -122,9 +122,10 @@ interface TurnStream { interface TurnStreamOptions { /** Lines pinned under the stream (repo/editor/preview links) — always the - * bottom of the terminal while streaming, printed permanently on stop. Keep - * each line under a typical terminal width: a soft-wrapped footer line - * breaks the redraw arithmetic. */ + * bottom of the terminal while streaming, printed permanently on stop. The + * array is read LIVE: pushing a line (e.g. the preview URL once fetched) + * makes it appear on the next tick. Keep each line under a typical terminal + * width: a soft-wrapped footer line breaks the redraw arithmetic. */ footer?: string[]; } @@ -171,7 +172,11 @@ export function createTurnStream( const drawBlock = () => { if (!interactive || stopped) return; - const lines = [...footer, chalk.dim(`${FRAMES[frame]} ${statusLabel()}`)]; + // Leading blank line keeps the pinned links visually apart from the stream. + const lines = [ + ...(footer.length ? ["", ...footer] : []), + chalk.dim(`${FRAMES[frame]} ${statusLabel()}`), + ]; write(lines.join("\n")); drawnLines = lines.length; }; @@ -220,8 +225,9 @@ export function createTurnStream( stop() { if (interactive) { clearBlock(); - // The links outlive the stream — leave them printed for clicking. - if (footer.length) write(`${footer.join("\n")}\n`); + // The links outlive the stream — leave them printed for clicking, set + // apart from the prose above. + if (footer.length) write(`\n${footer.join("\n")}\n`); } stopped = true; if (timer) clearInterval(timer); From 6e5561ded7a5b15717c266d39ce1db9cfb235009 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:23:26 +0300 Subject: [PATCH 16/73] feat(imported): Claude-Code-style interactive session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit new and chat (TTY) now run a persistent session: the conversation streams into normal scrollback while a redrawn bottom region pins the footer links, a status line — running tool + per-tool elapsed + LIVE turn timer, or 'ready — last turn 4m 32s' between turns — and an always-present input line. Typing works mid-turn (Enter sends; the backend queues it behind the running turn) with cursor editing, Ctrl+A/E/U, and paste flattening. One long-lived conversation watcher covers every turn including server-queued ones, so turn state derives from the newest user message's outcome stamp. Each settle prints '— turn finished · <time>' into scrollback; Ctrl+C clears input then exits (turns keep running server-side). --json and non-TTY keep their previous plain output paths; the old prompt-loop module is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 75 ++-- .../cli/src/cli/commands/imported/create.ts | 37 +- .../cli/src/cli/commands/imported/iterate.ts | 55 --- .../cli/src/cli/commands/imported/render.ts | 7 +- .../cli/src/cli/commands/imported/session.ts | 355 ++++++++++++++++++ .../cli/src/core/resources/imported/stream.ts | 29 +- 6 files changed, 440 insertions(+), 118 deletions(-) delete mode 100644 packages/cli/src/cli/commands/imported/iterate.ts create mode 100644 packages/cli/src/cli/commands/imported/session.ts diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 9eb9114ee..2e7dac403 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,9 +1,6 @@ import chalk from "chalk"; -import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; -import { - createTurnStream, - formatDuration, -} from "@/cli/commands/imported/render.js"; +import { createTurnStream } from "@/cli/commands/imported/render.js"; +import { runInteractiveSession } from "@/cli/commands/imported/session.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import { getBase44ApiUrl } from "@/core/config.js"; @@ -53,17 +50,23 @@ async function chatAction( const branchId = explicitBranchId ?? (await soleActiveBranchId().catch(() => undefined)); - let turn: ImportedChatTurn; - let turnStartedAt: number | undefined; if (jsonMode) { - turn = await runTask("Agent working (a turn can take minutes)", () => + const turn = await runTask("Agent working (a turn can take minutes)", () => sendImportedChatMessage(message, branchId), ); - } else { - turnStartedAt = Date.now(); - const stream = createTurnStream(process.stdout.isTTY === true, undefined, { - footer: chatFooter(), - }); + if (turn.queued) return { stdout: `${JSON.stringify({ queued: true })}\n` }; + return { + stdout: `${JSON.stringify({ + status: turn.status?.state ?? "ready", + error_source: turn.status?.error_source ?? null, + reply: lastAssistantReply(turn) ?? null, + })}\n`, + }; + } + + if (process.stdout.isTTY !== true) { + const stream = createTurnStream(false); + let turn: ImportedChatTurn; try { turn = await streamConversationDuring( () => sendImportedChatMessage(message, branchId), @@ -73,42 +76,30 @@ async function chatAction( } finally { stream.stop(); } + if (turn.queued) { + return { + outroMessage: + "The agent is busy with an earlier message — yours was queued and will run next.", + }; + } + return { outroMessage: turnOutro(turn) }; } - if (turn.queued) { - if (jsonMode) return { stdout: `${JSON.stringify({ queued: true })}\n` }; - return { - outroMessage: - "The agent is busy with an earlier message — yours was queued and will run next.", - }; - } - - if (jsonMode) { - return { - stdout: `${JSON.stringify({ - status: turn.status?.state ?? "ready", - error_source: turn.status?.error_source ?? null, - reply: lastAssistantReply(turn) ?? null, - })}\n`, - }; - } - - const took = - turnStartedAt != null - ? ` ${chalk.dim(`· ${formatDuration(Date.now() - turnStartedAt)}`)}` - : ""; - log.message(`${turnOutro(turn)}${took}`); - // Stay in the session: keep taking prompts on the same working branch. - if (process.stdout.isTTY === true) { - await runIterationLoop(log, branchId, chatFooter()); - } - return { outroMessage: "Session ended." }; + await runInteractiveSession({ + branchId, + footer: chatFooter(), + primeFirstPoll: true, + initialMessage: message, + }); + return { outroMessage: "Done." }; } export function getImportedChatCommand(): Base44Command { const command = new Base44Command("chat", { supportsBranch: true }); command - .description("Send a message to the app's agent and watch the turn live") + .description( + "Open an interactive agent session on the app, starting with this message", + ) .argument("<message>", "What you want the agent to do") .action(chatAction); return command; diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 7dae8fc69..55067d12b 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,11 +1,11 @@ import { mkdir } from "node:fs/promises"; import { join } from "node:path"; import chalk from "chalk"; -import { runIterationLoop } from "@/cli/commands/imported/iterate.js"; import { createTurnStream, formatDuration, } from "@/cli/commands/imported/render.js"; +import { runInteractiveSession } from "@/cli/commands/imported/session.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import { getBase44ApiUrl } from "@/core/config.js"; @@ -119,16 +119,34 @@ async function createImportedAction( let finalState: string | undefined; let previewUrl: string | undefined; - let workBranchId: string | undefined; let buildStartedAt: number | undefined; if (options.prompt) { // The kickoff turn runs on the app's setup branch conversation. Completion // is the outcome stamp on the turn's user message — the app status field // flaps mid-turn and cannot be trusted. const branchId = await soleActiveBranchId().catch(() => undefined); - workBranchId = branchId; buildStartedAt = Date.now(); - const stream = createTurnStream(interactive, undefined, { footer }); + if (interactive) { + // Full session: the kickoff streams, then the input stays open for + // follow-up turns. Per-turn outcomes and times print inline. + await runInteractiveSession({ + branchId, + footer, + primeFirstPoll: false, + onTurnSettled: async ({ turnIndex, ok }) => { + if (turnIndex === 0 && ok && !previewUrl) { + try { + previewUrl = await getImportedPreviewUrl(); + footer.push(chalk.dim(`preview ${previewUrl}`)); + } catch { + // Preview may still be booting; the editor shows it when up. + } + } + }, + }); + return { outroMessage: name ? `Next: cd ${name}` : "Done." }; + } + const stream = createTurnStream(false); try { const settled = await streamConversationUntilSettled( (event) => { @@ -142,10 +160,7 @@ async function createImportedAction( : ((await getImportedAppState(created.id)).status?.state ?? "ready"); if (finalState === "ready") { try { - // Fetched while the stream still ticks; the footer array is live, so - // the link joins the pinned block and persists with it on stop. previewUrl = await getImportedPreviewUrl(); - footer.push(chalk.dim(`preview ${previewUrl}`)); } catch { // Preview may still be booting; the editor shows it when it's up. } @@ -168,13 +183,7 @@ async function createImportedAction( } // Non-interactive runs never draw the pinned block — print the link plainly. - if (previewUrl && !interactive) log.message(`preview ${previewUrl}`); - - // Stay in the session: keep taking prompts on the same working branch. The - // footer already carries the preview link pushed above. - if (options.prompt && process.stdout.isTTY === true) { - await runIterationLoop(log, workBranchId, footer); - } + if (previewUrl && !jsonMode) log.message(`preview ${previewUrl}`); const cdHint = name ? ` Next: cd ${name}` : ""; if (finalState === "error") { return { diff --git a/packages/cli/src/cli/commands/imported/iterate.ts b/packages/cli/src/cli/commands/imported/iterate.ts deleted file mode 100644 index e5e7d98d0..000000000 --- a/packages/cli/src/cli/commands/imported/iterate.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { isCancel, text } from "@clack/prompts"; -import chalk from "chalk"; -import { - createTurnStream, - formatDuration, -} from "@/cli/commands/imported/render.js"; -import type { CLIContext } from "@/cli/types.js"; -import { sendImportedChatMessage } from "@/core/resources/imported/api.js"; -import { streamConversationDuring } from "@/core/resources/imported/stream.js"; - -/** - * Post-run iteration mode: keep taking prompts and running streamed turns on - * the same branch until the user submits nothing (or cancels). TTY only — - * callers gate on interactivity. - */ -export async function runIterationLoop( - log: CLIContext["log"], - branchId: string | undefined, - footer?: string[], -): Promise<void> { - for (;;) { - const reply = await text({ - message: "What next? (Enter with no text to finish)", - placeholder: "e.g. add user login with sessions", - defaultValue: "", - }); - if (isCancel(reply) || !String(reply ?? "").trim()) return; - - const turnStartedAt = Date.now(); - const stream = createTurnStream(process.stdout.isTTY === true, undefined, { - footer, - }); - let turn: Awaited<ReturnType<typeof sendImportedChatMessage>>; - try { - turn = await streamConversationDuring( - () => sendImportedChatMessage(String(reply).trim(), branchId), - stream.onEvent, - { branchId }, - ); - } finally { - stream.stop(); - } - if (turn.queued) { - log.message("Queued behind an earlier message — it will run next."); - continue; - } - const state = turn.status?.state ?? "ready"; - const took = chalk.dim(`· ${formatDuration(Date.now() - turnStartedAt)}`); - log.message( - state === "error" - ? `Turn failed (${turn.status?.error_source ?? "unknown"}) — see the editor for details. ${took}` - : `Turn finished ${took}`, - ); - } -} diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index 5f616ede4..2cd73a109 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -23,7 +23,7 @@ const TOOL_ALIASES: Record<string, string> = { // Verbose result text adds nothing for these; the path in the summary does. const QUIET_OK_RESULTS = new Set(["read", "write", "edit", "reload"]); -function toolAlias(name: string): string { +export function toolAlias(name: string): string { return TOOL_ALIASES[name] ?? name; } @@ -108,6 +108,11 @@ const MUSINGS = [ ]; const MUSING_ROTATE_MS = 6_000; +/** The rotating idle gerund for a given session seed. */ +export function idleMusing(seed: number): string { + return `${MUSINGS[(seed + Math.floor(Date.now() / MUSING_ROTATE_MS)) % MUSINGS.length]}…`; +} + interface RunningTool { alias: string; label: string; diff --git a/packages/cli/src/cli/commands/imported/session.ts b/packages/cli/src/cli/commands/imported/session.ts new file mode 100644 index 000000000..a3657f06b --- /dev/null +++ b/packages/cli/src/cli/commands/imported/session.ts @@ -0,0 +1,355 @@ +import { emitKeypressEvents } from "node:readline"; +import chalk from "chalk"; +import { + eventLine, + formatDuration, + idleMusing, + toolAlias, +} from "@/cli/commands/imported/render.js"; +import { + getFullConversation, + sendImportedChatMessage, +} from "@/core/resources/imported/api.js"; +import { + diffConversation, + newestUserTurn, + newStreamState, +} from "@/core/resources/imported/stream.js"; + +const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; +const POLL_MS = 1_000; +const DRAW_MS = 120; + +interface RunningTool { + alias: string; + label: string; + summary: string; + startedAt: number; +} + +interface TurnSettleInfo { + turnIndex: number; + ok: boolean; + backendStatus?: string; + durationMs: number; +} + +interface SessionOptions { + branchId?: string; + /** Live footer lines (repo/editor/preview) — pushing appends to the block. */ + footer: string[]; + /** Swallow whatever the conversation already holds before showing anything — + * false for a fresh create, whose kickoff turn IS the history. */ + primeFirstPoll: boolean; + /** Sent as the first turn right after priming (the `chat` argument). */ + initialMessage?: string; + onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; +} + +/** + * The Claude-Code-style interactive session: the conversation streams into + * normal scrollback while a redrawn bottom region keeps the footer links, a + * status line (activity + live turn timer + last turn), and an always-present + * input line. Typing works mid-turn — Enter sends, and the backend queues the + * message behind the running turn. One persistent conversation watcher covers + * every turn, including server-queued ones. Ctrl+C clears the input, then + * exits; Ctrl+D exits. TTY only — callers gate on interactivity. + */ +export async function runInteractiveSession( + options: SessionOptions, +): Promise<void> { + const write = (text: string) => process.stdout.write(text); + const footer = options.footer; + const running = new Map<string, RunningTool>(); + const diffState = newStreamState(); + const musingSeed = Math.floor(Math.random() * 97); + const sessionStartedAt = Date.now(); + + let frame = 0; + let drawnLines = 0; + let buffer = ""; + let cursor = 0; + let exitRequested = false; + let sendsInFlight = 0; + let activeTurnId: string | null = null; + let turnStartedAt: number | null = null; + let pendingSubmitAt: number | null = null; + let lastTurnMs: number | null = null; + let lastTurnOk = true; + let settledCount = 0; + + const columns = () => process.stdout.columns || 80; + + const statusLine = (): string => { + if (turnStartedAt != null) { + const turnFor = formatDuration(Date.now() - turnStartedAt); + let activity: string; + if (running.size > 0) { + const newest = [...running.values()].at(-1) as RunningTool; + const toolFor = Math.round((Date.now() - newest.startedAt) / 1000); + const others = running.size > 1 ? ` (+${running.size - 1})` : ""; + const what = + newest.label || + `${newest.alias}${newest.summary ? ` ${newest.summary}` : ""}`; + activity = `${what}${others} · ${toolFor}s`; + } else { + activity = idleMusing(musingSeed); + } + return chalk.dim(`${FRAMES[frame]} ${activity} — turn ${turnFor}`); + } + if (sendsInFlight > 0 || pendingSubmitAt != null) { + return chalk.dim(`${FRAMES[frame]} sending…`); + } + const last = + lastTurnMs != null + ? ` — last turn ${formatDuration(lastTurnMs)}${lastTurnOk ? "" : " (failed)"}` + : ""; + return chalk.dim(`· ready${last}`); + }; + + const inputLine = (): { text: string; cursorCol: number } => { + const width = Math.max(20, columns() - 4); + const start = Math.max(0, cursor - width + 6); + const visible = buffer.slice(start, start + width); + const cursorCol = cursor - start; + const body = + buffer.length === 0 + ? chalk.dim("type · Enter sends · Ctrl+C exits") + : visible; + return { text: `${chalk.cyan("❯")} ${body}`, cursorCol: cursorCol + 2 }; + }; + + const clearBlock = () => { + if (!drawnLines) return; + write("\r\x1b[2K"); + for (let i = 1; i < drawnLines; i++) write("\x1b[1A\r\x1b[2K"); + drawnLines = 0; + }; + + const drawBlock = () => { + const input = inputLine(); + const lines = ["", ...footer, statusLine(), input.text]; + write(lines.join("\n")); + drawnLines = lines.length; + // Park the terminal cursor where the logical cursor sits in the input. + if (buffer.length > 0) { + const lineLength = + 2 + Math.min(buffer.length, Math.max(20, columns() - 4)); + const back = lineLength - input.cursorCol; + if (back > 0) write(`\x1b[${back}D`); + } + }; + + const redraw = () => { + clearBlock(); + drawBlock(); + }; + + const printLine = (line: string) => { + clearBlock(); + write(`${line}\n`); + drawBlock(); + }; + + const submit = (raw: string) => { + const text = raw.trim(); + if (!text) return; + printLine(`${chalk.cyan("❯")} ${chalk.bold(text)}`); + pendingSubmitAt = Date.now(); + sendsInFlight++; + sendImportedChatMessage(text, options.branchId) + .then((turn) => { + if (turn.queued) { + printLine(chalk.dim("· queued — runs after the current turn")); + } + }) + .catch((error: unknown) => { + pendingSubmitAt = null; + const message = error instanceof Error ? error.message : String(error); + printLine(chalk.red(`✗ send failed: ${message}`)); + }) + .finally(() => { + sendsInFlight--; + }); + }; + + const onKeypress = ( + str: string | undefined, + key: { name?: string; ctrl?: boolean; meta?: boolean } = {}, + ) => { + if (key.ctrl && key.name === "c") { + if (buffer) { + buffer = ""; + cursor = 0; + } else { + exitRequested = true; + } + redraw(); + return; + } + if (key.ctrl && key.name === "d") { + exitRequested = true; + redraw(); + return; + } + if (key.name === "return" || key.name === "enter") { + const text = buffer; + buffer = ""; + cursor = 0; + submit(text); + return; + } + if (key.name === "backspace") { + if (cursor > 0) { + buffer = buffer.slice(0, cursor - 1) + buffer.slice(cursor); + cursor--; + } + redraw(); + return; + } + if (key.name === "left") { + cursor = Math.max(0, cursor - 1); + redraw(); + return; + } + if (key.name === "right") { + cursor = Math.min(buffer.length, cursor + 1); + redraw(); + return; + } + if (key.ctrl && key.name === "a") { + cursor = 0; + redraw(); + return; + } + if (key.ctrl && key.name === "e") { + cursor = buffer.length; + redraw(); + return; + } + if (key.ctrl && key.name === "u") { + buffer = buffer.slice(cursor); + cursor = 0; + redraw(); + return; + } + if (str && !key.ctrl && !key.meta) { + // Paste arrives as one chunk; newlines inside it become spaces so a + // multi-line paste is one prompt, not an accidental submit spree. + const clean = str.replace(/[\r\n]+/g, " "); + // Drop other control characters. + const printable = clean.replace(/[\x00-\x1f\x7f]/g, ""); + if (!printable) return; + buffer = buffer.slice(0, cursor) + printable + buffer.slice(cursor); + cursor += printable.length; + redraw(); + } + }; + + const poll = async (prime: boolean) => { + let messages: Awaited<ReturnType<typeof getFullConversation>>; + try { + messages = await getFullConversation(30, options.branchId); + } catch { + return; // Transient — next tick retries. + } + const events = diffConversation(diffState, messages); + if (!prime) { + for (const event of events) { + if (event.kind === "tool_start") { + running.set(event.id, { + alias: toolAlias(event.name), + label: event.label, + summary: event.summary, + startedAt: Date.now(), + }); + continue; + } + let elapsedMs: number | undefined; + if (event.kind === "tool_end") { + const started = running.get(event.id)?.startedAt; + if (started != null) elapsedMs = Date.now() - started; + running.delete(event.id); + } + const line = eventLine(event, elapsedMs); + if (line != null) printLine(line); + } + } + + const turn = newestUserTurn(messages); + if (!turn) return; + if (turn.id !== activeTurnId) { + activeTurnId = turn.id; + if (!turn.settled) { + turnStartedAt = pendingSubmitAt ?? Date.now(); + pendingSubmitAt = null; + running.clear(); + } else if (prime) { + // Session opened onto an already-finished turn — nothing to track. + turnStartedAt = null; + } + } + if (turn.settled && turnStartedAt != null && turn.id === activeTurnId) { + const durationMs = Date.now() - turnStartedAt; + turnStartedAt = null; + running.clear(); + lastTurnMs = durationMs; + const ok = !turn.backendStatus?.startsWith("error"); + lastTurnOk = ok; + const line = ok + ? chalk.dim(`— turn finished · ${formatDuration(durationMs)}`) + : chalk.red( + `— turn failed (${turn.backendStatus ?? "unknown"}) · ${formatDuration(durationMs)}`, + ); + printLine(line); + const info: TurnSettleInfo = { + turnIndex: settledCount++, + ok, + backendStatus: turn.backendStatus, + durationMs, + }; + try { + await options.onTurnSettled?.(info); + } catch { + // A settle hook failure must not kill the session. + } + } + }; + + const stdin = process.stdin; + const supportsRaw = stdin.isTTY === true; + emitKeypressEvents(stdin); + if (supportsRaw) stdin.setRawMode(true); + stdin.resume(); + stdin.on("keypress", onKeypress); + const drawTimer = setInterval(() => { + frame = (frame + 1) % FRAMES.length; + redraw(); + }, DRAW_MS); + drawTimer.unref?.(); + + try { + await poll(options.primeFirstPoll); + if (options.initialMessage) submit(options.initialMessage); + redraw(); + while (!exitRequested) { + await new Promise((resolve) => setTimeout(resolve, POLL_MS)); + if (exitRequested) break; + await poll(false); + } + } finally { + clearInterval(drawTimer); + stdin.off("keypress", onKeypress); + if (supportsRaw) stdin.setRawMode(false); + stdin.pause(); + clearBlock(); + if (footer.length) write(`\n${footer.join("\n")}\n`); + const note = + turnStartedAt != null + ? " — the running turn continues server-side (watch it in the editor)" + : ""; + write( + `${chalk.dim(`session ended · ${formatDuration(Date.now() - sessionStartedAt)}${note}`)}\n`, + ); + } +} diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index c4c9eea0c..8194302ef 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -202,18 +202,35 @@ export function diffConversation( * the app's status field, which flaps mid-turn. */ export function turnSettled(messages: ConversationMessage[]): boolean { + return newestUserTurn(messages)?.settled ?? false; +} + +interface UserTurn { + id: string; + settled: boolean; + backendStatus?: string; +} + +/** The newest user message and whether its turn reached a terminal outcome. */ +export function newestUserTurn( + messages: ConversationMessage[], +): UserTurn | null { for (let i = messages.length - 1; i >= 0; i--) { const message = messages[i]; if (message.role === "user" && !message.hidden) { const outcome = message.outcome as { backend_status?: string } | null; - return ( - outcome != null && - typeof outcome === "object" && - outcome.backend_status !== "pending" - ); + const backendStatus = + outcome && typeof outcome === "object" + ? outcome.backend_status + : undefined; + return { + id: message.id, + settled: outcome != null && backendStatus !== "pending", + backendStatus, + }; } } - return false; + return null; } const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); From 307b8be37ffe67130ffe63ef2f2b1bd6c1b3c1dc Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:27:30 +0300 Subject: [PATCH 17/73] =?UTF-8?q?feat(imported):=20session=20polish=20?= =?UTF-8?q?=E2=80=94=20fresh=20viewport,=20rule=20footer,=20hint=20below?= =?UTF-8?q?=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session clears the visible screen on start (shell history stays in scrollback), the pinned region opens with a full-width dim rule so the footer reads as a footer, and the key hints live on their own dim line under the input instead of posing as placeholder text. Cursor parks on the input line above the hint; clearing steps back down first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.ts | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.ts b/packages/cli/src/cli/commands/imported/session.ts index a3657f06b..9cc817d2f 100644 --- a/packages/cli/src/cli/commands/imported/session.ts +++ b/packages/cli/src/cli/commands/imported/session.ts @@ -112,32 +112,40 @@ export async function runInteractiveSession( const start = Math.max(0, cursor - width + 6); const visible = buffer.slice(start, start + width); const cursorCol = cursor - start; - const body = - buffer.length === 0 - ? chalk.dim("type · Enter sends · Ctrl+C exits") - : visible; - return { text: `${chalk.cyan("❯")} ${body}`, cursorCol: cursorCol + 2 }; + return { text: `${chalk.cyan("❯")} ${visible}`, cursorCol: cursorCol + 2 }; }; + // The cursor parks on the input line, one line above the hint at the bottom + // of the block — clearing must step back down first. + let parkedUp = 0; + const clearBlock = () => { if (!drawnLines) return; + if (parkedUp > 0) write(`\x1b[${parkedUp}B`); + parkedUp = 0; write("\r\x1b[2K"); for (let i = 1; i < drawnLines; i++) write("\x1b[1A\r\x1b[2K"); drawnLines = 0; }; + const rule = () => chalk.dim("─".repeat(Math.min(columns(), 100))); + const drawBlock = () => { const input = inputLine(); - const lines = ["", ...footer, statusLine(), input.text]; + const lines = [ + rule(), + ...footer, + statusLine(), + input.text, + chalk.dim(" Enter to send · Ctrl+C to exit (turns keep running)"), + ]; write(lines.join("\n")); drawnLines = lines.length; - // Park the terminal cursor where the logical cursor sits in the input. - if (buffer.length > 0) { - const lineLength = - 2 + Math.min(buffer.length, Math.max(20, columns() - 4)); - const back = lineLength - input.cursorCol; - if (back > 0) write(`\x1b[${back}D`); - } + // Park the terminal cursor where the logical cursor sits in the input line + // (one line above the hint). + write(`\x1b[1A\r`); + if (input.cursorCol > 0) write(`\x1b[${input.cursorCol}C`); + parkedUp = 1; }; const redraw = () => { @@ -329,6 +337,9 @@ export async function runInteractiveSession( drawTimer.unref?.(); try { + // Fresh viewport, Claude-Code style: the visible screen clears (shell + // history stays in scrollback) and the session owns what you see. + write("\x1b[2J\x1b[H"); await poll(options.primeFirstPoll); if (options.initialMessage) submit(options.initialMessage); redraw(); From c3415ce7e5ef3dee4f08a29f82828d51fe1391b8 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:32:34 +0300 Subject: [PATCH 18/73] fix(imported): created dirs are discoverable + kickoff wait is visible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit create now writes a minimal base44/config.jsonc alongside .app.jsonc — root discovery (findProjectRoot) keys on a PROJECT config, so without it every later command run from the created directory failed with 'No Base44 app ID found'. Verified live: imported status resolves the app from a created-dir shape with no --app-id. Never clobbers an existing config (wx flag). The session also no longer claims '· ready' during the 10-30s before the kickoff turn's user message exists (sandbox provisioning): create passes an awaiting label, shown with a spinner and elapsed time, and the kickoff's turn timer counts from session start rather than first detection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 18 ++++++++++++++++-- .../cli/src/cli/commands/imported/session.ts | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 55067d12b..782fe0f46 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,4 +1,4 @@ -import { mkdir } from "node:fs/promises"; +import { mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import chalk from "chalk"; import { @@ -97,7 +97,20 @@ async function createImportedAction( ); const configPath = await writeAppConfig(targetDir, created.id); - setAppContext({ id: created.id }); + // Root discovery (findProjectRoot) keys on a PROJECT config, not .app.jsonc — + // without this, every later command run from the directory fails to find it. + const projectConfigPath = join(targetDir, "base44", "config.jsonc"); + await mkdir(join(targetDir, "base44"), { recursive: true }); + try { + await writeFile( + projectConfigPath, + `// Base44 project configuration.\n{\n "name": ${JSON.stringify(appName)}\n}\n`, + { flag: "wx" }, // Never clobber an existing project config. + ); + } catch { + // Already present — fine. + } + setAppContext({ id: created.id, projectRoot: targetDir }); // The links ride as a sticky footer under the stream (always clickable) and // are printed permanently when it ends; non-interactive output gets them up @@ -133,6 +146,7 @@ async function createImportedAction( branchId, footer, primeFirstPoll: false, + awaitingTurnLabel: "provisioning the sandbox and starting the build", onTurnSettled: async ({ turnIndex, ok }) => { if (turnIndex === 0 && ok && !previewUrl) { try { diff --git a/packages/cli/src/cli/commands/imported/session.ts b/packages/cli/src/cli/commands/imported/session.ts index 9cc817d2f..0acfc48b5 100644 --- a/packages/cli/src/cli/commands/imported/session.ts +++ b/packages/cli/src/cli/commands/imported/session.ts @@ -43,6 +43,9 @@ interface SessionOptions { primeFirstPoll: boolean; /** Sent as the first turn right after priming (the `chat` argument). */ initialMessage?: string; + /** A turn is already starting server-side (the create kickoff): show this + * as the busy label until its user message appears, instead of "ready". */ + awaitingTurnLabel?: string; onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; } @@ -77,6 +80,8 @@ export async function runInteractiveSession( let lastTurnMs: number | null = null; let lastTurnOk = true; let settledCount = 0; + let awaitingTurn = options.awaitingTurnLabel ?? null; + const awaitingSince = Date.now(); const columns = () => process.stdout.columns || 80; @@ -97,6 +102,11 @@ export async function runInteractiveSession( } return chalk.dim(`${FRAMES[frame]} ${activity} — turn ${turnFor}`); } + if (awaitingTurn != null) { + return chalk.dim( + `${FRAMES[frame]} ${awaitingTurn} · ${formatDuration(Date.now() - awaitingSince)}`, + ); + } if (sendsInFlight > 0 || pendingSubmitAt != null) { return chalk.dim(`${FRAMES[frame]} sending…`); } @@ -286,10 +296,15 @@ export async function runInteractiveSession( const turn = newestUserTurn(messages); if (!turn) return; + const kickoffDetection = awaitingTurn != null && activeTurnId === null; + awaitingTurn = null; if (turn.id !== activeTurnId) { activeTurnId = turn.id; if (!turn.settled) { - turnStartedAt = pendingSubmitAt ?? Date.now(); + // A kickoff was already running before this session opened — count its + // time from session start. Later turns count from their own submit. + turnStartedAt = + pendingSubmitAt ?? (kickoffDetection ? awaitingSince : Date.now()); pendingSubmitAt = null; running.clear(); } else if (prime) { From b0f59278dc44b51174468fce2025e50e9648a667 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:41:54 +0300 Subject: [PATCH 19/73] feat(imported): port the interactive session to Ink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session UI is now React/Ink — the same architecture Claude Code uses: history renders permanently into scrollback via <Static> while the bottom region (rule, footer links, status with live turn timer, text input, hints) re-renders in place. This replaces ~150 lines of hand-rolled ANSI cursor arithmetic and buys correct input editing, wrapping, and resize behavior from the framework. The session logic itself moved unchanged into a render-free engine (session-engine.ts): persistent conversation watcher, outcome-stamp turn state, mid-turn queued sends, kickoff-awaiting phase. Containment: runInteractiveSession keeps its exact signature, so create/ chat are untouched beyond imports; no other command loads React. Bundling: ink's dev-only react-devtools-core import is stubbed by a build plugin (it otherwise lands as an eager import of an unshipped package); knip's project glob learns .tsx. Deps: ink, react, ink-text-input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- bun.lock | 1230 +++++++++-------- knip.json | 2 +- packages/cli/infra/build.ts | 26 +- packages/cli/package.json | 6 +- .../cli/src/cli/commands/imported/chat.ts | 2 +- .../cli/commands/imported/session-engine.ts | 231 ++++ .../cli/src/cli/commands/imported/session.ts | 381 ----- .../cli/src/cli/commands/imported/session.tsx | 204 +++ packages/cli/tsconfig.json | 1 + 9 files changed, 1133 insertions(+), 950 deletions(-) create mode 100644 packages/cli/src/cli/commands/imported/session-engine.ts delete mode 100644 packages/cli/src/cli/commands/imported/session.ts create mode 100644 packages/cli/src/cli/commands/imported/session.tsx diff --git a/bun.lock b/bun.lock index a5378fbbb..ccaba5fd1 100644 --- a/bun.lock +++ b/bun.lock @@ -11,14 +11,17 @@ }, "packages/cli": { "name": "base44", - "version": "0.1.14", + "version": "0.1.15", "bin": { "base44": "./bin/run.js", }, "dependencies": { "@deno/loader": "https://npm.jsr.io/~/11/@jsr/deno__loader/0.5.0.tgz", "esbuild": "0.28.0", + "ink": "^5", + "ink-text-input": "^6", "miniflare": "4.20260722.0", + "react": "^18", }, "devDependencies": { "@base44-cli/logger": "workspace:*", @@ -37,6 +40,7 @@ "@types/ms": "^2.1.0", "@types/multer": "^2.0.0", "@types/node": "^22.10.5", + "@types/react": "^18", "@vercel/detect-agent": "^1.1.0", "chalk": "^5.6.2", "chokidar": "^5.0.0", @@ -102,1138 +106,1236 @@ }, }, "packages": { - "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.1.3", "https://npm.dev.wixpress.com/api/npm/npm-repos/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.1.3.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], + + "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "https://npm.dev.wixpress.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], "@base44-cli/logger": ["@base44-cli/logger@workspace:packages/logger"], "@base44/functions-compiler": ["@base44/functions-compiler@workspace:packages/functions-compiler"], - "@base44/sdk": ["@base44/sdk@0.8.23", "", { "dependencies": { "axios": "^1.6.2", "socket.io-client": "^4.7.5", "uuid": "^13.0.0" } }, "sha512-udQwd9VikwsUjstf+Af4dr9rhMww9F8WExccnKihPX0V9Tk3SPvWvnhujMwWdjT8g2vy2j9rP4jIT76Yn+OKcA=="], + "@base44/sdk": ["@base44/sdk@0.8.23", "https://npm.dev.wixpress.com/@base44/sdk/-/sdk-0.8.23.tgz", { "dependencies": { "axios": "^1.6.2", "socket.io-client": "^4.7.5", "uuid": "^13.0.0" } }, "sha512-udQwd9VikwsUjstf+Af4dr9rhMww9F8WExccnKihPX0V9Tk3SPvWvnhujMwWdjT8g2vy2j9rP4jIT76Yn+OKcA=="], - "@biomejs/biome": ["@biomejs/biome@2.4.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.6", "@biomejs/cli-darwin-x64": "2.4.6", "@biomejs/cli-linux-arm64": "2.4.6", "@biomejs/cli-linux-arm64-musl": "2.4.6", "@biomejs/cli-linux-x64": "2.4.6", "@biomejs/cli-linux-x64-musl": "2.4.6", "@biomejs/cli-win32-arm64": "2.4.6", "@biomejs/cli-win32-x64": "2.4.6" }, "bin": { "biome": "bin/biome" } }, "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ=="], + "@biomejs/biome": ["@biomejs/biome@2.4.6", "https://npm.dev.wixpress.com/@biomejs/biome/-/biome-2.4.6.tgz", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.6", "@biomejs/cli-darwin-x64": "2.4.6", "@biomejs/cli-linux-arm64": "2.4.6", "@biomejs/cli-linux-arm64-musl": "2.4.6", "@biomejs/cli-linux-x64": "2.4.6", "@biomejs/cli-linux-x64-musl": "2.4.6", "@biomejs/cli-win32-arm64": "2.4.6", "@biomejs/cli-win32-x64": "2.4.6" }, "bin": { "biome": "bin/biome" } }, "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.6.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.6.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.6.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.6.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.6.tgz", { "os": "linux", "cpu": "x64" }, "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.6.tgz", { "os": "linux", "cpu": "x64" }, "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.6.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "https://npm.dev.wixpress.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.6.tgz", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="], - "@clack/core": ["@clack/core@1.0.1", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g=="], + "@clack/core": ["@clack/core@1.0.1", "https://npm.dev.wixpress.com/@clack/core/-/core-1.0.1.tgz", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g=="], - "@clack/prompts": ["@clack/prompts@1.0.1", "", { "dependencies": { "@clack/core": "1.0.1", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q=="], + "@clack/prompts": ["@clack/prompts@1.0.1", "https://npm.dev.wixpress.com/@clack/prompts/-/prompts-1.0.1.tgz", { "dependencies": { "@clack/core": "1.0.1", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q=="], - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260722.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-vZOP8vIS3NwnuaO+gz0FZ7kIGeiO3bZmxV35Ph9zOXKSREhDFlH7wQ7mkCdhW3O4jnXsew+XT7b+DNEI2CcJGQ=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260722.1", "https://npm.dev.wixpress.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260722.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-vZOP8vIS3NwnuaO+gz0FZ7kIGeiO3bZmxV35Ph9zOXKSREhDFlH7wQ7mkCdhW3O4jnXsew+XT7b+DNEI2CcJGQ=="], - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260722.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EmIQymihDq6WNdER4+LF8Qn80yqayBUpJ+tkOO7wmY8pmgfyXjIUFNXotl21AHovTeu2seR7HdVUgeN/BilCWw=="], + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260722.1", "https://npm.dev.wixpress.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260722.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-EmIQymihDq6WNdER4+LF8Qn80yqayBUpJ+tkOO7wmY8pmgfyXjIUFNXotl21AHovTeu2seR7HdVUgeN/BilCWw=="], - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260722.1", "", { "os": "linux", "cpu": "x64" }, "sha512-jvZ3k9fxcnEn04s80CgIYxQfpOyAiz/8qC42DP8EBa9tR27qWyg9wmm31zIobVlrgBZn/+8NfdP73avRGcQOjQ=="], + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260722.1", "https://npm.dev.wixpress.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260722.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-jvZ3k9fxcnEn04s80CgIYxQfpOyAiz/8qC42DP8EBa9tR27qWyg9wmm31zIobVlrgBZn/+8NfdP73avRGcQOjQ=="], - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260722.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-BOSB55SMNdy+DA5uj2WirgiNanpHGis5PVvXH1wSfvjRKr4JGgWK+EZzxz0RFUo6QjjQQC/NimEzNZ7va7jmKg=="], + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260722.1", "https://npm.dev.wixpress.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260722.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-BOSB55SMNdy+DA5uj2WirgiNanpHGis5PVvXH1wSfvjRKr4JGgWK+EZzxz0RFUo6QjjQQC/NimEzNZ7va7jmKg=="], - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260722.1", "", { "os": "win32", "cpu": "x64" }, "sha512-sYM8YgUpKnRz2xjvdJLX1Ojzoi4MlA4gk8WTTExhGydjYB2UTs5NIbv0ZmpKgMoK9io3ixgmiW56ZnTbcWOdiA=="], + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260722.1", "https://npm.dev.wixpress.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260722.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-sYM8YgUpKnRz2xjvdJLX1Ojzoi4MlA4gk8WTTExhGydjYB2UTs5NIbv0ZmpKgMoK9io3ixgmiW56ZnTbcWOdiA=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260702.1", "", {}, "sha512-mOhf5TUEB1m2vPrxtqoIGfz0fUC9xyxRDx5gWHy5s+OCo6dcV+g7wI1R7gYCMFohhqF/2y2xeKVwMwCJjfn/WA=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260702.1", "https://npm.dev.wixpress.com/@cloudflare/workers-types/-/workers-types-4.20260702.1.tgz", {}, "sha512-mOhf5TUEB1m2vPrxtqoIGfz0fUC9xyxRDx5gWHy5s+OCo6dcV+g7wI1R7gYCMFohhqF/2y2xeKVwMwCJjfn/WA=="], - "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], + "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "https://npm.dev.wixpress.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], "@deno/loader": ["@jsr/deno__loader@0.5.0", "https://npm.jsr.io/~/11/@jsr/deno__loader/0.5.0.tgz", {}, "sha512-sf/YBwnyAsbyeYYB71Zdj2Ca2Q9tt25EZpAiZdDA9W7Mm3GcpjA2WMeqj19xPIurZK12G3OAsa5yrtPB7E+gvA=="], - "@deno/shim-deno": ["@deno/shim-deno@0.19.2", "", { "dependencies": { "@deno/shim-deno-test": "^0.5.0", "which": "^4.0.0" } }, "sha512-q3VTHl44ad8T2Tw2SpeAvghdGOjlnLPDNO2cpOxwMrBE/PVas6geWpbpIgrM+czOCH0yejp0yi8OaTuB+NU40Q=="], + "@deno/shim-deno": ["@deno/shim-deno@0.19.2", "https://npm.dev.wixpress.com/@deno/shim-deno/-/shim-deno-0.19.2.tgz", { "dependencies": { "@deno/shim-deno-test": "^0.5.0", "which": "^4.0.0" } }, "sha512-q3VTHl44ad8T2Tw2SpeAvghdGOjlnLPDNO2cpOxwMrBE/PVas6geWpbpIgrM+czOCH0yejp0yi8OaTuB+NU40Q=="], + + "@deno/shim-deno-test": ["@deno/shim-deno-test@0.5.0", "https://npm.dev.wixpress.com/@deno/shim-deno-test/-/shim-deno-test-0.5.0.tgz", {}, "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w=="], + + "@emnapi/core": ["@emnapi/core@1.8.1", "https://npm.dev.wixpress.com/@emnapi/core/-/core-1.8.1.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "https://npm.dev.wixpress.com/@emnapi/runtime/-/runtime-1.8.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], - "@deno/shim-deno-test": ["@deno/shim-deno-test@0.5.0", "", {}, "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "https://npm.dev.wixpress.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], - "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "https://npm.dev.wixpress.com/@esbuild/android-arm/-/android-arm-0.28.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/android-x64/-/android-x64-0.28.0.tgz", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "https://npm.dev.wixpress.com/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "https://npm.dev.wixpress.com/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="], + "@img/colour": ["@img/colour@1.1.0", "https://npm.dev.wixpress.com/@img/colour/-/colour-1.1.0.tgz", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.1" }, "os": "darwin", "cpu": "arm64" }, "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.1" }, "os": "darwin", "cpu": "x64" }, "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw=="], - "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "os": "freebsd" }, "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.1" }, "os": "darwin", "cpu": "arm64" }, "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.1" }, "os": "darwin", "cpu": "x64" }, "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ=="], - "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "os": "freebsd" }, "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.1", "", { "os": "linux", "cpu": "arm" }, "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg=="], + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew=="], - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A=="], - "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.1", "", { "os": "linux", "cpu": "none" }, "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw=="], - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.1", "https://npm.dev.wixpress.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.1" }, "os": "linux", "cpu": "arm" }, "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.1" }, "os": "linux", "cpu": "ppc64" }, "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.1" }, "os": "linux", "cpu": "arm" }, "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A=="], + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.1" }, "os": "linux", "cpu": "none" }, "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.1" }, "os": "linux", "cpu": "s390x" }, "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA=="], - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.1" }, "os": "linux", "cpu": "ppc64" }, "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA=="], - "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.1" }, "os": "linux", "cpu": "none" }, "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg=="], - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.1" }, "os": "linux", "cpu": "s390x" }, "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA=="], + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" }, "os": "linux", "cpu": "arm64" }, "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg=="], + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "cpu": "none" }, "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.1" }, "os": "linux", "cpu": "x64" }, "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ=="], - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.2", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw=="], + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog=="], - "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.2", "", { "dependencies": { "@img/sharp-wasm32": "0.35.2" }, "cpu": "none" }, "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g=="], + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.2", "https://npm.dev.wixpress.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ=="], - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ=="], + "@inquirer/ansi": ["@inquirer/ansi@1.0.2", "https://npm.dev.wixpress.com/@inquirer/ansi/-/ansi-1.0.2.tgz", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="], - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog=="], + "@inquirer/confirm": ["@inquirer/confirm@5.1.21", "https://npm.dev.wixpress.com/@inquirer/confirm/-/confirm-5.1.21.tgz", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.2", "", { "os": "win32", "cpu": "x64" }, "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ=="], + "@inquirer/core": ["@inquirer/core@10.3.2", "https://npm.dev.wixpress.com/@inquirer/core/-/core-10.3.2.tgz", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "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" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="], - "@inquirer/ansi": ["@inquirer/ansi@1.0.2", "", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="], + "@inquirer/figures": ["@inquirer/figures@1.0.15", "https://npm.dev.wixpress.com/@inquirer/figures/-/figures-1.0.15.tgz", {}, "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g=="], - "@inquirer/confirm": ["@inquirer/confirm@5.1.21", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ=="], + "@inquirer/type": ["@inquirer/type@3.0.10", "https://npm.dev.wixpress.com/@inquirer/type/-/type-3.0.10.tgz", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="], - "@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "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" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="], + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "https://npm.dev.wixpress.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], - "@inquirer/figures": ["@inquirer/figures@1.0.15", "", {}, "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g=="], + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "https://npm.dev.wixpress.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - "@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://npm.dev.wixpress.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "https://npm.dev.wixpress.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "https://npm.dev.wixpress.com/@jsdevtools/ono/-/ono-7.1.3.tgz", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@mswjs/interceptors": ["@mswjs/interceptors@0.41.2", "https://npm.dev.wixpress.com/@mswjs/interceptors/-/interceptors-0.41.2.tgz", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-7G0Uf0yK3f2bjElBLGHIQzgRgMESczOMyYVasq1XK8P5HaXtlW4eQhz9MBL+TQILZLaruq+ClGId+hH0w4jvWw=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "https://npm.dev.wixpress.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], - "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "https://npm.dev.wixpress.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - "@mswjs/interceptors": ["@mswjs/interceptors@0.41.2", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-7G0Uf0yK3f2bjElBLGHIQzgRgMESczOMyYVasq1XK8P5HaXtlW4eQhz9MBL+TQILZLaruq+ClGId+hH0w4jvWw=="], + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "https://npm.dev.wixpress.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "https://npm.dev.wixpress.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + "@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "https://npm.dev.wixpress.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="], - "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + "@open-draft/logger": ["@open-draft/logger@0.3.0", "https://npm.dev.wixpress.com/@open-draft/logger/-/logger-0.3.0.tgz", { "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" } }, "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ=="], - "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@open-draft/until": ["@open-draft/until@2.1.0", "https://npm.dev.wixpress.com/@open-draft/until/-/until-2.1.0.tgz", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="], - "@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="], + "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.17.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-+VuZyMYYaap5uDAU1xDU3Kul0FekLqpBS8kI5JozlWfYQKnc/HsZg2gHPkQrj0SC9lt74WMNCfOzZZJlYXSdEQ=="], - "@open-draft/logger": ["@open-draft/logger@0.3.0", "", { "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" } }, "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ=="], + "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.17.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YlDDTjvOEKhom/cRSVsXsMVeXVIAM9PJ/x2mfe08rfuS0iIEfJd8PngKbEIhG72WPxleUa+vkEZj9ncmC14z3Q=="], - "@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="], + "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.17.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-HOYYLSY4JDk14YkXaz/ApgJYhgDP4KsG8EZpgpOxdszGW9HmIMMY/vXqVKYW74dSH+GQkIXYxBrEh3nv+XODVg=="], - "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.17.1", "", { "os": "android", "cpu": "arm" }, "sha512-+VuZyMYYaap5uDAU1xDU3Kul0FekLqpBS8kI5JozlWfYQKnc/HsZg2gHPkQrj0SC9lt74WMNCfOzZZJlYXSdEQ=="], + "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.17.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-JHPJbsa5HvPq2/RIdtGlqfaG9zV2WmgvHrKTYmlW0L5esqtKCBuetFudXTBzkNcyD69kSZLzH92AzTr6vFHMFg=="], - "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.17.1", "", { "os": "android", "cpu": "arm64" }, "sha512-YlDDTjvOEKhom/cRSVsXsMVeXVIAM9PJ/x2mfe08rfuS0iIEfJd8PngKbEIhG72WPxleUa+vkEZj9ncmC14z3Q=="], + "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.17.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-UD1FRC8j8xZstFXYsXwQkNmmg7vUbee006IqxokwDUUA+xEgKZDpLhBEiVKM08Urb+bn7Q0gn6M1pyNR0ng5mg=="], - "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.17.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HOYYLSY4JDk14YkXaz/ApgJYhgDP4KsG8EZpgpOxdszGW9HmIMMY/vXqVKYW74dSH+GQkIXYxBrEh3nv+XODVg=="], + "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.17.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-wFWC1wyf2ROFWTxK5x0Enm++DSof3EBQ/ypyAesMDLiYxOOASDoMOZG1ylWUnlKaCt5W7eNOWOzABpdfFf/ssA=="], - "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.17.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-JHPJbsa5HvPq2/RIdtGlqfaG9zV2WmgvHrKTYmlW0L5esqtKCBuetFudXTBzkNcyD69kSZLzH92AzTr6vFHMFg=="], + "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.17.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-k/hUif0GEBk/csSqCfTPXb8AAVs1NNWCa/skBghvNbTtORcWfOVqJ3mM+2pE189+enRm4UnryLREu5ysI0kXEQ=="], - "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.17.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UD1FRC8j8xZstFXYsXwQkNmmg7vUbee006IqxokwDUUA+xEgKZDpLhBEiVKM08Urb+bn7Q0gn6M1pyNR0ng5mg=="], + "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.17.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-Cwm6A071ww60QouJ9LoHAwBgEoZzHQ0Qaqk2E7WLfBdiQN9mLXIDhnrpn04hlRElRPhLiu/dtg+o5PPLvaINXQ=="], - "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.17.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wFWC1wyf2ROFWTxK5x0Enm++DSof3EBQ/ypyAesMDLiYxOOASDoMOZG1ylWUnlKaCt5W7eNOWOzABpdfFf/ssA=="], + "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.17.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-+hwlE2v3m0r3sk93SchJL1uyaKcPjf+NGO/TD2DZUDo+chXx7FfaEj0nUMewigSt7oZ2sQN9Z4NJOtUa75HE5Q=="], - "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.17.1", "", { "os": "linux", "cpu": "arm" }, "sha512-k/hUif0GEBk/csSqCfTPXb8AAVs1NNWCa/skBghvNbTtORcWfOVqJ3mM+2pE189+enRm4UnryLREu5ysI0kXEQ=="], + "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.17.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-bO+rsaE5Ox8cFyeL5Ct5tzot1TnQpFa/Wmu5k+hqBYSH2dNVDGoi0NizBN5QV8kOIC6O5MZr81UG4yW/2FyDTA=="], - "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.17.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Cwm6A071ww60QouJ9LoHAwBgEoZzHQ0Qaqk2E7WLfBdiQN9mLXIDhnrpn04hlRElRPhLiu/dtg+o5PPLvaINXQ=="], + "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.17.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-B/P+hxKQ1oX4YstI9Lyh4PGzqB87Ddqj/A4iyRBbPdXTcxa+WW3oRLx1CsJKLmHPdDk461Hmbghq1Bm3pl+8Aw=="], - "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.17.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+hwlE2v3m0r3sk93SchJL1uyaKcPjf+NGO/TD2DZUDo+chXx7FfaEj0nUMewigSt7oZ2sQN9Z4NJOtUa75HE5Q=="], + "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.17.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-ulp2H3bFXzd/th2maH+QNKj5qgOhJ3v9Yspdf1svTw3CDOuuTl6sRKsWQ7MUw0vnkSNvQndtflBwVXgzZvURsQ=="], - "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.17.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bO+rsaE5Ox8cFyeL5Ct5tzot1TnQpFa/Wmu5k+hqBYSH2dNVDGoi0NizBN5QV8kOIC6O5MZr81UG4yW/2FyDTA=="], + "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.17.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-LAXYVe3rKk09Zo9YKF2ZLBcH8sz8Oj+JIyiUxiHtq0hiYLMsN6dOpCf2hzQEjPAmsSEA/hdC1PVKeXo+oma8mQ=="], - "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.17.1", "", { "os": "linux", "cpu": "none" }, "sha512-B/P+hxKQ1oX4YstI9Lyh4PGzqB87Ddqj/A4iyRBbPdXTcxa+WW3oRLx1CsJKLmHPdDk461Hmbghq1Bm3pl+8Aw=="], + "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.17.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-3RAhxipMKE8RCSPn7O//sj440i+cYTgYbapLeOoDvQEt6R1QcJjTsFgI4iz99FhVj3YbPxlZmcLB5VW+ipyRTA=="], - "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.17.1", "", { "os": "linux", "cpu": "none" }, "sha512-ulp2H3bFXzd/th2maH+QNKj5qgOhJ3v9Yspdf1svTw3CDOuuTl6sRKsWQ7MUw0vnkSNvQndtflBwVXgzZvURsQ=="], + "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.17.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-wpjMEubGU8r9VjZTLdZR3aPHaBqTl8Jl8F4DBbgNoZ+yhkhQD1/MGvY70v2TLnAI6kAHSvcqgfvaqKDa2iWsPQ=="], - "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.17.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-LAXYVe3rKk09Zo9YKF2ZLBcH8sz8Oj+JIyiUxiHtq0hiYLMsN6dOpCf2hzQEjPAmsSEA/hdC1PVKeXo+oma8mQ=="], + "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.17.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-XIE4w17RYAVIgx+9Gs3deTREq5tsmalbatYOOBGNdH7n0DfTE600c7wYXsp7ANc3BPDXsInnOzXDEPCvO1F6cg=="], - "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.17.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3RAhxipMKE8RCSPn7O//sj440i+cYTgYbapLeOoDvQEt6R1QcJjTsFgI4iz99FhVj3YbPxlZmcLB5VW+ipyRTA=="], + "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.17.1.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-Lqi5BlHX3zS4bpSOkIbOKVf7DIk6Gvmdifr2OuOI58eUUyP944M8/OyaB09cNpPy9Vukj7nmmhOzj8pwLgAkIg=="], - "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.17.1", "", { "os": "linux", "cpu": "x64" }, "sha512-wpjMEubGU8r9VjZTLdZR3aPHaBqTl8Jl8F4DBbgNoZ+yhkhQD1/MGvY70v2TLnAI6kAHSvcqgfvaqKDa2iWsPQ=="], + "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.17.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-l6lTcLBQVj1HNquFpXSsrkCIM8X5Hlng5YNQJrg00z/KyovvDV5l3OFhoRyZ+aLBQ74zUnMRaJZC7xcBnHyeNg=="], - "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.17.1", "", { "os": "none", "cpu": "arm64" }, "sha512-XIE4w17RYAVIgx+9Gs3deTREq5tsmalbatYOOBGNdH7n0DfTE600c7wYXsp7ANc3BPDXsInnOzXDEPCvO1F6cg=="], + "@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.17.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-VTzVtfnCCsU/6GgvursWoyZrhe3Gj/RyXzDWmh4/U1Y3IW0u1FZbp+hCIlBL16pRPbDc5YvXVtCOnA41QOrOoQ=="], - "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.17.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-Lqi5BlHX3zS4bpSOkIbOKVf7DIk6Gvmdifr2OuOI58eUUyP944M8/OyaB09cNpPy9Vukj7nmmhOzj8pwLgAkIg=="], + "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.17.1", "https://npm.dev.wixpress.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.17.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-jRPVU+6/12baj87q2+UGRh30FBVBzqKdJ7rP/mSqiL1kpNQB9yZ1j0+m3sru1m+C8hiFK7lBFwjUtYUBI7+UpQ=="], - "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.17.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-l6lTcLBQVj1HNquFpXSsrkCIM8X5Hlng5YNQJrg00z/KyovvDV5l3OFhoRyZ+aLBQ74zUnMRaJZC7xcBnHyeNg=="], + "@poppinss/colors": ["@poppinss/colors@4.1.6", "https://npm.dev.wixpress.com/@poppinss/colors/-/colors-4.1.6.tgz", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], - "@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.17.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-VTzVtfnCCsU/6GgvursWoyZrhe3Gj/RyXzDWmh4/U1Y3IW0u1FZbp+hCIlBL16pRPbDc5YvXVtCOnA41QOrOoQ=="], + "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "https://npm.dev.wixpress.com/@poppinss/dumper/-/dumper-0.6.5.tgz", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], - "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.17.1", "", { "os": "win32", "cpu": "x64" }, "sha512-jRPVU+6/12baj87q2+UGRh30FBVBzqKdJ7rP/mSqiL1kpNQB9yZ1j0+m3sru1m+C8hiFK7lBFwjUtYUBI7+UpQ=="], + "@poppinss/exception": ["@poppinss/exception@1.2.3", "https://npm.dev.wixpress.com/@poppinss/exception/-/exception-1.2.3.tgz", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], - "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], + "@posthog/core": ["@posthog/core@1.10.0", "https://npm.dev.wixpress.com/@posthog/core/-/core-1.10.0.tgz", { "dependencies": { "cross-spawn": "^7.0.6" } }, "sha512-Xk3JQ+cdychsvftrV3G9ZrN9W329lbyFW0pGJXFGKFQf8qr4upw2SgNg9BVorjSrfhoXZRnJGt/uNF4nGFBL5A=="], - "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], - "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], - "@posthog/core": ["@posthog/core@1.10.0", "", { "dependencies": { "cross-spawn": "^7.0.6" } }, "sha512-Xk3JQ+cdychsvftrV3G9ZrN9W329lbyFW0pGJXFGKFQf8qr4upw2SgNg9BVorjSrfhoXZRnJGt/uNF4nGFBL5A=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "https://npm.dev.wixpress.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], + "@seald-io/binary-search-tree": ["@seald-io/binary-search-tree@1.0.3", "https://npm.dev.wixpress.com/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz", {}, "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], + "@seald-io/nedb": ["@seald-io/nedb@4.1.2", "https://npm.dev.wixpress.com/@seald-io/nedb/-/nedb-4.1.2.tgz", { "dependencies": { "@seald-io/binary-search-tree": "^1.0.3", "localforage": "^1.10.0", "util": "^0.12.5" } }, "sha512-bDr6TqjBVS2rDyYM9CPxAnotj5FuNL9NF8o7h7YyFXM7yruqT4ddr+PkSb2mJvvw991bqdftazkEo38gykvaww=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "https://npm.dev.wixpress.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], - "@seald-io/binary-search-tree": ["@seald-io/binary-search-tree@1.0.3", "", {}, "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA=="], + "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "https://npm.dev.wixpress.com/@sindresorhus/is/-/is-7.2.0.tgz", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], - "@seald-io/nedb": ["@seald-io/nedb@4.1.2", "", { "dependencies": { "@seald-io/binary-search-tree": "^1.0.3", "localforage": "^1.10.0", "util": "^0.12.5" } }, "sha512-bDr6TqjBVS2rDyYM9CPxAnotj5FuNL9NF8o7h7YyFXM7yruqT4ddr+PkSb2mJvvw991bqdftazkEo38gykvaww=="], + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "https://npm.dev.wixpress.com/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], - "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "https://npm.dev.wixpress.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], - "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], + "@speed-highlight/core": ["@speed-highlight/core@1.2.17", "https://npm.dev.wixpress.com/@speed-highlight/core/-/core-1.2.17.tgz", {}, "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg=="], - "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "https://npm.dev.wixpress.com/@standard-schema/spec/-/spec-1.1.0.tgz", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "https://npm.dev.wixpress.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "@speed-highlight/core": ["@speed-highlight/core@1.2.17", "", {}, "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg=="], + "@types/body-parser": ["@types/body-parser@1.19.6", "https://npm.dev.wixpress.com/@types/body-parser/-/body-parser-1.19.6.tgz", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], - "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@types/bun": ["@types/bun@1.3.9", "https://npm.dev.wixpress.com/@types/bun/-/bun-1.3.9.tgz", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@types/chai": ["@types/chai@5.2.3", "https://npm.dev.wixpress.com/@types/chai/-/chai-5.2.3.tgz", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], - "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + "@types/common-tags": ["@types/common-tags@1.8.4", "https://npm.dev.wixpress.com/@types/common-tags/-/common-tags-1.8.4.tgz", {}, "sha512-S+1hLDJPjWNDhcGxsxEbepzaxWqURP/o+3cP4aa2w7yBXgdcmKGQtZzP8JbyfOd0m+33nh+8+kvxYE2UJtBDkg=="], - "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="], + "@types/connect": ["@types/connect@3.4.38", "https://npm.dev.wixpress.com/@types/connect/-/connect-3.4.38.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + "@types/cors": ["@types/cors@2.8.19", "https://npm.dev.wixpress.com/@types/cors/-/cors-2.8.19.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], - "@types/common-tags": ["@types/common-tags@1.8.4", "", {}, "sha512-S+1hLDJPjWNDhcGxsxEbepzaxWqURP/o+3cP4aa2w7yBXgdcmKGQtZzP8JbyfOd0m+33nh+8+kvxYE2UJtBDkg=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "https://npm.dev.wixpress.com/@types/deep-eql/-/deep-eql-4.0.2.tgz", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], - "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/deno": ["@types/deno@2.5.0", "https://npm.dev.wixpress.com/@types/deno/-/deno-2.5.0.tgz", {}, "sha512-g8JS38vmc0S87jKsFzre+0ZyMOUDHPVokEJymSCRlL57h6f/FdKPWBXgdFh3Z8Ees9sz11qt9VWELU9Y9ZkiVw=="], - "@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], + "@types/ejs": ["@types/ejs@3.1.5", "https://npm.dev.wixpress.com/@types/ejs/-/ejs-3.1.5.tgz", {}, "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg=="], - "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/estree": ["@types/estree@1.0.8", "https://npm.dev.wixpress.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - "@types/deno": ["@types/deno@2.5.0", "", {}, "sha512-g8JS38vmc0S87jKsFzre+0ZyMOUDHPVokEJymSCRlL57h6f/FdKPWBXgdFh3Z8Ees9sz11qt9VWELU9Y9ZkiVw=="], + "@types/express": ["@types/express@5.0.6", "https://npm.dev.wixpress.com/@types/express/-/express-5.0.6.tgz", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA=="], - "@types/ejs": ["@types/ejs@3.1.5", "", {}, "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg=="], + "@types/express-serve-static-core": ["@types/express-serve-static-core@5.1.1", "https://npm.dev.wixpress.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A=="], - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/http-errors": ["@types/http-errors@2.0.5", "https://npm.dev.wixpress.com/@types/http-errors/-/http-errors-2.0.5.tgz", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], - "@types/express": ["@types/express@5.0.6", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA=="], + "@types/http-proxy": ["@types/http-proxy@1.17.17", "https://npm.dev.wixpress.com/@types/http-proxy/-/http-proxy-1.17.17.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw=="], - "@types/express-serve-static-core": ["@types/express-serve-static-core@5.1.1", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A=="], + "@types/json-schema": ["@types/json-schema@7.0.15", "https://npm.dev.wixpress.com/@types/json-schema/-/json-schema-7.0.15.tgz", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + "@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "https://npm.dev.wixpress.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="], - "@types/http-proxy": ["@types/http-proxy@1.17.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw=="], + "@types/lodash": ["@types/lodash@4.17.24", "https://npm.dev.wixpress.com/@types/lodash/-/lodash-4.17.24.tgz", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="], - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/ms": ["@types/ms@2.1.0", "https://npm.dev.wixpress.com/@types/ms/-/ms-2.1.0.tgz", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="], + "@types/multer": ["@types/multer@2.0.0", "https://npm.dev.wixpress.com/@types/multer/-/multer-2.0.0.tgz", { "dependencies": { "@types/express": "*" } }, "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw=="], - "@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="], + "@types/node": ["@types/node@22.19.11", "https://npm.dev.wixpress.com/@types/node/-/node-22.19.11.tgz", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], - "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + "@types/prop-types": ["@types/prop-types@15.7.15", "https://npm.dev.wixpress.com/api/npm/npm-repos/@types/prop-types/-/prop-types-15.7.15.tgz", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], - "@types/multer": ["@types/multer@2.0.0", "", { "dependencies": { "@types/express": "*" } }, "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw=="], + "@types/qs": ["@types/qs@6.14.0", "https://npm.dev.wixpress.com/@types/qs/-/qs-6.14.0.tgz", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], - "@types/node": ["@types/node@22.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w=="], + "@types/range-parser": ["@types/range-parser@1.2.7", "https://npm.dev.wixpress.com/@types/range-parser/-/range-parser-1.2.7.tgz", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], - "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + "@types/react": ["@types/react@18.3.31", "https://npm.dev.wixpress.com/api/npm/npm-repos/@types/react/-/react-18.3.31.tgz", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw=="], - "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + "@types/send": ["@types/send@1.2.1", "https://npm.dev.wixpress.com/@types/send/-/send-1.2.1.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], - "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], + "@types/serve-static": ["@types/serve-static@2.2.0", "https://npm.dev.wixpress.com/@types/serve-static/-/serve-static-2.2.0.tgz", { "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ=="], - "@types/serve-static": ["@types/serve-static@2.2.0", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ=="], + "@types/statuses": ["@types/statuses@2.0.6", "https://npm.dev.wixpress.com/@types/statuses/-/statuses-2.0.6.tgz", {}, "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA=="], - "@types/statuses": ["@types/statuses@2.0.6", "", {}, "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA=="], + "@vercel/detect-agent": ["@vercel/detect-agent@1.1.0", "https://npm.dev.wixpress.com/@vercel/detect-agent/-/detect-agent-1.1.0.tgz", {}, "sha512-Zfq6FbIcYl9gaAmVu6ROsqUiCNwpEj3Ljz/tMX5fl12Z95OFOxzf7vlO03WE5JBU/ri1tBDFHnW41dihMINOPQ=="], - "@vercel/detect-agent": ["@vercel/detect-agent@1.1.0", "", {}, "sha512-Zfq6FbIcYl9gaAmVu6ROsqUiCNwpEj3Ljz/tMX5fl12Z95OFOxzf7vlO03WE5JBU/ri1tBDFHnW41dihMINOPQ=="], + "@vitest/expect": ["@vitest/expect@4.0.18", "https://npm.dev.wixpress.com/@vitest/expect/-/expect-4.0.18.tgz", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ=="], - "@vitest/expect": ["@vitest/expect@4.0.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ=="], + "@vitest/mocker": ["@vitest/mocker@4.0.18", "https://npm.dev.wixpress.com/@vitest/mocker/-/mocker-4.0.18.tgz", { "dependencies": { "@vitest/spy": "4.0.18", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ=="], - "@vitest/mocker": ["@vitest/mocker@4.0.18", "", { "dependencies": { "@vitest/spy": "4.0.18", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ=="], + "@vitest/pretty-format": ["@vitest/pretty-format@4.0.18", "https://npm.dev.wixpress.com/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw=="], - "@vitest/pretty-format": ["@vitest/pretty-format@4.0.18", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw=="], + "@vitest/runner": ["@vitest/runner@4.0.18", "https://npm.dev.wixpress.com/@vitest/runner/-/runner-4.0.18.tgz", { "dependencies": { "@vitest/utils": "4.0.18", "pathe": "^2.0.3" } }, "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw=="], - "@vitest/runner": ["@vitest/runner@4.0.18", "", { "dependencies": { "@vitest/utils": "4.0.18", "pathe": "^2.0.3" } }, "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw=="], + "@vitest/snapshot": ["@vitest/snapshot@4.0.18", "https://npm.dev.wixpress.com/@vitest/snapshot/-/snapshot-4.0.18.tgz", { "dependencies": { "@vitest/pretty-format": "4.0.18", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA=="], - "@vitest/snapshot": ["@vitest/snapshot@4.0.18", "", { "dependencies": { "@vitest/pretty-format": "4.0.18", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA=="], + "@vitest/spy": ["@vitest/spy@4.0.18", "https://npm.dev.wixpress.com/@vitest/spy/-/spy-4.0.18.tgz", {}, "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw=="], - "@vitest/spy": ["@vitest/spy@4.0.18", "", {}, "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw=="], + "@vitest/utils": ["@vitest/utils@4.0.18", "https://npm.dev.wixpress.com/@vitest/utils/-/utils-4.0.18.tgz", { "dependencies": { "@vitest/pretty-format": "4.0.18", "tinyrainbow": "^3.0.3" } }, "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA=="], - "@vitest/utils": ["@vitest/utils@4.0.18", "", { "dependencies": { "@vitest/pretty-format": "4.0.18", "tinyrainbow": "^3.0.3" } }, "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA=="], + "accepts": ["accepts@2.0.0", "https://npm.dev.wixpress.com/accepts/-/accepts-2.0.0.tgz", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "ansi-escapes": ["ansi-escapes@7.3.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/ansi-escapes/-/ansi-escapes-7.3.0.tgz", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ansi-regex": ["ansi-regex@6.2.2", "https://npm.dev.wixpress.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@6.2.3", "https://npm.dev.wixpress.com/api/npm/npm-repos/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "append-field": ["append-field@1.0.0", "", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="], + "append-field": ["append-field@1.0.0", "https://npm.dev.wixpress.com/append-field/-/append-field-1.0.0.tgz", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="], - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "argparse": ["argparse@2.0.1", "https://npm.dev.wixpress.com/argparse/-/argparse-2.0.1.tgz", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "assertion-error": ["assertion-error@2.0.1", "https://npm.dev.wixpress.com/assertion-error/-/assertion-error-2.0.1.tgz", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + "async": ["async@3.2.6", "https://npm.dev.wixpress.com/async/-/async-3.2.6.tgz", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + "asynckit": ["asynckit@0.4.0", "https://npm.dev.wixpress.com/asynckit/-/asynckit-0.4.0.tgz", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "auto-bind": ["auto-bind@5.0.1", "https://npm.dev.wixpress.com/api/npm/npm-repos/auto-bind/-/auto-bind-5.0.1.tgz", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], - "axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], + "available-typed-arrays": ["available-typed-arrays@1.0.7", "https://npm.dev.wixpress.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "axios": ["axios@1.13.6", "https://npm.dev.wixpress.com/axios/-/axios-1.13.6.tgz", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], + + "balanced-match": ["balanced-match@1.0.2", "https://npm.dev.wixpress.com/balanced-match/-/balanced-match-1.0.2.tgz", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "base44": ["base44@workspace:packages/cli"], - "base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="], + "base64id": ["base64id@2.0.0", "https://npm.dev.wixpress.com/base64id/-/base64id-2.0.0.tgz", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="], + + "body-parser": ["body-parser@2.2.2", "https://npm.dev.wixpress.com/body-parser/-/body-parser-2.2.2.tgz", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + + "brace-expansion": ["brace-expansion@2.0.2", "https://npm.dev.wixpress.com/brace-expansion/-/brace-expansion-2.0.2.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "braces": ["braces@3.0.3", "https://npm.dev.wixpress.com/braces/-/braces-3.0.3.tgz", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "https://npm.dev.wixpress.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "buffer-from": ["buffer-from@1.1.2", "https://npm.dev.wixpress.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "bun-types": ["bun-types@1.3.9", "https://npm.dev.wixpress.com/bun-types/-/bun-types-1.3.9.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], + + "bundle-name": ["bundle-name@4.1.0", "https://npm.dev.wixpress.com/bundle-name/-/bundle-name-4.1.0.tgz", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + + "busboy": ["busboy@1.6.0", "https://npm.dev.wixpress.com/busboy/-/busboy-1.6.0.tgz", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], + + "bytes": ["bytes@3.1.2", "https://npm.dev.wixpress.com/bytes/-/bytes-3.1.2.tgz", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind": ["call-bind@1.0.8", "https://npm.dev.wixpress.com/call-bind/-/call-bind-1.0.8.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "https://npm.dev.wixpress.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "https://npm.dev.wixpress.com/call-bound/-/call-bound-1.0.4.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "chai": ["chai@6.2.2", "https://npm.dev.wixpress.com/chai/-/chai-6.2.2.tgz", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], + + "chalk": ["chalk@5.6.2", "https://npm.dev.wixpress.com/chalk/-/chalk-5.6.2.tgz", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "chokidar": ["chokidar@5.0.0", "https://npm.dev.wixpress.com/chokidar/-/chokidar-5.0.0.tgz", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "chownr": ["chownr@3.0.0", "https://npm.dev.wixpress.com/chownr/-/chownr-3.0.0.tgz", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "cli-boxes": ["cli-boxes@3.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/cli-boxes/-/cli-boxes-3.0.0.tgz", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], + + "cli-cursor": ["cli-cursor@4.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/cli-cursor/-/cli-cursor-4.0.0.tgz", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], + + "cli-truncate": ["cli-truncate@4.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/cli-truncate/-/cli-truncate-4.0.0.tgz", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + + "cli-width": ["cli-width@4.1.0", "https://npm.dev.wixpress.com/cli-width/-/cli-width-4.1.0.tgz", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], + + "cliui": ["cliui@8.0.1", "https://npm.dev.wixpress.com/cliui/-/cliui-8.0.1.tgz", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "code-excerpt": ["code-excerpt@4.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/code-excerpt/-/code-excerpt-4.0.0.tgz", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="], + + "color-convert": ["color-convert@2.0.1", "https://npm.dev.wixpress.com/color-convert/-/color-convert-2.0.1.tgz", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "https://npm.dev.wixpress.com/color-name/-/color-name-1.1.4.tgz", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "https://npm.dev.wixpress.com/combined-stream/-/combined-stream-1.0.8.tgz", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@12.1.0", "https://npm.dev.wixpress.com/commander/-/commander-12.1.0.tgz", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "common-tags": ["common-tags@1.8.2", "https://npm.dev.wixpress.com/common-tags/-/common-tags-1.8.2.tgz", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="], + + "concat-stream": ["concat-stream@2.0.0", "https://npm.dev.wixpress.com/concat-stream/-/concat-stream-2.0.0.tgz", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], + + "content-disposition": ["content-disposition@1.0.1", "https://npm.dev.wixpress.com/content-disposition/-/content-disposition-1.0.1.tgz", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], + + "content-type": ["content-type@1.0.5", "https://npm.dev.wixpress.com/content-type/-/content-type-1.0.5.tgz", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "convert-to-spaces": ["convert-to-spaces@2.0.1", "https://npm.dev.wixpress.com/api/npm/npm-repos/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], + + "cookie": ["cookie@0.7.2", "https://npm.dev.wixpress.com/cookie/-/cookie-0.7.2.tgz", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "https://npm.dev.wixpress.com/cookie-signature/-/cookie-signature-1.2.2.tgz", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.6", "https://npm.dev.wixpress.com/cors/-/cors-2.8.6.tgz", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "https://npm.dev.wixpress.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.2.3", "https://npm.dev.wixpress.com/api/npm/npm-repos/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "debug": ["debug@4.4.3", "https://npm.dev.wixpress.com/debug/-/debug-4.4.3.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "default-browser": ["default-browser@5.5.0", "https://npm.dev.wixpress.com/default-browser/-/default-browser-5.5.0.tgz", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], + + "default-browser-id": ["default-browser-id@5.0.1", "https://npm.dev.wixpress.com/default-browser-id/-/default-browser-id-5.0.1.tgz", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], + + "define-data-property": ["define-data-property@1.1.4", "https://npm.dev.wixpress.com/define-data-property/-/define-data-property-1.1.4.tgz", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-lazy-prop": ["define-lazy-prop@3.0.0", "https://npm.dev.wixpress.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + + "delayed-stream": ["delayed-stream@1.0.0", "https://npm.dev.wixpress.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "depd": ["depd@2.0.0", "https://npm.dev.wixpress.com/depd/-/depd-2.0.0.tgz", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "detect-libc": ["detect-libc@2.1.2", "https://npm.dev.wixpress.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + "dotenv": ["dotenv@17.3.1", "https://npm.dev.wixpress.com/dotenv/-/dotenv-17.3.1.tgz", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], - "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "dunder-proto": ["dunder-proto@1.0.1", "https://npm.dev.wixpress.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "https://npm.dev.wixpress.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], - "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "ee-first": ["ee-first@1.1.1", "https://npm.dev.wixpress.com/ee-first/-/ee-first-1.1.1.tgz", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "ejs": ["ejs@3.1.10", "https://npm.dev.wixpress.com/ejs/-/ejs-3.1.10.tgz", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], - "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], + "emoji-regex": ["emoji-regex@10.6.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + "encodeurl": ["encodeurl@2.0.0", "https://npm.dev.wixpress.com/encodeurl/-/encodeurl-2.0.0.tgz", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], + "engine.io": ["engine.io@6.6.5", "https://npm.dev.wixpress.com/engine.io/-/engine.io-6.6.5.tgz", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3" } }, "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A=="], - "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "engine.io-client": ["engine.io-client@6.6.4", "https://npm.dev.wixpress.com/engine.io-client/-/engine.io-client-6.6.4.tgz", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + "engine.io-parser": ["engine.io-parser@5.2.3", "https://npm.dev.wixpress.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + "environment": ["environment@1.1.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/environment/-/environment-1.1.0.tgz", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "https://npm.dev.wixpress.com/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], - "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], + "es-define-property": ["es-define-property@1.0.1", "https://npm.dev.wixpress.com/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "es-errors": ["es-errors@1.3.0", "https://npm.dev.wixpress.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + "es-module-lexer": ["es-module-lexer@1.7.0", "https://npm.dev.wixpress.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + "es-object-atoms": ["es-object-atoms@1.1.1", "https://npm.dev.wixpress.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "https://npm.dev.wixpress.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "es-toolkit": ["es-toolkit@1.52.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/es-toolkit/-/es-toolkit-1.52.0.tgz", {}, "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA=="], - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + "esbuild": ["esbuild@0.28.0", "https://npm.dev.wixpress.com/esbuild/-/esbuild-0.28.0.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "escalade": ["escalade@3.2.0", "https://npm.dev.wixpress.com/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + "escape-html": ["escape-html@1.0.3", "https://npm.dev.wixpress.com/escape-html/-/escape-html-1.0.3.tgz", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], - "commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "escape-string-regexp": ["escape-string-regexp@2.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], - "common-tags": ["common-tags@1.8.2", "", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="], + "esprima": ["esprima@4.0.1", "https://npm.dev.wixpress.com/esprima/-/esprima-4.0.1.tgz", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], + "estree-walker": ["estree-walker@3.0.3", "https://npm.dev.wixpress.com/estree-walker/-/estree-walker-3.0.3.tgz", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], + "etag": ["etag@1.8.1", "https://npm.dev.wixpress.com/etag/-/etag-1.8.1.tgz", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], - "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "eventemitter3": ["eventemitter3@4.0.7", "https://npm.dev.wixpress.com/eventemitter3/-/eventemitter3-4.0.7.tgz", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], - "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "execa": ["execa@9.6.1", "https://npm.dev.wixpress.com/execa/-/execa-9.6.1.tgz", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], - "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + "expect-type": ["expect-type@1.3.0", "https://npm.dev.wixpress.com/expect-type/-/expect-type-1.3.0.tgz", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], - "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "express": ["express@5.2.1", "https://npm.dev.wixpress.com/express/-/express-5.2.1.tgz", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "fast-glob": ["fast-glob@3.3.3", "https://npm.dev.wixpress.com/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "fastq": ["fastq@1.20.1", "https://npm.dev.wixpress.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], - "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], + "fd-package-json": ["fd-package-json@2.0.0", "https://npm.dev.wixpress.com/fd-package-json/-/fd-package-json-2.0.0.tgz", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="], - "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], + "fdir": ["fdir@6.5.0", "https://npm.dev.wixpress.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + "figures": ["figures@6.1.0", "https://npm.dev.wixpress.com/figures/-/figures-6.1.0.tgz", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], - "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + "filelist": ["filelist@1.0.4", "https://npm.dev.wixpress.com/filelist/-/filelist-1.0.4.tgz", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="], - "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "fill-range": ["fill-range@7.1.1", "https://npm.dev.wixpress.com/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "finalhandler": ["finalhandler@2.1.1", "https://npm.dev.wixpress.com/finalhandler/-/finalhandler-2.1.1.tgz", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "follow-redirects": ["follow-redirects@1.15.11", "https://npm.dev.wixpress.com/follow-redirects/-/follow-redirects-1.15.11.tgz", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], - "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], + "for-each": ["for-each@0.3.5", "https://npm.dev.wixpress.com/for-each/-/for-each-0.3.5.tgz", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - "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=="], + "form-data": ["form-data@4.0.5", "https://npm.dev.wixpress.com/form-data/-/form-data-4.0.5.tgz", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], - "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "formatly": ["formatly@0.3.0", "https://npm.dev.wixpress.com/formatly/-/formatly-0.3.0.tgz", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="], - "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "forwarded": ["forwarded@0.2.0", "https://npm.dev.wixpress.com/forwarded/-/forwarded-0.2.0.tgz", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], + "fresh": ["fresh@2.0.0", "https://npm.dev.wixpress.com/fresh/-/fresh-2.0.0.tgz", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "front-matter": ["front-matter@4.0.2", "https://npm.dev.wixpress.com/front-matter/-/front-matter-4.0.2.tgz", { "dependencies": { "js-yaml": "^3.13.1" } }, "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg=="], - "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "fsevents": ["fsevents@2.3.3", "https://npm.dev.wixpress.com/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "engine.io": ["engine.io@6.6.5", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3" } }, "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A=="], + "function-bind": ["function-bind@1.1.2", "https://npm.dev.wixpress.com/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="], + "generator-function": ["generator-function@2.0.1", "https://npm.dev.wixpress.com/generator-function/-/generator-function-2.0.1.tgz", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], - "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], + "get-caller-file": ["get-caller-file@2.0.5", "https://npm.dev.wixpress.com/get-caller-file/-/get-caller-file-2.0.5.tgz", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], + "get-east-asian-width": ["get-east-asian-width@1.6.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "https://npm.dev.wixpress.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + "get-port": ["get-port@7.1.0", "https://npm.dev.wixpress.com/get-port/-/get-port-7.1.0.tgz", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="], - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "get-proto": ["get-proto@1.0.1", "https://npm.dev.wixpress.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "get-stream": ["get-stream@9.0.1", "https://npm.dev.wixpress.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], - "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + "glob-parent": ["glob-parent@5.1.2", "https://npm.dev.wixpress.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], + "globby": ["globby@16.2.2", "https://npm.dev.wixpress.com/globby/-/globby-16.2.2.tgz", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "fast-glob": "^3.3.3", "ignore": "^7.0.5", "is-path-inside": "^4.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.4.0" } }, "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "gopd": ["gopd@1.2.0", "https://npm.dev.wixpress.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "graphql": ["graphql@16.12.0", "https://npm.dev.wixpress.com/graphql/-/graphql-16.12.0.tgz", {}, "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ=="], - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "has-property-descriptors": ["has-property-descriptors@1.0.2", "https://npm.dev.wixpress.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "has-symbols": ["has-symbols@1.1.0", "https://npm.dev.wixpress.com/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "has-tostringtag": ["has-tostringtag@1.0.2", "https://npm.dev.wixpress.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "hasown": ["hasown@2.0.2", "https://npm.dev.wixpress.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], + "headers-polyfill": ["headers-polyfill@4.0.3", "https://npm.dev.wixpress.com/headers-polyfill/-/headers-polyfill-4.0.3.tgz", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="], - "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "http-errors": ["http-errors@2.0.1", "https://npm.dev.wixpress.com/http-errors/-/http-errors-2.0.1.tgz", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + "http-proxy": ["http-proxy@1.18.1", "https://npm.dev.wixpress.com/http-proxy/-/http-proxy-1.18.1.tgz", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="], - "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + "http-proxy-middleware": ["http-proxy-middleware@3.0.5", "https://npm.dev.wixpress.com/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz", { "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", "http-proxy": "^1.18.1", "is-glob": "^4.0.3", "is-plain-object": "^5.0.0", "micromatch": "^4.0.8" } }, "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg=="], - "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + "human-signals": ["human-signals@8.0.1", "https://npm.dev.wixpress.com/human-signals/-/human-signals-8.0.1.tgz", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], - "fd-package-json": ["fd-package-json@2.0.0", "", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="], + "iconv-lite": ["iconv-lite@0.7.2", "https://npm.dev.wixpress.com/iconv-lite/-/iconv-lite-0.7.2.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "ignore": ["ignore@7.0.5", "https://npm.dev.wixpress.com/ignore/-/ignore-7.0.5.tgz", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + "immediate": ["immediate@3.0.6", "https://npm.dev.wixpress.com/immediate/-/immediate-3.0.6.tgz", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], - "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="], + "indent-string": ["indent-string@5.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/indent-string/-/indent-string-5.0.0.tgz", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "inherits": ["inherits@2.0.4", "https://npm.dev.wixpress.com/inherits/-/inherits-2.0.4.tgz", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + "ink": ["ink@5.2.1", "https://npm.dev.wixpress.com/api/npm/npm-repos/ink/-/ink-5.2.1.tgz", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.22.0", "indent-string": "^5.0.0", "is-in-ci": "^1.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.29.0", "scheduler": "^0.23.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0", "react-devtools-core": "^4.19.1" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg=="], - "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "ink-text-input": ["ink-text-input@6.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/ink-text-input/-/ink-text-input-6.0.0.tgz", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="], - "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "https://npm.dev.wixpress.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + "is-arguments": ["is-arguments@1.2.0", "https://npm.dev.wixpress.com/is-arguments/-/is-arguments-1.2.0.tgz", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], - "formatly": ["formatly@0.3.0", "", { "dependencies": { "fd-package-json": "^2.0.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w=="], + "is-callable": ["is-callable@1.2.7", "https://npm.dev.wixpress.com/is-callable/-/is-callable-1.2.7.tgz", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + "is-docker": ["is-docker@3.0.0", "https://npm.dev.wixpress.com/is-docker/-/is-docker-3.0.0.tgz", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + "is-extglob": ["is-extglob@2.1.1", "https://npm.dev.wixpress.com/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - "front-matter": ["front-matter@4.0.2", "", { "dependencies": { "js-yaml": "^3.13.1" } }, "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "is-generator-function": ["is-generator-function@1.1.2", "https://npm.dev.wixpress.com/is-generator-function/-/is-generator-function-1.1.2.tgz", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "is-glob": ["is-glob@4.0.3", "https://npm.dev.wixpress.com/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + "is-in-ci": ["is-in-ci@1.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/is-in-ci/-/is-in-ci-1.0.0.tgz", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="], - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + "is-in-ssh": ["is-in-ssh@1.0.0", "https://npm.dev.wixpress.com/is-in-ssh/-/is-in-ssh-1.0.0.tgz", {}, "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw=="], - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + "is-inside-container": ["is-inside-container@1.0.0", "https://npm.dev.wixpress.com/is-inside-container/-/is-inside-container-1.0.0.tgz", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], - "get-port": ["get-port@7.1.0", "", {}, "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="], + "is-node-process": ["is-node-process@1.2.0", "https://npm.dev.wixpress.com/is-node-process/-/is-node-process-1.2.0.tgz", {}, "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw=="], - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "is-number": ["is-number@7.0.0", "https://npm.dev.wixpress.com/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + "is-path-inside": ["is-path-inside@4.0.0", "https://npm.dev.wixpress.com/is-path-inside/-/is-path-inside-4.0.0.tgz", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="], - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "is-plain-obj": ["is-plain-obj@4.1.0", "https://npm.dev.wixpress.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], - "globby": ["globby@16.2.2", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "fast-glob": "^3.3.3", "ignore": "^7.0.5", "is-path-inside": "^4.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.4.0" } }, "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA=="], + "is-plain-object": ["is-plain-object@5.0.0", "https://npm.dev.wixpress.com/is-plain-object/-/is-plain-object-5.0.0.tgz", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "is-promise": ["is-promise@4.0.0", "https://npm.dev.wixpress.com/is-promise/-/is-promise-4.0.0.tgz", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "graphql": ["graphql@16.12.0", "", {}, "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ=="], + "is-regex": ["is-regex@1.2.1", "https://npm.dev.wixpress.com/is-regex/-/is-regex-1.2.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + "is-stream": ["is-stream@4.0.1", "https://npm.dev.wixpress.com/is-stream/-/is-stream-4.0.1.tgz", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + "is-typed-array": ["is-typed-array@1.1.15", "https://npm.dev.wixpress.com/is-typed-array/-/is-typed-array-1.1.15.tgz", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + "is-unicode-supported": ["is-unicode-supported@2.1.0", "https://npm.dev.wixpress.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "is-wsl": ["is-wsl@3.1.0", "https://npm.dev.wixpress.com/is-wsl/-/is-wsl-3.1.0.tgz", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], - "headers-polyfill": ["headers-polyfill@4.0.3", "", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="], + "isexe": ["isexe@3.1.5", "https://npm.dev.wixpress.com/isexe/-/isexe-3.1.5.tgz", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], - "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "jake": ["jake@10.9.4", "https://npm.dev.wixpress.com/jake/-/jake-10.9.4.tgz", { "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=="], - "http-proxy": ["http-proxy@1.18.1", "", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="], + "jiti": ["jiti@2.6.1", "https://npm.dev.wixpress.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - "http-proxy-middleware": ["http-proxy-middleware@3.0.5", "", { "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", "http-proxy": "^1.18.1", "is-glob": "^4.0.3", "is-plain-object": "^5.0.0", "micromatch": "^4.0.8" } }, "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg=="], + "js-tokens": ["js-tokens@4.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], + "js-yaml": ["js-yaml@4.1.1", "https://npm.dev.wixpress.com/js-yaml/-/js-yaml-4.1.1.tgz", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "json-schema-to-typescript": ["json-schema-to-typescript@15.0.4", "https://npm.dev.wixpress.com/json-schema-to-typescript/-/json-schema-to-typescript-15.0.4.tgz", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.5.5", "@types/json-schema": "^7.0.15", "@types/lodash": "^4.17.7", "is-glob": "^4.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "minimist": "^1.2.8", "prettier": "^3.2.5", "tinyglobby": "^0.2.9" }, "bin": { "json2ts": "dist/src/cli.js" } }, "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ=="], - "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "json5": ["json5@2.2.3", "https://npm.dev.wixpress.com/json5/-/json5-2.2.3.tgz", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], + "jsonwebtoken": ["jsonwebtoken@9.0.3", "https://npm.dev.wixpress.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "jwa": ["jwa@2.0.1", "https://npm.dev.wixpress.com/jwa/-/jwa-2.0.1.tgz", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "jws": ["jws@4.0.1", "https://npm.dev.wixpress.com/jws/-/jws-4.0.1.tgz", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], - "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], + "kleur": ["kleur@4.1.5", "https://npm.dev.wixpress.com/kleur/-/kleur-4.1.5.tgz", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], - "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + "knip": ["knip@5.83.1", "https://npm.dev.wixpress.com/knip/-/knip-5.83.1.tgz", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "jiti": "^2.6.0", "js-yaml": "^4.1.1", "minimist": "^1.2.8", "oxc-resolver": "^11.15.0", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.5.2", "strip-json-comments": "5.0.3", "zod": "^4.1.11" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4 <7" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-av3ZG/Nui6S/BNL8Tmj12yGxYfTnwWnslouW97m40him7o8MwiMjZBY9TPvlEWUci45aVId0/HbgTwSKIDGpMw=="], - "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "ky": ["ky@1.14.3", "https://npm.dev.wixpress.com/ky/-/ky-1.14.3.tgz", {}, "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw=="], - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + "lie": ["lie@3.1.1", "https://npm.dev.wixpress.com/lie/-/lie-3.1.1.tgz", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "localforage": ["localforage@1.10.0", "https://npm.dev.wixpress.com/localforage/-/localforage-1.10.0.tgz", { "dependencies": { "lie": "3.1.1" } }, "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg=="], - "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + "lodash": ["lodash@4.17.23", "https://npm.dev.wixpress.com/lodash/-/lodash-4.17.23.tgz", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "lodash.includes": ["lodash.includes@4.3.0", "https://npm.dev.wixpress.com/lodash.includes/-/lodash.includes-4.3.0.tgz", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], - "is-in-ssh": ["is-in-ssh@1.0.0", "", {}, "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw=="], + "lodash.isboolean": ["lodash.isboolean@3.0.3", "https://npm.dev.wixpress.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], - "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + "lodash.isinteger": ["lodash.isinteger@4.0.4", "https://npm.dev.wixpress.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], - "is-node-process": ["is-node-process@1.2.0", "", {}, "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw=="], + "lodash.isnumber": ["lodash.isnumber@3.0.3", "https://npm.dev.wixpress.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "https://npm.dev.wixpress.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], - "is-path-inside": ["is-path-inside@4.0.0", "", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="], + "lodash.isstring": ["lodash.isstring@4.0.1", "https://npm.dev.wixpress.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + "lodash.once": ["lodash.once@4.1.1", "https://npm.dev.wixpress.com/lodash.once/-/lodash.once-4.1.1.tgz", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], - "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], + "loose-envify": ["loose-envify@1.4.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/loose-envify/-/loose-envify-1.4.0.tgz", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "magic-string": ["magic-string@0.30.21", "https://npm.dev.wixpress.com/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "https://npm.dev.wixpress.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], + "media-typer": ["media-typer@1.1.0", "https://npm.dev.wixpress.com/media-typer/-/media-typer-1.1.0.tgz", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], - "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + "merge-descriptors": ["merge-descriptors@2.0.0", "https://npm.dev.wixpress.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], - "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + "merge2": ["merge2@1.4.1", "https://npm.dev.wixpress.com/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], + "micromatch": ["micromatch@4.0.8", "https://npm.dev.wixpress.com/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + "mime-db": ["mime-db@1.54.0", "https://npm.dev.wixpress.com/mime-db/-/mime-db-1.54.0.tgz", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "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=="], + "mime-types": ["mime-types@3.0.2", "https://npm.dev.wixpress.com/mime-types/-/mime-types-3.0.2.tgz", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "mimic-fn": ["mimic-fn@2.1.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/mimic-fn/-/mimic-fn-2.1.0.tgz", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "miniflare": ["miniflare@4.20260722.0", "https://npm.dev.wixpress.com/miniflare/-/miniflare-4.20260722.0.tgz", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.2", "undici": "7.28.0", "workerd": "1.20260722.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw=="], - "json-schema-to-typescript": ["json-schema-to-typescript@15.0.4", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.5.5", "@types/json-schema": "^7.0.15", "@types/lodash": "^4.17.7", "is-glob": "^4.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "minimist": "^1.2.8", "prettier": "^3.2.5", "tinyglobby": "^0.2.9" }, "bin": { "json2ts": "dist/src/cli.js" } }, "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ=="], + "minimatch": ["minimatch@5.1.6", "https://npm.dev.wixpress.com/minimatch/-/minimatch-5.1.6.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "minimist": ["minimist@1.2.8", "https://npm.dev.wixpress.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], + "minipass": ["minipass@7.1.2", "https://npm.dev.wixpress.com/minipass/-/minipass-7.1.2.tgz", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + "minizlib": ["minizlib@3.1.0", "https://npm.dev.wixpress.com/minizlib/-/minizlib-3.1.0.tgz", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], - "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], + "mkdirp": ["mkdirp@0.5.6", "https://npm.dev.wixpress.com/mkdirp/-/mkdirp-0.5.6.tgz", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], - "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + "ms": ["ms@2.1.3", "https://npm.dev.wixpress.com/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "knip": ["knip@5.83.1", "", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "jiti": "^2.6.0", "js-yaml": "^4.1.1", "minimist": "^1.2.8", "oxc-resolver": "^11.15.0", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.5.2", "strip-json-comments": "5.0.3", "zod": "^4.1.11" }, "peerDependencies": { "@types/node": ">=18", "typescript": ">=5.0.4 <7" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-av3ZG/Nui6S/BNL8Tmj12yGxYfTnwWnslouW97m40him7o8MwiMjZBY9TPvlEWUci45aVId0/HbgTwSKIDGpMw=="], + "msw": ["msw@2.12.10", "https://npm.dev.wixpress.com/msw/-/msw-2.12.10.tgz", { "dependencies": { "@inquirer/confirm": "^5.0.0", "@mswjs/interceptors": "^0.41.2", "@open-draft/deferred-promise": "^2.2.0", "@types/statuses": "^2.0.6", "cookie": "^1.0.2", "graphql": "^16.12.0", "headers-polyfill": "^4.0.2", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.10.1", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.0", "type-fest": "^5.2.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-G3VUymSE0/iegFnuipujpwyTM2GuZAKXNeerUSrG2+Eg391wW63xFs5ixWsK9MWzr1AGoSkYGmyAzNgbR3+urw=="], - "ky": ["ky@1.14.3", "", {}, "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw=="], + "multer": ["multer@2.0.2", "https://npm.dev.wixpress.com/multer/-/multer-2.0.2.tgz", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "mkdirp": "^0.5.6", "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" } }, "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw=="], - "lie": ["lie@3.1.1", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw=="], + "mute-stream": ["mute-stream@2.0.0", "https://npm.dev.wixpress.com/mute-stream/-/mute-stream-2.0.0.tgz", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="], - "localforage": ["localforage@1.10.0", "", { "dependencies": { "lie": "3.1.1" } }, "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg=="], + "nanoid": ["nanoid@5.1.6", "https://npm.dev.wixpress.com/nanoid/-/nanoid-5.1.6.tgz", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], - "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], + "negotiator": ["negotiator@1.0.0", "https://npm.dev.wixpress.com/negotiator/-/negotiator-1.0.0.tgz", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], + "npm-run-path": ["npm-run-path@6.0.0", "https://npm.dev.wixpress.com/npm-run-path/-/npm-run-path-6.0.0.tgz", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], - "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + "object-assign": ["object-assign@4.1.1", "https://npm.dev.wixpress.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], + "object-inspect": ["object-inspect@1.13.4", "https://npm.dev.wixpress.com/object-inspect/-/object-inspect-1.13.4.tgz", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], + "obug": ["obug@2.1.1", "https://npm.dev.wixpress.com/obug/-/obug-2.1.1.tgz", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], - "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], + "on-finished": ["on-finished@2.4.1", "https://npm.dev.wixpress.com/on-finished/-/on-finished-2.4.1.tgz", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], - "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], + "once": ["once@1.4.0", "https://npm.dev.wixpress.com/once/-/once-1.4.0.tgz", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], + "onetime": ["onetime@5.1.2", "https://npm.dev.wixpress.com/api/npm/npm-repos/onetime/-/onetime-5.1.2.tgz", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "open": ["open@11.0.0", "https://npm.dev.wixpress.com/open/-/open-11.0.0.tgz", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + "outdent": ["outdent@0.8.0", "https://npm.dev.wixpress.com/outdent/-/outdent-0.8.0.tgz", {}, "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A=="], - "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + "outvariant": ["outvariant@1.4.3", "https://npm.dev.wixpress.com/outvariant/-/outvariant-1.4.3.tgz", {}, "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA=="], - "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "oxc-resolver": ["oxc-resolver@11.17.1", "https://npm.dev.wixpress.com/oxc-resolver/-/oxc-resolver-11.17.1.tgz", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.17.1", "@oxc-resolver/binding-android-arm64": "11.17.1", "@oxc-resolver/binding-darwin-arm64": "11.17.1", "@oxc-resolver/binding-darwin-x64": "11.17.1", "@oxc-resolver/binding-freebsd-x64": "11.17.1", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.17.1", "@oxc-resolver/binding-linux-arm-musleabihf": "11.17.1", "@oxc-resolver/binding-linux-arm64-gnu": "11.17.1", "@oxc-resolver/binding-linux-arm64-musl": "11.17.1", "@oxc-resolver/binding-linux-ppc64-gnu": "11.17.1", "@oxc-resolver/binding-linux-riscv64-gnu": "11.17.1", "@oxc-resolver/binding-linux-riscv64-musl": "11.17.1", "@oxc-resolver/binding-linux-s390x-gnu": "11.17.1", "@oxc-resolver/binding-linux-x64-gnu": "11.17.1", "@oxc-resolver/binding-linux-x64-musl": "11.17.1", "@oxc-resolver/binding-openharmony-arm64": "11.17.1", "@oxc-resolver/binding-wasm32-wasi": "11.17.1", "@oxc-resolver/binding-win32-arm64-msvc": "11.17.1", "@oxc-resolver/binding-win32-ia32-msvc": "11.17.1", "@oxc-resolver/binding-win32-x64-msvc": "11.17.1" } }, "sha512-pyRXK9kH81zKlirHufkFhOFBZRks8iAMLwPH8gU7lvKFiuzUH9L8MxDEllazwOb8fjXMcWjY1PMDfMJ2/yh5cw=="], - "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + "p-map": ["p-map@7.0.6", "https://npm.dev.wixpress.com/p-map/-/p-map-7.0.6.tgz", {}, "sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg=="], - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "p-wait-for": ["p-wait-for@6.0.0", "https://npm.dev.wixpress.com/p-wait-for/-/p-wait-for-6.0.0.tgz", {}, "sha512-2kKzMtjS8TVcpCOU/gr3vZ4K/WIyS1AsEFXFWapM/0lERCdyTbB6ZeuCIp+cL1aeLZfQoMdZFCBTHiK4I9UtOw=="], - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "parse-ms": ["parse-ms@4.0.0", "https://npm.dev.wixpress.com/parse-ms/-/parse-ms-4.0.0.tgz", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], - "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "parseurl": ["parseurl@1.3.3", "https://npm.dev.wixpress.com/parseurl/-/parseurl-1.3.3.tgz", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - "miniflare": ["miniflare@4.20260722.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.2", "undici": "7.28.0", "workerd": "1.20260722.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw=="], + "partyserver": ["partyserver@0.0.56", "https://npm.dev.wixpress.com/partyserver/-/partyserver-0.0.56.tgz", { "dependencies": { "nanoid": "^5.0.7" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240729.0" } }, "sha512-6zdoS/0iBbYatSJe4WtMoCGWDL1I+pGdVlaHdME/TNBv0592Io0AGKWkEQCutHCkIht32AeNdUR66VpsXBaB/w=="], - "minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + "patch-console": ["patch-console@2.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/patch-console/-/patch-console-2.0.0.tgz", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "path-key": ["path-key@3.1.1", "https://npm.dev.wixpress.com/path-key/-/path-key-3.1.1.tgz", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "path-to-regexp": ["path-to-regexp@8.3.0", "https://npm.dev.wixpress.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], - "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + "pathe": ["pathe@2.0.3", "https://npm.dev.wixpress.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], + "picocolors": ["picocolors@1.1.1", "https://npm.dev.wixpress.com/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "picomatch": ["picomatch@4.0.3", "https://npm.dev.wixpress.com/picomatch/-/picomatch-4.0.3.tgz", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "msw": ["msw@2.12.10", "", { "dependencies": { "@inquirer/confirm": "^5.0.0", "@mswjs/interceptors": "^0.41.2", "@open-draft/deferred-promise": "^2.2.0", "@types/statuses": "^2.0.6", "cookie": "^1.0.2", "graphql": "^16.12.0", "headers-polyfill": "^4.0.2", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.10.1", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.0", "type-fest": "^5.2.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-G3VUymSE0/iegFnuipujpwyTM2GuZAKXNeerUSrG2+Eg391wW63xFs5ixWsK9MWzr1AGoSkYGmyAzNgbR3+urw=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "https://npm.dev.wixpress.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "multer": ["multer@2.0.2", "", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "mkdirp": "^0.5.6", "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" } }, "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw=="], + "postcss": ["postcss@8.5.6", "https://npm.dev.wixpress.com/postcss/-/postcss-8.5.6.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "mute-stream": ["mute-stream@2.0.0", "", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="], + "posthog-node": ["posthog-node@5.21.2", "https://npm.dev.wixpress.com/posthog-node/-/posthog-node-5.21.2.tgz", { "dependencies": { "@posthog/core": "1.10.0" } }, "sha512-Jehlu0KguL1LLyUczCt86OtA5INmeStK3zcgbv1BSyMcNxs0HP3GQogBrYhwhqHsk6JopiFFVpJyZEoXOUMhGw=="], - "nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], + "powershell-utils": ["powershell-utils@0.1.0", "https://npm.dev.wixpress.com/powershell-utils/-/powershell-utils-0.1.0.tgz", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], - "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "prettier": ["prettier@3.8.1", "https://npm.dev.wixpress.com/prettier/-/prettier-3.8.1.tgz", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], - "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], + "pretty-ms": ["pretty-ms@9.3.0", "https://npm.dev.wixpress.com/pretty-ms/-/pretty-ms-9.3.0.tgz", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "proxy-addr": ["proxy-addr@2.0.7", "https://npm.dev.wixpress.com/proxy-addr/-/proxy-addr-2.0.7.tgz", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "https://npm.dev.wixpress.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], - "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + "qs": ["qs@6.14.2", "https://npm.dev.wixpress.com/qs/-/qs-6.14.2.tgz", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], - "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "queue-microtask": ["queue-microtask@1.2.3", "https://npm.dev.wixpress.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "range-parser": ["range-parser@1.2.1", "https://npm.dev.wixpress.com/range-parser/-/range-parser-1.2.1.tgz", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], - "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], + "raw-body": ["raw-body@3.0.2", "https://npm.dev.wixpress.com/raw-body/-/raw-body-3.0.2.tgz", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], - "outdent": ["outdent@0.8.0", "", {}, "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A=="], + "react": ["react@18.3.1", "https://npm.dev.wixpress.com/api/npm/npm-repos/react/-/react-18.3.1.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], - "outvariant": ["outvariant@1.4.3", "", {}, "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA=="], + "react-reconciler": ["react-reconciler@0.29.2", "https://npm.dev.wixpress.com/api/npm/npm-repos/react-reconciler/-/react-reconciler-0.29.2.tgz", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="], - "oxc-resolver": ["oxc-resolver@11.17.1", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.17.1", "@oxc-resolver/binding-android-arm64": "11.17.1", "@oxc-resolver/binding-darwin-arm64": "11.17.1", "@oxc-resolver/binding-darwin-x64": "11.17.1", "@oxc-resolver/binding-freebsd-x64": "11.17.1", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.17.1", "@oxc-resolver/binding-linux-arm-musleabihf": "11.17.1", "@oxc-resolver/binding-linux-arm64-gnu": "11.17.1", "@oxc-resolver/binding-linux-arm64-musl": "11.17.1", "@oxc-resolver/binding-linux-ppc64-gnu": "11.17.1", "@oxc-resolver/binding-linux-riscv64-gnu": "11.17.1", "@oxc-resolver/binding-linux-riscv64-musl": "11.17.1", "@oxc-resolver/binding-linux-s390x-gnu": "11.17.1", "@oxc-resolver/binding-linux-x64-gnu": "11.17.1", "@oxc-resolver/binding-linux-x64-musl": "11.17.1", "@oxc-resolver/binding-openharmony-arm64": "11.17.1", "@oxc-resolver/binding-wasm32-wasi": "11.17.1", "@oxc-resolver/binding-win32-arm64-msvc": "11.17.1", "@oxc-resolver/binding-win32-ia32-msvc": "11.17.1", "@oxc-resolver/binding-win32-x64-msvc": "11.17.1" } }, "sha512-pyRXK9kH81zKlirHufkFhOFBZRks8iAMLwPH8gU7lvKFiuzUH9L8MxDEllazwOb8fjXMcWjY1PMDfMJ2/yh5cw=="], + "readable-stream": ["readable-stream@3.6.2", "https://npm.dev.wixpress.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "p-map": ["p-map@7.0.6", "", {}, "sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg=="], + "readdirp": ["readdirp@5.0.0", "https://npm.dev.wixpress.com/readdirp/-/readdirp-5.0.0.tgz", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - "p-wait-for": ["p-wait-for@6.0.0", "", {}, "sha512-2kKzMtjS8TVcpCOU/gr3vZ4K/WIyS1AsEFXFWapM/0lERCdyTbB6ZeuCIp+cL1aeLZfQoMdZFCBTHiK4I9UtOw=="], + "require-directory": ["require-directory@2.1.1", "https://npm.dev.wixpress.com/require-directory/-/require-directory-2.1.1.tgz", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + "requires-port": ["requires-port@1.0.0", "https://npm.dev.wixpress.com/requires-port/-/requires-port-1.0.0.tgz", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], - "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "restore-cursor": ["restore-cursor@4.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/restore-cursor/-/restore-cursor-4.0.0.tgz", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], - "partyserver": ["partyserver@0.0.56", "", { "dependencies": { "nanoid": "^5.0.7" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240729.0" } }, "sha512-6zdoS/0iBbYatSJe4WtMoCGWDL1I+pGdVlaHdME/TNBv0592Io0AGKWkEQCutHCkIht32AeNdUR66VpsXBaB/w=="], + "rettime": ["rettime@0.10.1", "https://npm.dev.wixpress.com/rettime/-/rettime-0.10.1.tgz", {}, "sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw=="], - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "reusify": ["reusify@1.1.0", "https://npm.dev.wixpress.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "rollup": ["rollup@4.57.1", "https://npm.dev.wixpress.com/rollup/-/rollup-4.57.1.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "router": ["router@2.2.0", "https://npm.dev.wixpress.com/router/-/router-2.2.0.tgz", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "run-applescript": ["run-applescript@7.1.0", "https://npm.dev.wixpress.com/run-applescript/-/run-applescript-7.1.0.tgz", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "run-parallel": ["run-parallel@1.2.0", "https://npm.dev.wixpress.com/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "safe-buffer": ["safe-buffer@5.2.1", "https://npm.dev.wixpress.com/safe-buffer/-/safe-buffer-5.2.1.tgz", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + "safe-regex-test": ["safe-regex-test@1.1.0", "https://npm.dev.wixpress.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], - "posthog-node": ["posthog-node@5.21.2", "", { "dependencies": { "@posthog/core": "1.10.0" } }, "sha512-Jehlu0KguL1LLyUczCt86OtA5INmeStK3zcgbv1BSyMcNxs0HP3GQogBrYhwhqHsk6JopiFFVpJyZEoXOUMhGw=="], + "safer-buffer": ["safer-buffer@2.1.2", "https://npm.dev.wixpress.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], + "scheduler": ["scheduler@0.23.2", "https://npm.dev.wixpress.com/api/npm/npm-repos/scheduler/-/scheduler-0.23.2.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], + "semver": ["semver@7.7.4", "https://npm.dev.wixpress.com/semver/-/semver-7.7.4.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], + "send": ["send@1.2.1", "https://npm.dev.wixpress.com/send/-/send-1.2.1.tgz", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "serve-static": ["serve-static@2.2.1", "https://npm.dev.wixpress.com/serve-static/-/serve-static-2.2.1.tgz", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], - "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "set-function-length": ["set-function-length@1.2.2", "https://npm.dev.wixpress.com/set-function-length/-/set-function-length-1.2.2.tgz", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - "qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], + "setprototypeof": ["setprototypeof@1.2.0", "https://npm.dev.wixpress.com/setprototypeof/-/setprototypeof-1.2.0.tgz", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "sharp": ["sharp@0.35.2", "https://npm.dev.wixpress.com/sharp/-/sharp-0.35.2.tgz", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.4" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.2", "@img/sharp-darwin-x64": "0.35.2", "@img/sharp-freebsd-wasm32": "0.35.2", "@img/sharp-libvips-darwin-arm64": "1.3.1", "@img/sharp-libvips-darwin-x64": "1.3.1", "@img/sharp-libvips-linux-arm": "1.3.1", "@img/sharp-libvips-linux-arm64": "1.3.1", "@img/sharp-libvips-linux-ppc64": "1.3.1", "@img/sharp-libvips-linux-riscv64": "1.3.1", "@img/sharp-libvips-linux-s390x": "1.3.1", "@img/sharp-libvips-linux-x64": "1.3.1", "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", "@img/sharp-libvips-linuxmusl-x64": "1.3.1", "@img/sharp-linux-arm": "0.35.2", "@img/sharp-linux-arm64": "0.35.2", "@img/sharp-linux-ppc64": "0.35.2", "@img/sharp-linux-riscv64": "0.35.2", "@img/sharp-linux-s390x": "0.35.2", "@img/sharp-linux-x64": "0.35.2", "@img/sharp-linuxmusl-arm64": "0.35.2", "@img/sharp-linuxmusl-x64": "0.35.2", "@img/sharp-webcontainers-wasm32": "0.35.2", "@img/sharp-win32-arm64": "0.35.2", "@img/sharp-win32-ia32": "0.35.2", "@img/sharp-win32-x64": "0.35.2" } }, "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w=="], - "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + "shebang-command": ["shebang-command@2.0.0", "https://npm.dev.wixpress.com/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "shebang-regex": ["shebang-regex@3.0.0", "https://npm.dev.wixpress.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "side-channel": ["side-channel@1.1.0", "https://npm.dev.wixpress.com/side-channel/-/side-channel-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "side-channel-list": ["side-channel-list@1.0.0", "https://npm.dev.wixpress.com/side-channel-list/-/side-channel-list-1.0.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "side-channel-map": ["side-channel-map@1.0.1", "https://npm.dev.wixpress.com/side-channel-map/-/side-channel-map-1.0.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "https://npm.dev.wixpress.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - "rettime": ["rettime@0.10.1", "", {}, "sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw=="], + "siginfo": ["siginfo@2.0.0", "https://npm.dev.wixpress.com/siginfo/-/siginfo-2.0.0.tgz", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "signal-exit": ["signal-exit@4.1.0", "https://npm.dev.wixpress.com/signal-exit/-/signal-exit-4.1.0.tgz", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], + "sisteransi": ["sisteransi@1.0.5", "https://npm.dev.wixpress.com/sisteransi/-/sisteransi-1.0.5.tgz", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + "slash": ["slash@5.1.0", "https://npm.dev.wixpress.com/slash/-/slash-5.1.0.tgz", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], - "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], + "slice-ansi": ["slice-ansi@7.1.2", "https://npm.dev.wixpress.com/api/npm/npm-repos/slice-ansi/-/slice-ansi-7.1.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "smol-toml": ["smol-toml@1.6.0", "https://npm.dev.wixpress.com/smol-toml/-/smol-toml-1.6.0.tgz", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="], - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "socket.io": ["socket.io@4.8.3", "https://npm.dev.wixpress.com/socket.io/-/socket.io-4.8.3.tgz", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A=="], - "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "socket.io-adapter": ["socket.io-adapter@2.5.6", "https://npm.dev.wixpress.com/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", { "dependencies": { "debug": "~4.4.1", "ws": "~8.18.3" } }, "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ=="], - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "socket.io-client": ["socket.io-client@4.8.3", "https://npm.dev.wixpress.com/socket.io-client/-/socket.io-client-4.8.3.tgz", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "socket.io-parser": ["socket.io-parser@4.2.5", "https://npm.dev.wixpress.com/socket.io-parser/-/socket.io-parser-4.2.5.tgz", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ=="], - "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + "source-map-js": ["source-map-js@1.2.1", "https://npm.dev.wixpress.com/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "sprintf-js": ["sprintf-js@1.0.3", "https://npm.dev.wixpress.com/sprintf-js/-/sprintf-js-1.0.3.tgz", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + "stack-utils": ["stack-utils@2.0.6", "https://npm.dev.wixpress.com/api/npm/npm-repos/stack-utils/-/stack-utils-2.0.6.tgz", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "stackback": ["stackback@0.0.2", "https://npm.dev.wixpress.com/stackback/-/stackback-0.0.2.tgz", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - "sharp": ["sharp@0.35.2", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.4" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.2", "@img/sharp-darwin-x64": "0.35.2", "@img/sharp-freebsd-wasm32": "0.35.2", "@img/sharp-libvips-darwin-arm64": "1.3.1", "@img/sharp-libvips-darwin-x64": "1.3.1", "@img/sharp-libvips-linux-arm": "1.3.1", "@img/sharp-libvips-linux-arm64": "1.3.1", "@img/sharp-libvips-linux-ppc64": "1.3.1", "@img/sharp-libvips-linux-riscv64": "1.3.1", "@img/sharp-libvips-linux-s390x": "1.3.1", "@img/sharp-libvips-linux-x64": "1.3.1", "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", "@img/sharp-libvips-linuxmusl-x64": "1.3.1", "@img/sharp-linux-arm": "0.35.2", "@img/sharp-linux-arm64": "0.35.2", "@img/sharp-linux-ppc64": "0.35.2", "@img/sharp-linux-riscv64": "0.35.2", "@img/sharp-linux-s390x": "0.35.2", "@img/sharp-linux-x64": "0.35.2", "@img/sharp-linuxmusl-arm64": "0.35.2", "@img/sharp-linuxmusl-x64": "0.35.2", "@img/sharp-webcontainers-wasm32": "0.35.2", "@img/sharp-win32-arm64": "0.35.2", "@img/sharp-win32-ia32": "0.35.2", "@img/sharp-win32-x64": "0.35.2" } }, "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w=="], + "statuses": ["statuses@2.0.2", "https://npm.dev.wixpress.com/statuses/-/statuses-2.0.2.tgz", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + "std-env": ["std-env@3.10.0", "https://npm.dev.wixpress.com/std-env/-/std-env-3.10.0.tgz", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "streamsearch": ["streamsearch@1.1.0", "https://npm.dev.wixpress.com/streamsearch/-/streamsearch-1.1.0.tgz", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + "strict-event-emitter": ["strict-event-emitter@0.5.1", "https://npm.dev.wixpress.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="], - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + "string-width": ["string-width@7.2.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/string-width/-/string-width-7.2.0.tgz", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + "string_decoder": ["string_decoder@1.3.0", "https://npm.dev.wixpress.com/string_decoder/-/string_decoder-1.3.0.tgz", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "strip-ansi": ["strip-ansi@7.1.2", "https://npm.dev.wixpress.com/strip-ansi/-/strip-ansi-7.1.2.tgz", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "strip-final-newline": ["strip-final-newline@4.0.0", "https://npm.dev.wixpress.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], - "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "strip-json-comments": ["strip-json-comments@5.0.3", "https://npm.dev.wixpress.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], - "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + "supports-color": ["supports-color@10.2.2", "https://npm.dev.wixpress.com/supports-color/-/supports-color-10.2.2.tgz", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], - "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], + "tagged-tag": ["tagged-tag@1.0.0", "https://npm.dev.wixpress.com/tagged-tag/-/tagged-tag-1.0.0.tgz", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], - "smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="], + "tar": ["tar@7.5.7", "https://npm.dev.wixpress.com/tar/-/tar-7.5.7.tgz", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="], - "socket.io": ["socket.io@4.8.3", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A=="], + "tinybench": ["tinybench@2.9.0", "https://npm.dev.wixpress.com/tinybench/-/tinybench-2.9.0.tgz", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - "socket.io-adapter": ["socket.io-adapter@2.5.6", "", { "dependencies": { "debug": "~4.4.1", "ws": "~8.18.3" } }, "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ=="], + "tinyexec": ["tinyexec@1.0.2", "https://npm.dev.wixpress.com/tinyexec/-/tinyexec-1.0.2.tgz", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], - "socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="], + "tinyglobby": ["tinyglobby@0.2.15", "https://npm.dev.wixpress.com/tinyglobby/-/tinyglobby-0.2.15.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "socket.io-parser": ["socket.io-parser@4.2.5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ=="], + "tinyrainbow": ["tinyrainbow@3.0.3", "https://npm.dev.wixpress.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "tldts": ["tldts@7.0.23", "https://npm.dev.wixpress.com/tldts/-/tldts-7.0.23.tgz", { "dependencies": { "tldts-core": "^7.0.23" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw=="], - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + "tldts-core": ["tldts-core@7.0.23", "https://npm.dev.wixpress.com/tldts-core/-/tldts-core-7.0.23.tgz", {}, "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ=="], - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + "tmp": ["tmp@0.2.5", "https://npm.dev.wixpress.com/tmp/-/tmp-0.2.5.tgz", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], - "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "tmp-promise": ["tmp-promise@3.0.3", "https://npm.dev.wixpress.com/tmp-promise/-/tmp-promise-3.0.3.tgz", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="], - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "to-regex-range": ["to-regex-range@5.0.1", "https://npm.dev.wixpress.com/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], + "toidentifier": ["toidentifier@1.0.1", "https://npm.dev.wixpress.com/toidentifier/-/toidentifier-1.0.1.tgz", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "strict-event-emitter": ["strict-event-emitter@0.5.1", "", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="], + "tough-cookie": ["tough-cookie@6.0.0", "https://npm.dev.wixpress.com/tough-cookie/-/tough-cookie-6.0.0.tgz", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "tslib": ["tslib@2.8.1", "https://npm.dev.wixpress.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "type-fest": ["type-fest@4.41.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/type-fest/-/type-fest-4.41.0.tgz", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "type-is": ["type-is@2.0.1", "https://npm.dev.wixpress.com/type-is/-/type-is-2.0.1.tgz", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], - "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], + "typedarray": ["typedarray@0.0.6", "https://npm.dev.wixpress.com/typedarray/-/typedarray-0.0.6.tgz", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], - "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], + "typescript": ["typescript@5.9.3", "https://npm.dev.wixpress.com/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + "undici": ["undici@7.28.0", "https://npm.dev.wixpress.com/undici/-/undici-7.28.0.tgz", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], - "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], + "undici-types": ["undici-types@6.21.0", "https://npm.dev.wixpress.com/undici-types/-/undici-types-6.21.0.tgz", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="], + "unicorn-magic": ["unicorn-magic@0.4.0", "https://npm.dev.wixpress.com/unicorn-magic/-/unicorn-magic-0.4.0.tgz", {}, "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + "unpipe": ["unpipe@1.0.0", "https://npm.dev.wixpress.com/unpipe/-/unpipe-1.0.0.tgz", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + "until-async": ["until-async@3.0.2", "https://npm.dev.wixpress.com/until-async/-/until-async-3.0.2.tgz", {}, "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw=="], - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "util": ["util@0.12.5", "https://npm.dev.wixpress.com/util/-/util-0.12.5.tgz", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], - "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], + "util-deprecate": ["util-deprecate@1.0.2", "https://npm.dev.wixpress.com/util-deprecate/-/util-deprecate-1.0.2.tgz", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - "tldts": ["tldts@7.0.23", "", { "dependencies": { "tldts-core": "^7.0.23" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw=="], + "uuid": ["uuid@13.0.0", "https://npm.dev.wixpress.com/uuid/-/uuid-13.0.0.tgz", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], - "tldts-core": ["tldts-core@7.0.23", "", {}, "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ=="], + "vary": ["vary@1.1.2", "https://npm.dev.wixpress.com/vary/-/vary-1.1.2.tgz", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + "vite": ["vite@7.3.1", "https://npm.dev.wixpress.com/vite/-/vite-7.3.1.tgz", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], - "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="], + "vitest": ["vitest@4.0.18", "https://npm.dev.wixpress.com/vitest/-/vitest-4.0.18.tgz", { "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", "@vitest/pretty-format": "4.0.18", "@vitest/runner": "4.0.18", "@vitest/snapshot": "4.0.18", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.18", "@vitest/browser-preview": "4.0.18", "@vitest/browser-webdriverio": "4.0.18", "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "walk-up-path": ["walk-up-path@4.0.0", "https://npm.dev.wixpress.com/walk-up-path/-/walk-up-path-4.0.0.tgz", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "which": ["which@4.0.0", "https://npm.dev.wixpress.com/which/-/which-4.0.0.tgz", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], - "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], + "which-typed-array": ["which-typed-array@1.1.20", "https://npm.dev.wixpress.com/which-typed-array/-/which-typed-array-1.1.20.tgz", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "why-is-node-running": ["why-is-node-running@2.3.0", "https://npm.dev.wixpress.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - "type-fest": ["type-fest@5.4.4", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw=="], + "widest-line": ["widest-line@5.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/widest-line/-/widest-line-5.0.0.tgz", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], - "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "workerd": ["workerd@1.20260722.1", "https://npm.dev.wixpress.com/workerd/-/workerd-1.20260722.1.tgz", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260722.1", "@cloudflare/workerd-darwin-arm64": "1.20260722.1", "@cloudflare/workerd-linux-64": "1.20260722.1", "@cloudflare/workerd-linux-arm64": "1.20260722.1", "@cloudflare/workerd-windows-64": "1.20260722.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-NycKuc1x2onvsRfGGpM093vRlLFU2zHDAM0+APpccfg4+gZxDGCH27RmdDvkeBuoZyYqgLo3oAfF6re4mvC3vQ=="], - "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], + "wrap-ansi": ["wrap-ansi@9.0.2", "https://npm.dev.wixpress.com/api/npm/npm-repos/wrap-ansi/-/wrap-ansi-9.0.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "wrappy": ["wrappy@1.0.2", "https://npm.dev.wixpress.com/wrappy/-/wrappy-1.0.2.tgz", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], + "ws": ["ws@8.21.0", "https://npm.dev.wixpress.com/ws/-/ws-8.21.0.tgz", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "wsl-utils": ["wsl-utils@0.3.1", "https://npm.dev.wixpress.com/wsl-utils/-/wsl-utils-0.3.1.tgz", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], - "unicorn-magic": ["unicorn-magic@0.4.0", "", {}, "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw=="], + "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "https://npm.dev.wixpress.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "xtend": ["xtend@4.0.2", "https://npm.dev.wixpress.com/xtend/-/xtend-4.0.2.tgz", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - "until-async": ["until-async@3.0.2", "", {}, "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw=="], + "y18n": ["y18n@5.0.8", "https://npm.dev.wixpress.com/y18n/-/y18n-5.0.8.tgz", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], + "yallist": ["yallist@5.0.0", "https://npm.dev.wixpress.com/yallist/-/yallist-5.0.0.tgz", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "yaml": ["yaml@2.8.2", "https://npm.dev.wixpress.com/yaml/-/yaml-2.8.2.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], - "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + "yargs": ["yargs@17.7.2", "https://npm.dev.wixpress.com/yargs/-/yargs-17.7.2.tgz", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "yargs-parser": ["yargs-parser@21.1.1", "https://npm.dev.wixpress.com/yargs-parser/-/yargs-parser-21.1.1.tgz", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], + "yoctocolors": ["yoctocolors@2.1.2", "https://npm.dev.wixpress.com/yoctocolors/-/yoctocolors-2.1.2.tgz", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], - "vitest": ["vitest@4.0.18", "", { "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", "@vitest/pretty-format": "4.0.18", "@vitest/runner": "4.0.18", "@vitest/snapshot": "4.0.18", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.18", "@vitest/browser-preview": "4.0.18", "@vitest/browser-webdriverio": "4.0.18", "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ=="], + "yoctocolors-cjs": ["yoctocolors-cjs@2.1.3", "https://npm.dev.wixpress.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="], - "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], + "yoga-layout": ["yoga-layout@3.2.1", "https://npm.dev.wixpress.com/api/npm/npm-repos/yoga-layout/-/yoga-layout-3.2.1.tgz", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + "youch": ["youch@4.1.0-beta.10", "https://npm.dev.wixpress.com/youch/-/youch-4.1.0-beta.10.tgz", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], - "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + "youch-core": ["youch-core@0.3.3", "https://npm.dev.wixpress.com/youch-core/-/youch-core-0.3.3.tgz", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "zod": ["zod@3.25.76", "https://npm.dev.wixpress.com/zod/-/zod-3.25.76.tgz", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "workerd": ["workerd@1.20260722.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260722.1", "@cloudflare/workerd-darwin-arm64": "1.20260722.1", "@cloudflare/workerd-linux-64": "1.20260722.1", "@cloudflare/workerd-linux-arm64": "1.20260722.1", "@cloudflare/workerd-windows-64": "1.20260722.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-NycKuc1x2onvsRfGGpM093vRlLFU2zHDAM0+APpccfg4+gZxDGCH27RmdDvkeBuoZyYqgLo3oAfF6re4mvC3vQ=="], + "@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "https://npm.dev.wixpress.com/@emnapi/runtime/-/runtime-1.11.3.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], + "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "https://npm.dev.wixpress.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "base44/@deno/loader": ["@jsr/deno__loader@https://npm.jsr.io/~/11/@jsr/deno__loader/0.5.0.tgz", {}], - "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], + "base44/zod": ["zod@4.3.6", "https://npm.dev.wixpress.com/zod/-/zod-4.3.6.tgz", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], + "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/slice-ansi/-/slice-ansi-5.0.0.tgz", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], - "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], + "cliui/string-width": ["string-width@4.2.3", "https://npm.dev.wixpress.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "cliui/strip-ansi": ["strip-ansi@6.0.1", "https://npm.dev.wixpress.com/strip-ansi/-/strip-ansi-6.0.1.tgz", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "https://npm.dev.wixpress.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + "cross-spawn/which": ["which@2.0.2", "https://npm.dev.wixpress.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + "engine.io/accepts": ["accepts@1.3.8", "https://npm.dev.wixpress.com/accepts/-/accepts-1.3.8.tgz", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + "engine.io/ws": ["ws@8.18.3", "https://npm.dev.wixpress.com/ws/-/ws-8.18.3.tgz", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "engine.io-client/ws": ["ws@8.18.3", "https://npm.dev.wixpress.com/ws/-/ws-8.18.3.tgz", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + "form-data/mime-types": ["mime-types@2.1.35", "https://npm.dev.wixpress.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "yoctocolors-cjs": ["yoctocolors-cjs@2.1.3", "", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="], + "front-matter/js-yaml": ["js-yaml@3.14.2", "https://npm.dev.wixpress.com/js-yaml/-/js-yaml-3.14.2.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], - "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], + "ink/signal-exit": ["signal-exit@3.0.7", "https://npm.dev.wixpress.com/api/npm/npm-repos/signal-exit/-/signal-exit-3.0.7.tgz", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], + "json-schema-to-typescript/@types/lodash": ["@types/lodash@4.17.23", "https://npm.dev.wixpress.com/@types/lodash/-/lodash-4.17.23.tgz", {}, "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA=="], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "knip/zod": ["zod@4.3.6", "https://npm.dev.wixpress.com/zod/-/zod-4.3.6.tgz", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + "micromatch/picomatch": ["picomatch@2.3.1", "https://npm.dev.wixpress.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "base44/@deno/loader": ["@jsr/deno__loader@https://npm.jsr.io/~/11/@jsr/deno__loader/0.5.0.tgz", {}, "sha512-sf/YBwnyAsbyeYYB71Zdj2Ca2Q9tt25EZpAiZdDA9W7Mm3GcpjA2WMeqj19xPIurZK12G3OAsa5yrtPB7E+gvA=="], + "msw/cookie": ["cookie@1.1.1", "https://npm.dev.wixpress.com/cookie/-/cookie-1.1.1.tgz", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "base44/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "msw/path-to-regexp": ["path-to-regexp@6.3.0", "https://npm.dev.wixpress.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], - "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "msw/type-fest": ["type-fest@5.4.4", "https://npm.dev.wixpress.com/type-fest/-/type-fest-5.4.4.tgz", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw=="], - "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "multer/type-is": ["type-is@1.6.18", "https://npm.dev.wixpress.com/type-is/-/type-is-1.6.18.tgz", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], - "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "npm-run-path/path-key": ["path-key@4.0.0", "https://npm.dev.wixpress.com/path-key/-/path-key-4.0.0.tgz", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], - "engine.io/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + "npm-run-path/unicorn-magic": ["unicorn-magic@0.3.0", "https://npm.dev.wixpress.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], - "engine.io/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "postcss/nanoid": ["nanoid@3.3.11", "https://npm.dev.wixpress.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "restore-cursor/signal-exit": ["signal-exit@3.0.7", "https://npm.dev.wixpress.com/api/npm/npm-repos/signal-exit/-/signal-exit-3.0.7.tgz", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "sharp/semver": ["semver@7.8.5", "https://npm.dev.wixpress.com/semver/-/semver-7.8.5.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], - "front-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "https://npm.dev.wixpress.com/api/npm/npm-repos/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], - "json-schema-to-typescript/@types/lodash": ["@types/lodash@4.17.23", "", {}, "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA=="], + "socket.io/accepts": ["accepts@1.3.8", "https://npm.dev.wixpress.com/accepts/-/accepts-1.3.8.tgz", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], - "knip/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "socket.io-adapter/ws": ["ws@8.18.3", "https://npm.dev.wixpress.com/ws/-/ws-8.18.3.tgz", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "vite/esbuild": ["esbuild@0.27.3", "https://npm.dev.wixpress.com/esbuild/-/esbuild-0.27.3.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], - "msw/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "yargs/string-width": ["string-width@4.2.3", "https://npm.dev.wixpress.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "msw/path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], + "youch/cookie": ["cookie@1.1.1", "https://npm.dev.wixpress.com/cookie/-/cookie-1.1.1.tgz", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "multer/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "https://npm.dev.wixpress.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "@inquirer/core/wrap-ansi/string-width": ["string-width@4.2.3", "https://npm.dev.wixpress.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "npm-run-path/unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + "@inquirer/core/wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "https://npm.dev.wixpress.com/strip-ansi/-/strip-ansi-6.0.1.tgz", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://npm.dev.wixpress.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + "cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "https://npm.dev.wixpress.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "socket.io/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "https://npm.dev.wixpress.com/ansi-regex/-/ansi-regex-5.0.1.tgz", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "socket.io-adapter/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "https://npm.dev.wixpress.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "cross-spawn/which/isexe": ["isexe@2.0.0", "https://npm.dev.wixpress.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "vite/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], + "engine.io/accepts/mime-types": ["mime-types@2.1.35", "https://npm.dev.wixpress.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "engine.io/accepts/negotiator": ["negotiator@0.6.3", "https://npm.dev.wixpress.com/negotiator/-/negotiator-0.6.3.tgz", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], - "youch/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "https://npm.dev.wixpress.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "front-matter/js-yaml/argparse": ["argparse@1.0.10", "https://npm.dev.wixpress.com/argparse/-/argparse-1.0.10.tgz", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "multer/type-is/media-typer": ["media-typer@0.3.0", "https://npm.dev.wixpress.com/media-typer/-/media-typer-0.3.0.tgz", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], - "engine.io/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "multer/type-is/mime-types": ["mime-types@2.1.35", "https://npm.dev.wixpress.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "engine.io/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + "socket.io/accepts/mime-types": ["mime-types@2.1.35", "https://npm.dev.wixpress.com/mime-types/-/mime-types-2.1.35.tgz", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "socket.io/accepts/negotiator": ["negotiator@0.6.3", "https://npm.dev.wixpress.com/negotiator/-/negotiator-0.6.3.tgz", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], - "front-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], - "multer/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "https://npm.dev.wixpress.com/@esbuild/android-arm/-/android-arm-0.27.3.tgz", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], - "multer/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], - "socket.io/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/android-x64/-/android-x64-0.27.3.tgz", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], - "socket.io/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], - "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], - "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], - "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], - "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], - "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], - "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], - "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], - "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], - "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], - "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], - "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], - "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], - "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], + "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], - "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], - "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], + "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], - "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], - "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], + "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], - "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], - "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], - "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "https://npm.dev.wixpress.com/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], - "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "https://npm.dev.wixpress.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], - "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], + "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://npm.dev.wixpress.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], + "yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "https://npm.dev.wixpress.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], + "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "https://npm.dev.wixpress.com/strip-ansi/-/strip-ansi-6.0.1.tgz", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], + "@inquirer/core/wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://npm.dev.wixpress.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], + "@inquirer/core/wrap-ansi/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "https://npm.dev.wixpress.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], + "@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "https://npm.dev.wixpress.com/ansi-regex/-/ansi-regex-5.0.1.tgz", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "engine.io/accepts/mime-types/mime-db": ["mime-db@1.52.0", "https://npm.dev.wixpress.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "engine.io/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "multer/type-is/mime-types/mime-db": ["mime-db@1.52.0", "https://npm.dev.wixpress.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "multer/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "socket.io/accepts/mime-types/mime-db": ["mime-db@1.52.0", "https://npm.dev.wixpress.com/mime-db/-/mime-db-1.52.0.tgz", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "socket.io/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "https://npm.dev.wixpress.com/ansi-regex/-/ansi-regex-5.0.1.tgz", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], } } diff --git a/knip.json b/knip.json index 7e0b54385..293cb4e12 100644 --- a/knip.json +++ b/knip.json @@ -11,7 +11,7 @@ "tests/**/testkit/index.ts" ], "project": [ - "src/**/*.ts", + "src/**/*.{ts,tsx}", "tests/**/*.ts" ], "ignore": [ diff --git a/packages/cli/infra/build.ts b/packages/cli/infra/build.ts index d28f8542f..9c886169c 100644 --- a/packages/cli/infra/build.ts +++ b/packages/cli/infra/build.ts @@ -1,4 +1,4 @@ -import { watch, copyFileSync, mkdirSync } from "node:fs"; +import { copyFileSync, mkdirSync, watch } from "node:fs"; import type { BuildConfig } from "bun"; import chalk from "chalk"; @@ -32,7 +32,10 @@ const copyBackendRuntime = () => { copyFileSync("./backend-runtime/exec.ts", `${outDir}/exec.ts`); // The import map and the module it points at must land next to main.ts — // function-manager.ts resolves the config relative to the wrapper. - copyFileSync("./backend-runtime/import-map.json", `${outDir}/import-map.json`); + copyFileSync( + "./backend-runtime/import-map.json", + `${outDir}/import-map.json`, + ); copyFileSync( "./backend-runtime/base44-runtime.ts", `${outDir}/base44-runtime.ts`, @@ -48,11 +51,30 @@ const copyBackendRuntime = () => { // runtime there. export const RUNTIME_EXTERNALS = ["miniflare", "esbuild", "@deno/loader"]; +// Ink's dev-only react-devtools bridge would otherwise land in the bundle as +// an eager import of a package we don't ship; the code path is dead outside +// DEV=true, so it compiles to an inert stub. +const stubReactDevtools = { + name: "stub-react-devtools", + setup(build: { onResolve: Function; onLoad: Function }) { + build.onResolve({ filter: /^react-devtools-core$/ }, () => ({ + path: "react-devtools-core-stub", + namespace: "stub", + })); + build.onLoad({ filter: /.*/, namespace: "stub" }, () => ({ + contents: "export default {}; export const connectToDevTools = () => {};", + loader: "js", + })); + }, +}; + const runAllBuilds = async () => { const cli = await runBuild({ entrypoints: ["./src/cli/index.ts"], outdir: "./dist/cli", external: RUNTIME_EXTERNALS, + // biome-ignore lint/suspicious/noExplicitAny: BunPlugin's setup type is stricter than the minimal stub needs + plugins: [stubReactDevtools as any], }); const backendRuntimePath = copyBackendRuntime(); return { diff --git a/packages/cli/package.json b/packages/cli/package.json index 2884ff9cd..d452459d1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -55,6 +55,7 @@ "@types/ms": "^2.1.0", "@types/multer": "^2.0.0", "@types/node": "^22.10.5", + "@types/react": "^18", "@vercel/detect-agent": "^1.1.0", "chalk": "^5.6.2", "chokidar": "^5.0.0", @@ -98,6 +99,9 @@ "dependencies": { "@deno/loader": "https://npm.jsr.io/~/11/@jsr/deno__loader/0.5.0.tgz", "esbuild": "0.28.0", - "miniflare": "4.20260722.0" + "ink": "^5", + "ink-text-input": "^6", + "miniflare": "4.20260722.0", + "react": "^18" } } diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 2e7dac403..29237c453 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -42,7 +42,7 @@ function turnOutro(turn: ImportedChatTurn): string { } async function chatAction( - { log, runTask, jsonMode, branchId: explicitBranchId }: CLIContext, + { runTask, jsonMode, branchId: explicitBranchId }: CLIContext, message: string, ): Promise<RunCommandResult> { // Messages must land on the app's working branch: an unscoped send goes to diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts new file mode 100644 index 000000000..92462bc3e --- /dev/null +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -0,0 +1,231 @@ +import chalk from "chalk"; +import { + eventLine, + formatDuration, + toolAlias, +} from "@/cli/commands/imported/render.js"; +import { + getFullConversation, + sendImportedChatMessage, +} from "@/core/resources/imported/api.js"; +import { + diffConversation, + newestUserTurn, + newStreamState, +} from "@/core/resources/imported/stream.js"; + +const POLL_MS = 1_000; + +interface RunningTool { + alias: string; + label: string; + summary: string; + startedAt: number; +} + +export interface TurnSettleInfo { + turnIndex: number; + ok: boolean; + backendStatus?: string; + durationMs: number; +} + +type SessionPhase = "awaiting" | "running" | "sending" | "idle"; + +export interface SessionStatus { + phase: SessionPhase; + awaitingLabel?: string; + awaitingSince: number; + turnStartedAt: number | null; + runningTool: { + label: string; + alias: string; + summary: string; + startedAt: number; + others: number; + } | null; + lastTurnMs: number | null; + lastTurnOk: boolean; +} + +interface EngineOptions { + branchId?: string; + awaitingTurnLabel?: string; + onLine: (line: string) => void; + onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; +} + +export interface SessionEngine { + start(primeFirstPoll: boolean): Promise<void>; + stop(): void; + submit(text: string): void; + status(): SessionStatus; + turnRunning(): boolean; +} + +/** + * Everything about a session except pixels: the persistent conversation + * watcher, turn-state derivation from the newest user message's outcome + * stamp, and message submission (including mid-turn sends the backend + * queues). Emits already-styled scrollback lines through `onLine`; the UI + * layer renders them plus a status snapshot. + */ +export function createSessionEngine(options: EngineOptions): SessionEngine { + const running = new Map<string, RunningTool>(); + const diffState = newStreamState(); + + let stopped = false; + let polling = false; + let timer: ReturnType<typeof setInterval> | null = null; + let sendsInFlight = 0; + let activeTurnId: string | null = null; + let turnStartedAt: number | null = null; + let pendingSubmitAt: number | null = null; + let lastTurnMs: number | null = null; + let lastTurnOk = true; + let settledCount = 0; + let awaitingTurn = options.awaitingTurnLabel ?? null; + const awaitingSince = Date.now(); + + const submit = (raw: string) => { + const text = raw.trim(); + if (!text) return; + options.onLine(`${chalk.cyan("❯")} ${chalk.bold(text)}`); + pendingSubmitAt = Date.now(); + sendsInFlight++; + sendImportedChatMessage(text, options.branchId) + .then((turn) => { + if (turn.queued) { + options.onLine(chalk.dim("· queued — runs after the current turn")); + } + }) + .catch((error: unknown) => { + pendingSubmitAt = null; + const message = error instanceof Error ? error.message : String(error); + options.onLine(chalk.red(`✗ send failed: ${message}`)); + }) + .finally(() => { + sendsInFlight--; + }); + }; + + const poll = async (prime: boolean) => { + if (polling) return; + polling = true; + try { + let messages: Awaited<ReturnType<typeof getFullConversation>>; + try { + messages = await getFullConversation(30, options.branchId); + } catch { + return; // Transient — next tick retries. + } + const events = diffConversation(diffState, messages); + if (!prime) { + for (const event of events) { + if (event.kind === "tool_start") { + running.set(event.id, { + alias: toolAlias(event.name), + label: event.label, + summary: event.summary, + startedAt: Date.now(), + }); + continue; + } + let elapsedMs: number | undefined; + if (event.kind === "tool_end") { + const started = running.get(event.id)?.startedAt; + if (started != null) elapsedMs = Date.now() - started; + running.delete(event.id); + } + const line = eventLine(event, elapsedMs); + if (line != null) options.onLine(line); + } + } + + const turn = newestUserTurn(messages); + if (!turn) return; + const kickoffDetection = awaitingTurn != null && activeTurnId === null; + awaitingTurn = null; + if (turn.id !== activeTurnId) { + activeTurnId = turn.id; + if (!turn.settled) { + // A kickoff was already running before this session opened — count + // its time from session start. Later turns count from their submit. + turnStartedAt = + pendingSubmitAt ?? (kickoffDetection ? awaitingSince : Date.now()); + pendingSubmitAt = null; + running.clear(); + } else if (prime) { + // Session opened onto an already-finished turn — nothing to track. + turnStartedAt = null; + } + } + if (turn.settled && turnStartedAt != null && turn.id === activeTurnId) { + const durationMs = Date.now() - turnStartedAt; + turnStartedAt = null; + running.clear(); + lastTurnMs = durationMs; + const ok = !turn.backendStatus?.startsWith("error"); + lastTurnOk = ok; + options.onLine( + ok + ? chalk.dim(`— turn finished · ${formatDuration(durationMs)}`) + : chalk.red( + `— turn failed (${turn.backendStatus ?? "unknown"}) · ${formatDuration(durationMs)}`, + ), + ); + const info: TurnSettleInfo = { + turnIndex: settledCount++, + ok, + backendStatus: turn.backendStatus, + durationMs, + }; + try { + await options.onTurnSettled?.(info); + } catch { + // A settle hook failure must not kill the session. + } + } + } finally { + polling = false; + } + }; + + return { + async start(primeFirstPoll: boolean) { + await poll(primeFirstPoll); + timer = setInterval(() => { + if (!stopped) void poll(false); + }, POLL_MS); + timer.unref?.(); + }, + stop() { + stopped = true; + if (timer) clearInterval(timer); + }, + submit, + status(): SessionStatus { + let phase: SessionPhase = "idle"; + if (awaitingTurn != null) phase = "awaiting"; + else if (turnStartedAt != null) phase = "running"; + else if (sendsInFlight > 0 || pendingSubmitAt != null) phase = "sending"; + let runningTool: SessionStatus["runningTool"] = null; + if (running.size > 0) { + const newest = [...running.values()].at(-1) as RunningTool; + runningTool = { ...newest, others: running.size - 1 }; + } + return { + phase, + awaitingLabel: awaitingTurn ?? undefined, + awaitingSince, + turnStartedAt, + runningTool, + lastTurnMs, + lastTurnOk, + }; + }, + turnRunning() { + return turnStartedAt != null; + }, + }; +} diff --git a/packages/cli/src/cli/commands/imported/session.ts b/packages/cli/src/cli/commands/imported/session.ts deleted file mode 100644 index 0acfc48b5..000000000 --- a/packages/cli/src/cli/commands/imported/session.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { emitKeypressEvents } from "node:readline"; -import chalk from "chalk"; -import { - eventLine, - formatDuration, - idleMusing, - toolAlias, -} from "@/cli/commands/imported/render.js"; -import { - getFullConversation, - sendImportedChatMessage, -} from "@/core/resources/imported/api.js"; -import { - diffConversation, - newestUserTurn, - newStreamState, -} from "@/core/resources/imported/stream.js"; - -const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; -const POLL_MS = 1_000; -const DRAW_MS = 120; - -interface RunningTool { - alias: string; - label: string; - summary: string; - startedAt: number; -} - -interface TurnSettleInfo { - turnIndex: number; - ok: boolean; - backendStatus?: string; - durationMs: number; -} - -interface SessionOptions { - branchId?: string; - /** Live footer lines (repo/editor/preview) — pushing appends to the block. */ - footer: string[]; - /** Swallow whatever the conversation already holds before showing anything — - * false for a fresh create, whose kickoff turn IS the history. */ - primeFirstPoll: boolean; - /** Sent as the first turn right after priming (the `chat` argument). */ - initialMessage?: string; - /** A turn is already starting server-side (the create kickoff): show this - * as the busy label until its user message appears, instead of "ready". */ - awaitingTurnLabel?: string; - onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; -} - -/** - * The Claude-Code-style interactive session: the conversation streams into - * normal scrollback while a redrawn bottom region keeps the footer links, a - * status line (activity + live turn timer + last turn), and an always-present - * input line. Typing works mid-turn — Enter sends, and the backend queues the - * message behind the running turn. One persistent conversation watcher covers - * every turn, including server-queued ones. Ctrl+C clears the input, then - * exits; Ctrl+D exits. TTY only — callers gate on interactivity. - */ -export async function runInteractiveSession( - options: SessionOptions, -): Promise<void> { - const write = (text: string) => process.stdout.write(text); - const footer = options.footer; - const running = new Map<string, RunningTool>(); - const diffState = newStreamState(); - const musingSeed = Math.floor(Math.random() * 97); - const sessionStartedAt = Date.now(); - - let frame = 0; - let drawnLines = 0; - let buffer = ""; - let cursor = 0; - let exitRequested = false; - let sendsInFlight = 0; - let activeTurnId: string | null = null; - let turnStartedAt: number | null = null; - let pendingSubmitAt: number | null = null; - let lastTurnMs: number | null = null; - let lastTurnOk = true; - let settledCount = 0; - let awaitingTurn = options.awaitingTurnLabel ?? null; - const awaitingSince = Date.now(); - - const columns = () => process.stdout.columns || 80; - - const statusLine = (): string => { - if (turnStartedAt != null) { - const turnFor = formatDuration(Date.now() - turnStartedAt); - let activity: string; - if (running.size > 0) { - const newest = [...running.values()].at(-1) as RunningTool; - const toolFor = Math.round((Date.now() - newest.startedAt) / 1000); - const others = running.size > 1 ? ` (+${running.size - 1})` : ""; - const what = - newest.label || - `${newest.alias}${newest.summary ? ` ${newest.summary}` : ""}`; - activity = `${what}${others} · ${toolFor}s`; - } else { - activity = idleMusing(musingSeed); - } - return chalk.dim(`${FRAMES[frame]} ${activity} — turn ${turnFor}`); - } - if (awaitingTurn != null) { - return chalk.dim( - `${FRAMES[frame]} ${awaitingTurn} · ${formatDuration(Date.now() - awaitingSince)}`, - ); - } - if (sendsInFlight > 0 || pendingSubmitAt != null) { - return chalk.dim(`${FRAMES[frame]} sending…`); - } - const last = - lastTurnMs != null - ? ` — last turn ${formatDuration(lastTurnMs)}${lastTurnOk ? "" : " (failed)"}` - : ""; - return chalk.dim(`· ready${last}`); - }; - - const inputLine = (): { text: string; cursorCol: number } => { - const width = Math.max(20, columns() - 4); - const start = Math.max(0, cursor - width + 6); - const visible = buffer.slice(start, start + width); - const cursorCol = cursor - start; - return { text: `${chalk.cyan("❯")} ${visible}`, cursorCol: cursorCol + 2 }; - }; - - // The cursor parks on the input line, one line above the hint at the bottom - // of the block — clearing must step back down first. - let parkedUp = 0; - - const clearBlock = () => { - if (!drawnLines) return; - if (parkedUp > 0) write(`\x1b[${parkedUp}B`); - parkedUp = 0; - write("\r\x1b[2K"); - for (let i = 1; i < drawnLines; i++) write("\x1b[1A\r\x1b[2K"); - drawnLines = 0; - }; - - const rule = () => chalk.dim("─".repeat(Math.min(columns(), 100))); - - const drawBlock = () => { - const input = inputLine(); - const lines = [ - rule(), - ...footer, - statusLine(), - input.text, - chalk.dim(" Enter to send · Ctrl+C to exit (turns keep running)"), - ]; - write(lines.join("\n")); - drawnLines = lines.length; - // Park the terminal cursor where the logical cursor sits in the input line - // (one line above the hint). - write(`\x1b[1A\r`); - if (input.cursorCol > 0) write(`\x1b[${input.cursorCol}C`); - parkedUp = 1; - }; - - const redraw = () => { - clearBlock(); - drawBlock(); - }; - - const printLine = (line: string) => { - clearBlock(); - write(`${line}\n`); - drawBlock(); - }; - - const submit = (raw: string) => { - const text = raw.trim(); - if (!text) return; - printLine(`${chalk.cyan("❯")} ${chalk.bold(text)}`); - pendingSubmitAt = Date.now(); - sendsInFlight++; - sendImportedChatMessage(text, options.branchId) - .then((turn) => { - if (turn.queued) { - printLine(chalk.dim("· queued — runs after the current turn")); - } - }) - .catch((error: unknown) => { - pendingSubmitAt = null; - const message = error instanceof Error ? error.message : String(error); - printLine(chalk.red(`✗ send failed: ${message}`)); - }) - .finally(() => { - sendsInFlight--; - }); - }; - - const onKeypress = ( - str: string | undefined, - key: { name?: string; ctrl?: boolean; meta?: boolean } = {}, - ) => { - if (key.ctrl && key.name === "c") { - if (buffer) { - buffer = ""; - cursor = 0; - } else { - exitRequested = true; - } - redraw(); - return; - } - if (key.ctrl && key.name === "d") { - exitRequested = true; - redraw(); - return; - } - if (key.name === "return" || key.name === "enter") { - const text = buffer; - buffer = ""; - cursor = 0; - submit(text); - return; - } - if (key.name === "backspace") { - if (cursor > 0) { - buffer = buffer.slice(0, cursor - 1) + buffer.slice(cursor); - cursor--; - } - redraw(); - return; - } - if (key.name === "left") { - cursor = Math.max(0, cursor - 1); - redraw(); - return; - } - if (key.name === "right") { - cursor = Math.min(buffer.length, cursor + 1); - redraw(); - return; - } - if (key.ctrl && key.name === "a") { - cursor = 0; - redraw(); - return; - } - if (key.ctrl && key.name === "e") { - cursor = buffer.length; - redraw(); - return; - } - if (key.ctrl && key.name === "u") { - buffer = buffer.slice(cursor); - cursor = 0; - redraw(); - return; - } - if (str && !key.ctrl && !key.meta) { - // Paste arrives as one chunk; newlines inside it become spaces so a - // multi-line paste is one prompt, not an accidental submit spree. - const clean = str.replace(/[\r\n]+/g, " "); - // Drop other control characters. - const printable = clean.replace(/[\x00-\x1f\x7f]/g, ""); - if (!printable) return; - buffer = buffer.slice(0, cursor) + printable + buffer.slice(cursor); - cursor += printable.length; - redraw(); - } - }; - - const poll = async (prime: boolean) => { - let messages: Awaited<ReturnType<typeof getFullConversation>>; - try { - messages = await getFullConversation(30, options.branchId); - } catch { - return; // Transient — next tick retries. - } - const events = diffConversation(diffState, messages); - if (!prime) { - for (const event of events) { - if (event.kind === "tool_start") { - running.set(event.id, { - alias: toolAlias(event.name), - label: event.label, - summary: event.summary, - startedAt: Date.now(), - }); - continue; - } - let elapsedMs: number | undefined; - if (event.kind === "tool_end") { - const started = running.get(event.id)?.startedAt; - if (started != null) elapsedMs = Date.now() - started; - running.delete(event.id); - } - const line = eventLine(event, elapsedMs); - if (line != null) printLine(line); - } - } - - const turn = newestUserTurn(messages); - if (!turn) return; - const kickoffDetection = awaitingTurn != null && activeTurnId === null; - awaitingTurn = null; - if (turn.id !== activeTurnId) { - activeTurnId = turn.id; - if (!turn.settled) { - // A kickoff was already running before this session opened — count its - // time from session start. Later turns count from their own submit. - turnStartedAt = - pendingSubmitAt ?? (kickoffDetection ? awaitingSince : Date.now()); - pendingSubmitAt = null; - running.clear(); - } else if (prime) { - // Session opened onto an already-finished turn — nothing to track. - turnStartedAt = null; - } - } - if (turn.settled && turnStartedAt != null && turn.id === activeTurnId) { - const durationMs = Date.now() - turnStartedAt; - turnStartedAt = null; - running.clear(); - lastTurnMs = durationMs; - const ok = !turn.backendStatus?.startsWith("error"); - lastTurnOk = ok; - const line = ok - ? chalk.dim(`— turn finished · ${formatDuration(durationMs)}`) - : chalk.red( - `— turn failed (${turn.backendStatus ?? "unknown"}) · ${formatDuration(durationMs)}`, - ); - printLine(line); - const info: TurnSettleInfo = { - turnIndex: settledCount++, - ok, - backendStatus: turn.backendStatus, - durationMs, - }; - try { - await options.onTurnSettled?.(info); - } catch { - // A settle hook failure must not kill the session. - } - } - }; - - const stdin = process.stdin; - const supportsRaw = stdin.isTTY === true; - emitKeypressEvents(stdin); - if (supportsRaw) stdin.setRawMode(true); - stdin.resume(); - stdin.on("keypress", onKeypress); - const drawTimer = setInterval(() => { - frame = (frame + 1) % FRAMES.length; - redraw(); - }, DRAW_MS); - drawTimer.unref?.(); - - try { - // Fresh viewport, Claude-Code style: the visible screen clears (shell - // history stays in scrollback) and the session owns what you see. - write("\x1b[2J\x1b[H"); - await poll(options.primeFirstPoll); - if (options.initialMessage) submit(options.initialMessage); - redraw(); - while (!exitRequested) { - await new Promise((resolve) => setTimeout(resolve, POLL_MS)); - if (exitRequested) break; - await poll(false); - } - } finally { - clearInterval(drawTimer); - stdin.off("keypress", onKeypress); - if (supportsRaw) stdin.setRawMode(false); - stdin.pause(); - clearBlock(); - if (footer.length) write(`\n${footer.join("\n")}\n`); - const note = - turnStartedAt != null - ? " — the running turn continues server-side (watch it in the editor)" - : ""; - write( - `${chalk.dim(`session ended · ${formatDuration(Date.now() - sessionStartedAt)}${note}`)}\n`, - ); - } -} diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx new file mode 100644 index 000000000..22abbe7ea --- /dev/null +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -0,0 +1,204 @@ +import chalk from "chalk"; +import { Box, render, Static, Text, useApp, useInput } from "ink"; +import TextInput from "ink-text-input"; +import { useEffect, useReducer, useState } from "react"; +import { formatDuration, idleMusing } from "@/cli/commands/imported/render.js"; +import type { + SessionEngine, + SessionStatus, + TurnSettleInfo, +} from "@/cli/commands/imported/session-engine.js"; +import { createSessionEngine } from "@/cli/commands/imported/session-engine.js"; + +const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; + +interface SessionOptions { + branchId?: string; + /** Live footer lines (repo/editor/preview) — pushing appends to the block. */ + footer: string[]; + /** Swallow whatever the conversation already holds before showing anything — + * false for a fresh create, whose kickoff turn IS the history. */ + primeFirstPoll: boolean; + /** Sent as the first turn right after priming (the `chat` argument). */ + initialMessage?: string; + /** A turn is already starting server-side (the create kickoff): show this + * as the busy label until its user message appears, instead of "ready". */ + awaitingTurnLabel?: string; + onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; +} + +function statusText(status: SessionStatus, musingSeed: number): string { + const frame = FRAMES[Math.floor(Date.now() / 120) % FRAMES.length]; + switch (status.phase) { + case "awaiting": + return chalk.dim( + `${frame} ${status.awaitingLabel} · ${formatDuration(Date.now() - status.awaitingSince)}`, + ); + case "running": { + const turnFor = formatDuration( + Date.now() - (status.turnStartedAt ?? Date.now()), + ); + let activity: string; + const tool = status.runningTool; + if (tool) { + const toolFor = Math.round((Date.now() - tool.startedAt) / 1000); + const others = tool.others > 0 ? ` (+${tool.others})` : ""; + const what = + tool.label || + `${tool.alias}${tool.summary ? ` ${tool.summary}` : ""}`; + activity = `${what}${others} · ${toolFor}s`; + } else { + activity = idleMusing(musingSeed); + } + return chalk.dim(`${frame} ${activity} — turn ${turnFor}`); + } + case "sending": + return chalk.dim(`${frame} sending…`); + case "idle": { + const last = + status.lastTurnMs != null + ? ` — last turn ${formatDuration(status.lastTurnMs)}${status.lastTurnOk ? "" : " (failed)"}` + : ""; + return chalk.dim(`· ready${last}`); + } + } +} + +interface ViewProps { + engine: SessionEngine; + footer: string[]; + subscribe: (listener: (line: string) => void) => () => void; +} + +function SessionView({ engine, footer, subscribe }: ViewProps) { + const { exit } = useApp(); + const [history, setHistory] = useState<string[]>([]); + const [input, setInput] = useState(""); + const [exiting, setExiting] = useState(false); + const [, tick] = useReducer((x: number) => x + 1, 0); + const [musingSeed] = useState(() => Math.floor(Math.random() * 97)); + + useEffect( + () => subscribe((line) => setHistory((h) => [...h, line])), + [subscribe], + ); + useEffect(() => { + const timer = setInterval(tick, 120); + return () => clearInterval(timer); + }, []); + + useInput((char, key) => { + if (key.ctrl && char === "c") { + if (input) setInput(""); + else { + setExiting(true); + exit(); + } + } else if (key.ctrl && char === "d") { + setExiting(true); + exit(); + } + }); + + const width = Math.min(process.stdout.columns || 80, 100); + return ( + <> + <Static items={history}> + {(line, index) => <Text key={`${index}`}>{line}</Text>} + </Static> + {exiting ? ( + <Box flexDirection="column"> + {footer.map((line) => ( + <Text key={line}>{line}</Text> + ))} + </Box> + ) : ( + <Box flexDirection="column"> + <Text dimColor>{"─".repeat(width)}</Text> + {footer.map((line) => ( + <Text key={line}>{line}</Text> + ))} + <Text>{statusText(engine.status(), musingSeed)}</Text> + <Box> + <Text color="cyan">{"❯ "}</Text> + <TextInput + value={input} + onChange={setInput} + onSubmit={(value) => { + if (value.trim()) engine.submit(value); + setInput(""); + }} + /> + </Box> + <Text dimColor> + {" Enter to send · Ctrl+C to exit (turns keep running)"} + </Text> + </Box> + )} + </> + ); +} + +/** + * The Claude-Code-style interactive session, rendered with Ink: history goes + * permanently into scrollback via <Static>, while the bottom region — rule, + * footer links, status line with the live turn timer, input, hints — re-renders + * in place. Typing works mid-turn (the backend queues the message); Ctrl+C + * clears the input, then exits; turns keep running server-side after exit. + * TTY only — callers gate on interactivity. + */ +export async function runInteractiveSession( + options: SessionOptions, +): Promise<void> { + const sessionStartedAt = Date.now(); + const listeners = new Set<(line: string) => void>(); + const buffered: string[] = []; + const onLine = (line: string) => { + if (listeners.size === 0) { + buffered.push(line); + return; + } + for (const listener of listeners) listener(line); + }; + const subscribe = (listener: (line: string) => void) => { + listeners.add(listener); + if (buffered.length) { + for (const line of buffered.splice(0)) listener(line); + } + return () => listeners.delete(listener); + }; + + const engine = createSessionEngine({ + branchId: options.branchId, + awaitingTurnLabel: options.awaitingTurnLabel, + onLine, + onTurnSettled: options.onTurnSettled, + }); + + // Fresh viewport, Claude-Code style: the visible screen clears (shell + // history stays in scrollback) and the session owns what you see. + process.stdout.write("\x1b[2J\x1b[H"); + + const app = render( + <SessionView + engine={engine} + footer={options.footer} + subscribe={subscribe} + />, + { exitOnCtrlC: false }, + ); + + try { + await engine.start(options.primeFirstPoll); + if (options.initialMessage) engine.submit(options.initialMessage); + await app.waitUntilExit(); + } finally { + engine.stop(); + const note = engine.turnRunning() + ? " — the running turn continues server-side (watch it in the editor)" + : ""; + process.stdout.write( + `${chalk.dim(`session ended · ${formatDuration(Date.now() - sessionStartedAt)}${note}`)}\n`, + ); + } +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index f22df6dd2..3cae31354 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "jsx": "react-jsx", "types": ["node", "bun"], "baseUrl": ".", "paths": { From b7f6f5e10ccdf16739dc13f7e598c2f55f687bfb Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:44:52 +0300 Subject: [PATCH 20/73] =?UTF-8?q?feat(imported):=20Claude-Code-style=20bot?= =?UTF-8?q?tom=20widget=20=E2=80=94=20bordered=20input,=20links=20below?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Status line above (✻ musing with turn time, or spinner + running tool), a rounded-border input box, and the footer links + hints as dim lines under it — same layout grammar as Claude Code's bottom region. The rule line is gone; the border carries the separation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 22abbe7ea..aca8089d4 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -32,13 +32,12 @@ function statusText(status: SessionStatus, musingSeed: number): string { switch (status.phase) { case "awaiting": return chalk.dim( - `${frame} ${status.awaitingLabel} · ${formatDuration(Date.now() - status.awaitingSince)}`, + `${frame} ${status.awaitingLabel} (${formatDuration(Date.now() - status.awaitingSince)})`, ); case "running": { const turnFor = formatDuration( Date.now() - (status.turnStartedAt ?? Date.now()), ); - let activity: string; const tool = status.runningTool; if (tool) { const toolFor = Math.round((Date.now() - tool.startedAt) / 1000); @@ -46,20 +45,20 @@ function statusText(status: SessionStatus, musingSeed: number): string { const what = tool.label || `${tool.alias}${tool.summary ? ` ${tool.summary}` : ""}`; - activity = `${what}${others} · ${toolFor}s`; - } else { - activity = idleMusing(musingSeed); + return chalk.dim( + `${frame} ${what}${others} · ${toolFor}s (turn ${turnFor})`, + ); } - return chalk.dim(`${frame} ${activity} — turn ${turnFor}`); + return `${chalk.magenta("✻")} ${chalk.dim(`${idleMusing(musingSeed)} (${turnFor})`)}`; } case "sending": return chalk.dim(`${frame} sending…`); case "idle": { const last = status.lastTurnMs != null - ? ` — last turn ${formatDuration(status.lastTurnMs)}${status.lastTurnOk ? "" : " (failed)"}` + ? ` · last turn ${formatDuration(status.lastTurnMs)}${status.lastTurnOk ? "" : " (failed)"}` : ""; - return chalk.dim(`· ready${last}`); + return chalk.dim(`ready${last}`); } } } @@ -113,13 +112,14 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { ))} </Box> ) : ( - <Box flexDirection="column"> - <Text dimColor>{"─".repeat(width)}</Text> - {footer.map((line) => ( - <Text key={line}>{line}</Text> - ))} + <Box flexDirection="column" marginTop={1}> <Text>{statusText(engine.status(), musingSeed)}</Text> - <Box> + <Box + borderStyle="round" + borderColor="gray" + paddingX={1} + width={width} + > <Text color="cyan">{"❯ "}</Text> <TextInput value={input} @@ -130,6 +130,9 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { }} /> </Box> + {footer.map((line) => ( + <Text key={line}>{` ${line}`}</Text> + ))} <Text dimColor> {" Enter to send · Ctrl+C to exit (turns keep running)"} </Text> From 11ff257b72dc27a8a4b0a36a08abf82063b46113 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:46:31 +0300 Subject: [PATCH 21/73] feat(imported): session opens bottom-anchored, full-height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear the viewport and park the cursor on the bottom row before mounting Ink, so the input widget owns the bottom of the terminal from the first frame and the conversation fills the space above — the Claude Code launch feel, still without the alternate screen (scrollback survives). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index aca8089d4..9cb765de0 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -178,9 +178,12 @@ export async function runInteractiveSession( onTurnSettled: options.onTurnSettled, }); - // Fresh viewport, Claude-Code style: the visible screen clears (shell - // history stays in scrollback) and the session owns what you see. - process.stdout.write("\x1b[2J\x1b[H"); + // Fresh viewport, Claude-Code style: clear the visible screen (shell history + // stays in scrollback) and park the cursor on the BOTTOM row — the widget + // then owns the bottom of the terminal from the first frame, and the + // conversation fills the empty space above it as it streams. + const rows = process.stdout.rows || 24; + process.stdout.write(`\x1b[2J\x1b[${rows};1H`); const app = render( <SessionView From 288d01decba8e1e91fe24370e91d28d46cabddd2 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:50:05 +0300 Subject: [PATCH 22/73] feat(imported): Base44 Code welcome header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sessions open with a bordered welcome box as the first history item — the Base44 Code name and version in the border title, welcome-back with the logged-in identity, the brand-orange dome logo, and the active target host and cwd — scrolling away naturally like Claude Code's header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 9cb765de0..46dd4f78e 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -2,6 +2,7 @@ import chalk from "chalk"; import { Box, render, Static, Text, useApp, useInput } from "ink"; import TextInput from "ink-text-input"; import { useEffect, useReducer, useState } from "react"; +import stripAnsi from "strip-ansi"; import { formatDuration, idleMusing } from "@/cli/commands/imported/render.js"; import type { SessionEngine, @@ -9,6 +10,9 @@ import type { TurnSettleInfo, } from "@/cli/commands/imported/session-engine.js"; import { createSessionEngine } from "@/cli/commands/imported/session-engine.js"; +import { readAuth } from "@/core/auth/config.js"; +import { getBase44ApiUrl } from "@/core/config.js"; +import packageJson from "../../../../package.json"; const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; @@ -150,6 +154,45 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { * clears the input, then exits; turns keep running server-side after exit. * TTY only — callers gate on interactivity. */ +const BRAND_ORANGE = "#E86B3C"; + +/** The Base44 Code welcome box — the session's first history item, so it + * scrolls away naturally like Claude Code's header does. */ +async function buildHeader(): Promise<string> { + const orange = chalk.hex(BRAND_ORANGE); + let who = ""; + try { + const auth = await readAuth(); + who = auth.name || auth.email || ""; + } catch { + // Not logged in yet — the welcome stays generic. + } + const cwd = process.cwd().replace(process.env.HOME ?? "", "~"); + const inner = Math.min((process.stdout.columns || 80) - 2, 64); + const stripLength = (s: string) => stripAnsi(s).length; + const center = (s: string) => { + const pad = Math.max(0, inner - stripLength(s)); + const left = Math.floor(pad / 2); + return `│${" ".repeat(left)}${s}${" ".repeat(pad - left)}│`; + }; + const title = ` ${orange.bold("Base44 Code")} ${chalk.dim(`v${packageJson.version}`)} `; + const top = `╭─${title}${"─".repeat(Math.max(0, inner - stripLength(title) - 1))}╮`; + const rowsOut = [ + top, + center(""), + center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), + center(""), + center(orange("▄▄██████▄▄")), + center(orange("████████████")), + center(""), + center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), + center(chalk.dim(cwd)), + center(""), + `╰${"─".repeat(inner)}╯`, + ]; + return rowsOut.join("\n"); +} + export async function runInteractiveSession( options: SessionOptions, ): Promise<void> { @@ -184,6 +227,7 @@ export async function runInteractiveSession( // conversation fills the empty space above it as it streams. const rows = process.stdout.rows || 24; process.stdout.write(`\x1b[2J\x1b[${rows};1H`); + onLine(await buildHeader()); const app = render( <SessionView From 2ae9d13033cfdfa97aa81523b515fcc531f18ebb Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:53:27 +0300 Subject: [PATCH 23/73] fix(imported): header at top, widget at bottom; full-circle logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session starts at the top row — header first, then a fixed-height dynamic region that bottom-justifies the input widget at the terminal's bottom, conversation filling the space between (the actual Claude Code layout). The mark is now a full circle with its bottom slice cut flat, per the real logo, instead of a dome. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 46dd4f78e..3af70b736 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -71,9 +71,13 @@ interface ViewProps { engine: SessionEngine; footer: string[]; subscribe: (listener: (line: string) => void) => () => void; + /** Fixed height of the dynamic region: the widget bottom-justifies inside + * it, so on a fresh screen the input sits at the terminal's bottom while + * the header stays at the top. */ + bottomHeight: number; } -function SessionView({ engine, footer, subscribe }: ViewProps) { +function SessionView({ engine, footer, subscribe, bottomHeight }: ViewProps) { const { exit } = useApp(); const [history, setHistory] = useState<string[]>([]); const [input, setInput] = useState(""); @@ -116,7 +120,11 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { ))} </Box> ) : ( - <Box flexDirection="column" marginTop={1}> + <Box + flexDirection="column" + justifyContent="flex-end" + height={bottomHeight} + > <Text>{statusText(engine.status(), musingSeed)}</Text> <Box borderStyle="round" @@ -182,8 +190,11 @@ async function buildHeader(): Promise<string> { center(""), center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), center(""), + // The Base44 mark: a full circle with its bottom slice cut flat. center(orange("▄▄██████▄▄")), center(orange("████████████")), + center(orange("████████████")), + center(orange("▀██████████▀")), center(""), center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), center(chalk.dim(cwd)), @@ -222,18 +233,22 @@ export async function runInteractiveSession( }); // Fresh viewport, Claude-Code style: clear the visible screen (shell history - // stays in scrollback) and park the cursor on the BOTTOM row — the widget - // then owns the bottom of the terminal from the first frame, and the - // conversation fills the empty space above it as it streams. + // stays in scrollback) and start at the TOP — the header renders first, and + // the dynamic region's fixed height bottom-justifies the input widget at the + // terminal's bottom, with the conversation filling the space between. const rows = process.stdout.rows || 24; - process.stdout.write(`\x1b[2J\x1b[${rows};1H`); - onLine(await buildHeader()); + process.stdout.write("\x1b[2J\x1b[H"); + const header = await buildHeader(); + onLine(header); + const headerLines = header.split("\n").length; + const bottomHeight = Math.max(10, rows - headerLines - 1); const app = render( <SessionView engine={engine} footer={options.footer} subscribe={subscribe} + bottomHeight={bottomHeight} />, { exitOnCtrlC: false }, ); From a541f679573b0934de1701a0dc90db187d4b2208 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 21:59:21 +0300 Subject: [PATCH 24/73] =?UTF-8?q?feat(imported):=20invented=20repo=20names?= =?UTF-8?q?=20=E2=80=94=20new=20works=20from=20just=20a=20prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit base44 new "online store selling tmnt figures" is now the whole ceremony: a lone argument that reads like a sentence is the prompt, and the repo, directory, and app get an invented name from its words (base44-online-store-selling-x7k) — recognizable in a repo list and renameable later. create --blank without a name invents one the same way; an explicit name still wins everywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 102 ++++++++++++++---- packages/cli/tests/cli/imported.spec.ts | 61 ++++++++++- 2 files changed, 139 insertions(+), 24 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 782fe0f46..5b5eecd33 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -35,13 +35,59 @@ interface CreateImportedOptions { prompt?: string; } +const REPO_NAME_RE = /^[A-Za-z0-9._-]+$/; + +const NAME_STOPWORDS = new Set([ + "a", + "an", + "the", + "and", + "or", + "of", + "for", + "with", + "to", + "in", + "on", + "that", + "this", + "its", + "it", + "my", + "our", + "your", + "me", +]); +const FALLBACK_WORDS = [ + "swift-otter", + "sunny-comet", + "tidy-maple", + "brisk-panda", +]; + +/** A recognizable repo name nobody had to think up: base44-<words from the + * prompt>-<3 chars> — renameable later, unique enough not to collide. */ +function inventRepoName(prompt?: string): string { + const suffix = Math.random().toString(36).slice(2, 5); + const words = + (prompt ?? "") + .toLowerCase() + .match(/[a-z0-9]+/g) + ?.filter((w) => w.length > 2 && !NAME_STOPWORDS.has(w)) + .slice(0, 3) ?? []; + const core = words.length + ? words.join("-") + : FALLBACK_WORDS[Math.floor(Math.random() * FALLBACK_WORDS.length)]; + return `base44-${core}-${suffix}`.slice(0, 60); +} + async function createImportedAction( { log, runTask, jsonMode }: CLIContext, name: string | undefined, options: CreateImportedOptions, ): Promise<RunCommandResult> { // The positional name is the whole identity: directory, GitHub repo, app. - const repoName = options.repoName ?? name; + let repoName = options.repoName ?? name; // A bare name means "from scratch" — --blank stays for explicitness. const blank = options.blank || (Boolean(name) && !options.repo); if (blank && options.repo) { @@ -49,28 +95,26 @@ async function createImportedAction( "A from-scratch create takes no --repo; drop it, or drop --blank to import that repository.", ); } - if (blank && !repoName) { - throw new InvalidInputError( - "Starting from scratch needs a name: `imported create <name>` (or --repo-name <name>).", - ); - } if (!blank && !options.repo) { throw new InvalidInputError( "Pass a <name> to start from scratch, or --repo <github-url> to import a repository.", ); } - if (name && !/^[A-Za-z0-9._-]+$/.test(name)) { + if (name && !REPO_NAME_RE.test(name)) { throw new InvalidInputError( "The name becomes a directory and a GitHub repository — letters, digits, dots, dashes and underscores only.", ); } + if (blank && !repoName) repoName = inventRepoName(options.prompt); - const targetDir = name ? join(process.cwd(), name) : process.cwd(); - if (name) await mkdir(targetDir, { recursive: true }); + // The directory carries the same name — explicit or invented. + const dirName = name ?? (blank ? repoName : undefined); + const targetDir = dirName ? join(process.cwd(), dirName) : process.cwd(); + if (dirName) await mkdir(targetDir, { recursive: true }); if (await appConfigExists(targetDir)) { throw new InvalidInputError( - name - ? `./${name} is already linked to a Base44 app. Pick another name.` + dirName + ? `./${dirName} is already linked to a Base44 app. Pick another name.` : "This directory is already linked to a Base44 app. Run the command from a fresh directory.", ); } @@ -126,7 +170,7 @@ async function createImportedAction( if (!jsonMode) { if (!interactive) for (const line of footer) log.message(line); log.message( - chalk.dim(name ? `linked ./${name}` : `linked ${configPath}`), + chalk.dim(dirName ? `linked ./${dirName}` : `linked ${configPath}`), ); } @@ -158,7 +202,7 @@ async function createImportedAction( } }, }); - return { outroMessage: name ? `Next: cd ${name}` : "Done." }; + return { outroMessage: dirName ? `Next: cd ${dirName}` : "Done." }; } const stream = createTurnStream(false); try { @@ -198,7 +242,7 @@ async function createImportedAction( // Non-interactive runs never draw the pinned block — print the link plainly. if (previewUrl && !jsonMode) log.message(`preview ${previewUrl}`); - const cdHint = name ? ` Next: cd ${name}` : ""; + const cdHint = dirName ? ` Next: cd ${dirName}` : ""; if (finalState === "error") { return { outroMessage: `The first build reported an error — open the editor to see what the agent hit.${cdHint}`, @@ -220,18 +264,36 @@ async function createImportedAction( }; } -/** Top-level sugar: `base44 new <name> ["<prompt>"]` — blank mode with the - * prompt as a positional, no flags to remember. */ +/** Top-level sugar: `base44 new ["<prompt>"]` or `base44 new <name> ["<prompt>"]` + * — blank mode with no flags to remember. A lone argument that reads like a + * sentence is the prompt, and the repo/directory name is invented from it. */ export function getNewCommand(): Base44Command { const command = new Base44Command("new", { requireAppContext: false }); command .description( - "Start a blank app: makes ./<name>, a fresh private GitHub repo named <name>, and builds from your prompt", + "Start a blank app from a prompt — the GitHub repo, directory, and app get an invented base44-* name unless you give one", + ) + .argument( + "[nameOrPrompt]", + "A name for everything, or just the prompt (a name is invented)", ) - .argument("<name>", "One name for the directory, GitHub repo, and app") .argument("[prompt]", "First message for the agent; the build streams live") - .action((ctx: CLIContext, name: string, prompt: string | undefined) => - createImportedAction(ctx, name, { prompt }), + .action( + ( + ctx: CLIContext, + nameOrPrompt: string | undefined, + prompt: string | undefined, + ) => { + // One argument that can't be a repo name is the prompt. + const isName = + nameOrPrompt !== undefined && REPO_NAME_RE.test(nameOrPrompt); + const name = isName ? nameOrPrompt : undefined; + const effectivePrompt = isName ? prompt : (nameOrPrompt ?? prompt); + return createImportedAction(ctx, name, { + blank: true, + prompt: effectivePrompt, + }); + }, ); return command; } diff --git a/packages/cli/tests/cli/imported.spec.ts b/packages/cli/tests/cli/imported.spec.ts index cc69e76e7..362d679a8 100644 --- a/packages/cli/tests/cli/imported.spec.ts +++ b/packages/cli/tests/cli/imported.spec.ts @@ -244,13 +244,66 @@ describe("imported", () => { }); }); - it("create --blank requires a repo name and sends the blank payload", async () => { + it("create --blank with no name invents a base44-* one", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + let sentBody: Record<string, unknown> | undefined; + t.api.mockRoute("POST", "/api/apps", (req, res) => { + sentBody = req.body as Record<string, unknown>; + return res.json({ id: "inv-1", name: "whatever" }); + }); + const result = await t.run("imported", "create", "--blank", "--json"); + t.expectResult(result).toSucceed(); + expect(sentBody?.imported_new_repo_name).toMatch(/^base44-[a-z0-9-]+$/); + expect(sentBody?.name).toBe(sentBody?.imported_new_repo_name); + }); - const missingName = await t.run("imported", "create", "--blank", "--json"); - t.expectResult(missingName).toFail(); - expect(JSON.parse(missingName.stdout).error).toContain("--repo-name"); + it("new with only a prompt invents a name from its words", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + let sentBody: Record<string, unknown> | undefined; + t.api.mockRoute("POST", "/api/apps", (req, res) => { + sentBody = req.body as Record<string, unknown>; + return res.json({ id: "inv-2", name: "whatever" }); + }); + t.api.mockRoute("GET", "/api/apps/inv-2/branches", (_req, res) => + res.json([{ id: "b1", branch_name: "base44/setup-z", status: "active" }]), + ); + t.api.mockRoute( + "GET", + "/api/apps/inv-2/chat/full-conversation", + (_req, res) => + res.json({ + messages: [ + { + id: "u1", + role: "user", + content: "x", + outcome: { backend_status: "success_build" }, + }, + ], + }), + ); + t.api.mockRoute("GET", "/api/apps/inv-2", (_req, res) => + res.json({ id: "inv-2", status: { state: "ready" } }), + ); + t.api.mockRoute("GET", "/api/apps/inv-2/sandbox/preview-url", (_req, res) => + res.json({ preview_url: "3000-z.e2b.app" }), + ); + const result = await t.run( + "new", + "online store selling tmnt figures", + "--json", + ); + t.expectResult(result).toSucceed(); + expect(sentBody?.imported_new_repo_name).toMatch( + /^base44-online-store-selling-[a-z0-9]+$/, + ); + expect(sentBody?.initial_message).toEqual({ + content: "online store selling tmnt figures", + }); + }); + it("create --blank with an explicit repo name sends the blank payload", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); let sentBody: Record<string, unknown> | undefined; t.api.mockRoute("POST", "/api/apps", (req, res) => { sentBody = req.body as Record<string, unknown>; From 5b1c62d48aa067465d63e2405e4a339b1415643e Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:02:38 +0300 Subject: [PATCH 25/73] fix(imported): true CC scroll model, boot inside the page, logo slice placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bottom widget's spacer now SHRINKS as history accumulates (wrap-aware line estimate), hitting zero when the screen fills — so the input stays on the bottom row forever and only the conversation scrolls, instead of the fixed-height box pushing the widget off-screen. The create call runs inside the full-page frame (header top, spinner bottom) rather than a clack task before it. The logo's missing slice moved to one row above the bottom cap, matching the mark. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/create.ts | 38 ++++++--- .../cli/src/cli/commands/imported/session.tsx | 83 +++++++++++++++---- 2 files changed, 90 insertions(+), 31 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 5b5eecd33..003be99b2 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -5,7 +5,10 @@ import { createTurnStream, formatDuration, } from "@/cli/commands/imported/render.js"; -import { runInteractiveSession } from "@/cli/commands/imported/session.js"; +import { + runInteractiveSession, + withBootScreen, +} from "@/cli/commands/imported/session.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import { getBase44ApiUrl } from "@/core/config.js"; @@ -127,18 +130,27 @@ async function createImportedAction( : ((options.repo as string).replace(/\/+$/, "").split("/").pop() ?? "Imported app")); - const created = await runTask( - blank ? "Creating your repository and app" : "Importing the repository", - () => - createImportedApp({ - appName, - sourceMode, - repoUrl: options.repo, - newRepoName: repoName, - branch: options.fromBranch, - prompt: options.prompt, - }), - ); + const interactiveEarly = !jsonMode && process.stdout.isTTY === true; + const createCall = () => + createImportedApp({ + appName, + sourceMode, + repoUrl: options.repo, + newRepoName: repoName, + branch: options.fromBranch, + prompt: options.prompt, + }); + const bootLabel = blank + ? "creating your repository and app" + : "importing the repository"; + // Interactive runs start the full-page frame immediately — the create call + // spins inside it rather than in a clack task outside the page. + const created = interactiveEarly + ? await withBootScreen(bootLabel, createCall) + : await runTask( + blank ? "Creating your repository and app" : "Importing the repository", + createCall, + ); const configPath = await writeAppConfig(targetDir, created.id); // Root discovery (findProjectRoot) keys on a PROJECT config, not .app.jsonc — diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 3af70b736..158f48227 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -71,13 +71,20 @@ interface ViewProps { engine: SessionEngine; footer: string[]; subscribe: (listener: (line: string) => void) => () => void; - /** Fixed height of the dynamic region: the widget bottom-justifies inside - * it, so on a fresh screen the input sits at the terminal's bottom while - * the header stays at the top. */ - bottomHeight: number; } -function SessionView({ engine, footer, subscribe, bottomHeight }: ViewProps) { +/** Terminal lines a history item occupies, wrap-aware (estimate). */ +function lineCount(item: string, columns: number): number { + return item + .split("\n") + .reduce( + (sum, line) => + sum + Math.max(1, Math.ceil(stripAnsi(line).length / columns)), + 0, + ); +} + +function SessionView({ engine, footer, subscribe }: ViewProps) { const { exit } = useApp(); const [history, setHistory] = useState<string[]>([]); const [input, setInput] = useState(""); @@ -120,11 +127,21 @@ function SessionView({ engine, footer, subscribe, bottomHeight }: ViewProps) { ))} </Box> ) : ( - <Box - flexDirection="column" - justifyContent="flex-end" - height={bottomHeight} - > + <Box flexDirection="column"> + {(() => { + // A shrinking spacer keeps the widget on the terminal's bottom row + // until the conversation fills the screen; from then on only the + // conversation scrolls and the widget stays put. + const columns = process.stdout.columns || 80; + const rows = process.stdout.rows || 24; + const used = history.reduce( + (sum, item) => sum + lineCount(item, columns), + 0, + ); + const widgetHeight = 5 + footer.length; // status + bordered input + hint + const spacer = Math.max(0, rows - used - widgetHeight - 1); + return spacer > 0 ? <Box height={spacer} /> : null; + })()} <Text>{statusText(engine.status(), musingSeed)}</Text> <Box borderStyle="round" @@ -190,11 +207,12 @@ async function buildHeader(): Promise<string> { center(""), center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), center(""), - // The Base44 mark: a full circle with its bottom slice cut flat. + // The Base44 mark: a full circle with one slice missing near the bottom. center(orange("▄▄██████▄▄")), center(orange("████████████")), center(orange("████████████")), - center(orange("▀██████████▀")), + center(""), + center(orange("▀▀████████▀▀")), center(""), center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), center(chalk.dim(cwd)), @@ -204,6 +222,40 @@ async function buildHeader(): Promise<string> { return rowsOut.join("\n"); } +/** Run `work` (e.g. the create call) inside the full-page frame: header at + * the top, a spinner on the bottom row — so the session look starts before + * the app even exists. */ +export async function withBootScreen<T>( + label: string, + work: () => Promise<T>, +): Promise<T> { + const header = await buildHeader(); + process.stdout.write("\x1b[2J\x1b[H"); + const rows = process.stdout.rows || 24; + const headerLines = header.split("\n").length; + const BootScreen = () => { + const [, tick] = useReducer((x: number) => x + 1, 0); + useEffect(() => { + const timer = setInterval(tick, 120); + return () => clearInterval(timer); + }, []); + const frame = FRAMES[Math.floor(Date.now() / 120) % FRAMES.length]; + return ( + <Box flexDirection="column"> + <Text>{header}</Text> + <Box height={Math.max(0, rows - headerLines - 2)} /> + <Text>{chalk.dim(`${frame} ${label}`)}</Text> + </Box> + ); + }; + const app = render(<BootScreen />, { exitOnCtrlC: false }); + try { + return await work(); + } finally { + app.unmount(); + } +} + export async function runInteractiveSession( options: SessionOptions, ): Promise<void> { @@ -236,19 +288,14 @@ export async function runInteractiveSession( // stays in scrollback) and start at the TOP — the header renders first, and // the dynamic region's fixed height bottom-justifies the input widget at the // terminal's bottom, with the conversation filling the space between. - const rows = process.stdout.rows || 24; process.stdout.write("\x1b[2J\x1b[H"); - const header = await buildHeader(); - onLine(header); - const headerLines = header.split("\n").length; - const bottomHeight = Math.max(10, rows - headerLines - 1); + onLine(await buildHeader()); const app = render( <SessionView engine={engine} footer={options.footer} subscribe={subscribe} - bottomHeight={bottomHeight} />, { exitOnCtrlC: false }, ); From c045ea22b8b0bc526535950e8fe0c8fa527b3f3c Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:07:36 +0300 Subject: [PATCH 26/73] polish(imported): rasterized circle logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A real circle — computed on a 14×14 subpixel grid with half-blocks doubling vertical resolution — with the slice above the bottom cap removed, per the mark. All rows padded to equal width so per-row centering keeps alignment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 158f48227..38c89526c 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -207,12 +207,15 @@ async function buildHeader(): Promise<string> { center(""), center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), center(""), - // The Base44 mark: a full circle with one slice missing near the bottom. - center(orange("▄▄██████▄▄")), - center(orange("████████████")), - center(orange("████████████")), + // The Base44 mark: a rasterized circle (half-blocks double the vertical + // resolution) with the slice above the bottom cap missing. + center(orange(" ▄▄████▄▄ ")), + center(orange(" ▄██████████▄ ")), + center(orange("▄████████████▄")), + center(orange("██████████████")), + center(orange("▀████████████▀")), center(""), - center(orange("▀▀████████▀▀")), + center(orange(" ▀▀████▀▀ ")), center(""), center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), center(chalk.dim(cwd)), From 6a5dd4f401289f215446ef3e1a66dad5e8aace13 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:13:40 +0300 Subject: [PATCH 27/73] =?UTF-8?q?feat:=20base44=20code=20=E2=80=94=20the?= =?UTF-8?q?=20session=20IS=20the=20entry=20point?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit base44 code opens the Base44 Code page with nothing but the header and the input. In an empty directory the first prompt creates everything — invented repo name, fresh private repo, linked directory, kickoff build — via a genesis engine that swaps itself for a real session engine once the app exists. Inside a linked directory it opens a session on that app instead. TTY required; --json/non-TTY refuse with guidance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/code.ts | 51 +++++++ .../cli/src/cli/commands/imported/create.ts | 59 +++++++ .../cli/commands/imported/session-engine.ts | 3 + .../cli/src/cli/commands/imported/session.tsx | 144 +++++++++++++++++- packages/cli/src/cli/program.ts | 2 + packages/cli/tests/cli/imported.spec.ts | 7 + 6 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/cli/commands/code.ts diff --git a/packages/cli/src/cli/commands/code.ts b/packages/cli/src/cli/commands/code.ts new file mode 100644 index 000000000..45984aeb5 --- /dev/null +++ b/packages/cli/src/cli/commands/code.ts @@ -0,0 +1,51 @@ +import { bootstrapBlankApp } from "@/cli/commands/imported/create.js"; +import { + runGenesisSession, + runInteractiveSession, +} from "@/cli/commands/imported/session.js"; +import type { CLIContext, RunCommandResult } from "@/cli/types.js"; +import { Base44Command } from "@/cli/utils/index.js"; +import { InvalidInputError } from "@/core/errors.js"; +import { appConfigExists, initAppContext } from "@/core/project/app-config.js"; +import { soleActiveBranchId } from "@/core/resources/imported/api.js"; + +async function codeAction(_ctx: CLIContext): Promise<RunCommandResult> { + if (process.stdout.isTTY !== true) { + throw new InvalidInputError( + "base44 code is an interactive session and needs a terminal.", + ); + } + + // Inside a linked app directory, open the session on that app; anywhere + // else, the first prompt creates one from scratch. + if (await appConfigExists(process.cwd())) { + await initAppContext(); + const branchId = await soleActiveBranchId().catch(() => undefined); + await runInteractiveSession({ + branchId, + footer: [], + primeFirstPoll: true, + idleHint: "what should the agent do next?", + }); + return { outroMessage: "Done." }; + } + + const footer: string[] = []; + await runGenesisSession({ + idleHint: "describe the app you want to build", + creatingLabel: "creating your repository and app", + footer, + createApp: (prompt, emit) => bootstrapBlankApp(prompt, footer, emit), + }); + return { outroMessage: "Done." }; +} + +export function getCodeCommand(): Base44Command { + const command = new Base44Command("code", { requireAppContext: false }); + command + .description( + "Open Base44 Code: an interactive agent session — in an empty directory, your first prompt creates the app", + ) + .action(codeAction); + return command; +} diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 003be99b2..93015ad28 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -342,3 +342,62 @@ export function getImportedCreateCommand(): Base44Command { .action(createImportedAction); return command; } + +/** Genesis bootstrap for `base44 code`: turn the session's first prompt into + * a blank app — invented name, fresh repo, linked directory — and hand back + * the engine wiring. Emits progress lines into the session scrollback. */ +export async function bootstrapBlankApp( + prompt: string, + footer: string[], + emit: (line: string) => void, +): Promise<{ + branchId?: string; + awaitingTurnLabel: string; + onTurnSettled: (info: { turnIndex: number; ok: boolean }) => Promise<void>; +}> { + const repoName = inventRepoName(prompt); + const created = await createImportedApp({ + appName: repoName, + sourceMode: "blank", + newRepoName: repoName, + prompt, + }); + const targetDir = join(process.cwd(), repoName); + await mkdir(join(targetDir, "base44"), { recursive: true }); + await writeAppConfig(targetDir, created.id); + try { + await writeFile( + join(targetDir, "base44", "config.jsonc"), + `// Base44 project configuration.\n{\n "name": ${JSON.stringify(repoName)}\n}\n`, + { flag: "wx" }, + ); + } catch { + // Already present — fine. + } + setAppContext({ id: created.id, projectRoot: targetDir }); + + const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; + if (created.imported_repo_url) { + footer.push(chalk.dim(`repo ${created.imported_repo_url}`)); + } + footer.push(chalk.dim(`editor ${editorUrl}`)); + emit(chalk.dim(`linked ./${repoName} (cd ${repoName} after the session)`)); + + const branchId = await soleActiveBranchId().catch(() => undefined); + let previewPushed = false; + return { + branchId, + awaitingTurnLabel: "provisioning the sandbox and starting the build", + onTurnSettled: async ({ turnIndex, ok }) => { + if (turnIndex === 0 && ok && !previewPushed) { + try { + const previewUrl = await getImportedPreviewUrl(); + previewPushed = true; + footer.push(chalk.dim(`preview ${previewUrl}`)); + } catch { + // Preview may still be booting; the editor shows it when up. + } + } + }, + }; +} diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts index 92462bc3e..397a5c493 100644 --- a/packages/cli/src/cli/commands/imported/session-engine.ts +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -35,6 +35,7 @@ type SessionPhase = "awaiting" | "running" | "sending" | "idle"; export interface SessionStatus { phase: SessionPhase; awaitingLabel?: string; + idleHint?: string; awaitingSince: number; turnStartedAt: number | null; runningTool: { @@ -51,6 +52,7 @@ export interface SessionStatus { interface EngineOptions { branchId?: string; awaitingTurnLabel?: string; + idleHint?: string; onLine: (line: string) => void; onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; } @@ -217,6 +219,7 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { return { phase, awaitingLabel: awaitingTurn ?? undefined, + idleHint: options.idleHint, awaitingSince, turnStartedAt, runningTool, diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 38c89526c..fa1abe55b 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -28,6 +28,8 @@ interface SessionOptions { /** A turn is already starting server-side (the create kickoff): show this * as the busy label until its user message appears, instead of "ready". */ awaitingTurnLabel?: string; + /** Shown next to "ready" when nothing is running. */ + idleHint?: string; onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; } @@ -58,11 +60,12 @@ function statusText(status: SessionStatus, musingSeed: number): string { case "sending": return chalk.dim(`${frame} sending…`); case "idle": { + const hint = status.idleHint ? ` — ${status.idleHint}` : ""; const last = status.lastTurnMs != null ? ` · last turn ${formatDuration(status.lastTurnMs)}${status.lastTurnOk ? "" : " (failed)"}` : ""; - return chalk.dim(`ready${last}`); + return chalk.dim(`ready${hint}${last}`); } } } @@ -283,6 +286,7 @@ export async function runInteractiveSession( const engine = createSessionEngine({ branchId: options.branchId, awaitingTurnLabel: options.awaitingTurnLabel, + idleHint: options.idleHint, onLine, onTurnSettled: options.onTurnSettled, }); @@ -317,3 +321,141 @@ export async function runInteractiveSession( ); } } + +interface GenesisAppConfig { + branchId?: string; + awaitingTurnLabel?: string; + onTurnSettled?: (info: TurnSettleInfo) => void | Promise<void>; +} + +interface GenesisOptions { + /** Shown next to "ready" before the first prompt. */ + idleHint: string; + /** Busy label while `createApp` runs. */ + creatingLabel: string; + /** Live footer array — `createApp` pushes the links as they exist. */ + footer: string[]; + /** Turn the first prompt into an app; returns the wiring for the real + * engine, which takes over every later prompt. */ + createApp: ( + prompt: string, + emit: (line: string) => void, + ) => Promise<GenesisAppConfig>; +} + +/** + * A session that starts BEFORE any app exists: the Base44 Code page opens + * with just the header and the input, and the first prompt creates the app + * (repo, directory, kickoff build) — then a real engine takes over, exactly + * as if the session had been opened on it. + */ +export async function runGenesisSession( + options: GenesisOptions, +): Promise<void> { + const sessionStartedAt = Date.now(); + const listeners = new Set<(line: string) => void>(); + const buffered: string[] = []; + const onLine = (line: string) => { + if (listeners.size === 0) { + buffered.push(line); + return; + } + for (const listener of listeners) listener(line); + }; + const subscribe = (listener: (line: string) => void) => { + listeners.add(listener); + if (buffered.length) { + for (const line of buffered.splice(0)) listener(line); + } + return () => listeners.delete(listener); + }; + + let inner: SessionEngine | null = null; + let creating = false; + let creatingSince = 0; + const IDLE_STATUS: SessionStatus = { + phase: "idle", + idleHint: options.idleHint, + awaitingSince: 0, + turnStartedAt: null, + runningTool: null, + lastTurnMs: null, + lastTurnOk: true, + }; + const genesis: SessionEngine = { + async start() {}, + stop() { + inner?.stop(); + }, + submit(text: string) { + if (inner) { + inner.submit(text); + return; + } + if (creating) { + onLine(chalk.dim("· hold on — still creating the app")); + return; + } + creating = true; + creatingSince = Date.now(); + onLine(`${chalk.cyan("❯")} ${chalk.bold(text)}`); + options + .createApp(text, onLine) + .then(async (config) => { + const engine = createSessionEngine({ + branchId: config.branchId, + awaitingTurnLabel: config.awaitingTurnLabel, + onLine, + onTurnSettled: config.onTurnSettled, + }); + await engine.start(false); + inner = engine; + }) + .catch((error: unknown) => { + creating = false; + const message = + error instanceof Error ? error.message : String(error); + onLine(chalk.red(`✗ create failed: ${message}`)); + }); + }, + status(): SessionStatus { + if (inner) return inner.status(); + if (creating) { + return { + ...IDLE_STATUS, + phase: "awaiting", + awaitingLabel: options.creatingLabel, + awaitingSince: creatingSince, + }; + } + return IDLE_STATUS; + }, + turnRunning() { + return inner?.turnRunning() ?? creating; + }, + }; + + process.stdout.write("\x1b[2J\x1b[H"); + onLine(await buildHeader()); + + const app = render( + <SessionView + engine={genesis} + footer={options.footer} + subscribe={subscribe} + />, + { exitOnCtrlC: false }, + ); + + try { + await app.waitUntilExit(); + } finally { + genesis.stop(); + const note = genesis.turnRunning() + ? " — the running turn continues server-side (watch it in the editor)" + : ""; + process.stdout.write( + `${chalk.dim(`session ended · ${formatDuration(Date.now() - sessionStartedAt)}${note}`)}\n`, + ); + } +} diff --git a/packages/cli/src/cli/program.ts b/packages/cli/src/cli/program.ts index 5181fab81..e9f505605 100644 --- a/packages/cli/src/cli/program.ts +++ b/packages/cli/src/cli/program.ts @@ -7,6 +7,7 @@ import { getLoginCommand } from "@/cli/commands/auth/login.js"; import { getLogoutCommand } from "@/cli/commands/auth/logout.js"; import { getWhoamiCommand } from "@/cli/commands/auth/whoami.js"; import { getBranchesCommand } from "@/cli/commands/branches/index.js"; +import { getCodeCommand } from "@/cli/commands/code.js"; import { getConnectorsCommand } from "@/cli/commands/connectors/index.js"; import { getDashboardCommand } from "@/cli/commands/dashboard/index.js"; import { getEntitiesPushCommand } from "@/cli/commands/entities/push.js"; @@ -118,6 +119,7 @@ export function createProgram(context: CLIContext): Command { // Register imported-app commands program.addCommand(getImportedCommand()); program.addCommand(getNewCommand()); + program.addCommand(getCodeCommand()); // Register the target command (staging/preview host selection) program.addCommand(getTargetCommand()); diff --git a/packages/cli/tests/cli/imported.spec.ts b/packages/cli/tests/cli/imported.spec.ts index 362d679a8..b29cb5984 100644 --- a/packages/cli/tests/cli/imported.spec.ts +++ b/packages/cli/tests/cli/imported.spec.ts @@ -244,6 +244,13 @@ describe("imported", () => { }); }); + it("code refuses to run without a terminal", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + const result = await t.run("code", "--json"); + t.expectResult(result).toFail(); + expect(JSON.parse(result.stdout).error).toContain("terminal"); + }); + it("create --blank with no name invents a base44-* one", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); let sentBody: Record<string, unknown> | undefined; From 701455937d7d4ba8be798bc87a5753d313bf1396 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:15:52 +0300 Subject: [PATCH 28/73] fix(code): linked-project detection uses real app-context resolution appConfigExists globs **/.app.jsonc recursively, so a parent directory of several app dirs read as 'linked' and code crashed on init instead of opening genesis mode. Detection is now try-initAppContext, falling back to genesis when the cwd resolves to no project. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/code.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/cli/commands/code.ts b/packages/cli/src/cli/commands/code.ts index 45984aeb5..119fcc19d 100644 --- a/packages/cli/src/cli/commands/code.ts +++ b/packages/cli/src/cli/commands/code.ts @@ -6,7 +6,7 @@ import { import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; import { InvalidInputError } from "@/core/errors.js"; -import { appConfigExists, initAppContext } from "@/core/project/app-config.js"; +import { initAppContext } from "@/core/project/app-config.js"; import { soleActiveBranchId } from "@/core/resources/imported/api.js"; async function codeAction(_ctx: CLIContext): Promise<RunCommandResult> { @@ -16,10 +16,18 @@ async function codeAction(_ctx: CLIContext): Promise<RunCommandResult> { ); } - // Inside a linked app directory, open the session on that app; anywhere - // else, the first prompt creates one from scratch. - if (await appConfigExists(process.cwd())) { + // Inside a linked app project, open the session on that app; anywhere + // else, the first prompt creates one from scratch. Resolution must be the + // real app-context lookup — an existence glob is recursive and would match + // apps in SUBdirectories of an unlinked cwd. + let linked = false; + try { await initAppContext(); + linked = true; + } catch { + // Not a linked project — genesis mode below. + } + if (linked) { const branchId = await soleActiveBranchId().catch(() => undefined); await runInteractiveSession({ branchId, From 319d0fa7fb48a2e60699b88068b343471950aabd Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:17:33 +0300 Subject: [PATCH 29/73] polish(imported): widen the logo to a true visual circle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-rasterized as a 16x14 ellipse (x-radius 7.9 vs y 6.9) — terminal cells run taller than 2:1, so the geometrically round version rendered narrow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index fa1abe55b..846e139e8 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -212,13 +212,13 @@ async function buildHeader(): Promise<string> { center(""), // The Base44 mark: a rasterized circle (half-blocks double the vertical // resolution) with the slice above the bottom cap missing. - center(orange(" ▄▄████▄▄ ")), - center(orange(" ▄██████████▄ ")), - center(orange("▄████████████▄")), - center(orange("██████████████")), - center(orange("▀████████████▀")), + center(orange(" ▄▄██████▄▄ ")), + center(orange(" ▄████████████▄ ")), + center(orange("▄██████████████▄")), + center(orange("████████████████")), + center(orange("▀██████████████▀")), center(""), - center(orange(" ▀▀████▀▀ ")), + center(orange(" ▀▀██████▀▀ ")), center(""), center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), center(chalk.dim(cwd)), From daa414f39560d2f3c8d5e910a4543b0576db2a2b Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:24:49 +0300 Subject: [PATCH 30/73] fix(imported): clean paste in the session input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session enables bracketed paste (mode 2004) — which also stops the terminal's multi-line paste warning — and Ink reads stdin through a proxy that strips the paste markers and flattens pasted newlines/tabs to spaces. A multi-line paste lands as one editable line instead of a submit per line with marker garbage. Sanitizer is pure and covered, including markers split across chunks; typed Enter outside a paste is untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/paste.ts | 93 +++++++++++++++++++ .../cli/src/cli/commands/imported/session.tsx | 15 ++- .../cli/tests/core/imported-stream.spec.ts | 26 ++++++ 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 packages/cli/src/cli/commands/imported/paste.ts diff --git a/packages/cli/src/cli/commands/imported/paste.ts b/packages/cli/src/cli/commands/imported/paste.ts new file mode 100644 index 000000000..4b404b5c6 --- /dev/null +++ b/packages/cli/src/cli/commands/imported/paste.ts @@ -0,0 +1,93 @@ +import { PassThrough } from "node:stream"; + +const START = "\x1b[200~"; +const END = "\x1b[201~"; + +/** Longest suffix of `s` that is a prefix of `marker` — a paste marker can + * arrive split across stdin chunks. */ +function partialSuffix(s: string, marker: string): string { + for (let n = Math.min(marker.length - 1, s.length); n > 0; n--) { + if (marker.startsWith(s.slice(-n))) return s.slice(-n); + } + return ""; +} + +/** Stateful chunk sanitizer for bracketed paste: strips the markers and + * flattens pasted newlines/tabs to spaces so a multi-line paste lands in the + * input as ONE line instead of a submit per line. Pure — unit-testable. */ +export function makePasteSanitizer(): (chunk: string) => string { + let inPaste = false; + let carry = ""; + const clean = (t: string) => + t.replace(/\r\n|\r|\n/g, " ").replace(/\t/g, " "); + return (chunk: string): string => { + let s = carry + chunk; + carry = ""; + let out = ""; + while (s.length > 0) { + if (!inPaste) { + const i = s.indexOf(START); + if (i === -1) { + const tail = partialSuffix(s, START); + out += s.slice(0, s.length - tail.length); + carry = tail; + s = ""; + } else { + out += s.slice(0, i); + s = s.slice(i + START.length); + inPaste = true; + } + } else { + const j = s.indexOf(END); + if (j === -1) { + const tail = partialSuffix(s, END); + out += clean(s.slice(0, s.length - tail.length)); + carry = tail; + s = ""; + } else { + out += clean(s.slice(0, j)); + s = s.slice(j + END.length); + inPaste = false; + } + } + } + return out; + }; +} + +interface PasteFriendlyStdin extends NodeJS.ReadStream { + cleanup(): void; +} + +/** + * A stdin for Ink that understands bracketed paste. The caller enables mode + * 2004 on the terminal (which also silences iTerm's multi-line paste warning); + * this proxy strips the markers and flattens the pasted text before Ink or + * ink-text-input ever see it. + */ +export function createPasteFriendlyStdin( + real: NodeJS.ReadStream, +): PasteFriendlyStdin { + const out = new PassThrough(); + const sanitize = makePasteSanitizer(); + const onData = (buf: Buffer) => { + const text = sanitize(buf.toString("utf8")); + if (text) out.write(text); + }; + real.on("data", onData); + + // biome-ignore lint/suspicious/noExplicitAny: decorating a stream into Ink's expected stdin shape + const proxy = out as any; + proxy.isTTY = true; + proxy.setRawMode = (mode: boolean) => { + real.setRawMode?.(mode); + return proxy; + }; + proxy.ref = () => real.ref?.(); + proxy.unref = () => real.unref?.(); + proxy.cleanup = () => { + real.off("data", onData); + real.pause(); + }; + return proxy as PasteFriendlyStdin; +} diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 846e139e8..9456297cd 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -3,6 +3,7 @@ import { Box, render, Static, Text, useApp, useInput } from "ink"; import TextInput from "ink-text-input"; import { useEffect, useReducer, useState } from "react"; import stripAnsi from "strip-ansi"; +import { createPasteFriendlyStdin } from "@/cli/commands/imported/paste.js"; import { formatDuration, idleMusing } from "@/cli/commands/imported/render.js"; import type { SessionEngine, @@ -298,13 +299,17 @@ export async function runInteractiveSession( process.stdout.write("\x1b[2J\x1b[H"); onLine(await buildHeader()); + // Bracketed paste: the terminal wraps pastes in markers (and drops its + // multi-line paste warning); the stdin proxy flattens them to one line. + process.stdout.write("\x1b[?2004h"); + const stdinProxy = createPasteFriendlyStdin(process.stdin); const app = render( <SessionView engine={engine} footer={options.footer} subscribe={subscribe} />, - { exitOnCtrlC: false }, + { exitOnCtrlC: false, stdin: stdinProxy }, ); try { @@ -312,6 +317,8 @@ export async function runInteractiveSession( if (options.initialMessage) engine.submit(options.initialMessage); await app.waitUntilExit(); } finally { + process.stdout.write("\x1b[?2004l"); + stdinProxy.cleanup(); engine.stop(); const note = engine.turnRunning() ? " — the running turn continues server-side (watch it in the editor)" @@ -438,18 +445,22 @@ export async function runGenesisSession( process.stdout.write("\x1b[2J\x1b[H"); onLine(await buildHeader()); + process.stdout.write("\x1b[?2004h"); + const stdinProxy = createPasteFriendlyStdin(process.stdin); const app = render( <SessionView engine={genesis} footer={options.footer} subscribe={subscribe} />, - { exitOnCtrlC: false }, + { exitOnCtrlC: false, stdin: stdinProxy }, ); try { await app.waitUntilExit(); } finally { + process.stdout.write("\x1b[?2004l"); + stdinProxy.cleanup(); genesis.stop(); const note = genesis.turnRunning() ? " — the running turn continues server-side (watch it in the editor)" diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 18a5fe350..195026712 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -280,6 +280,32 @@ describe("render", () => { }); }); +describe("makePasteSanitizer", () => { + it("strips paste markers and flattens newlines to one line", async () => { + const { makePasteSanitizer } = await import( + "@/cli/commands/imported/paste.js" + ); + const sanitize = makePasteSanitizer(); + expect(sanitize("\x1b[200~line one\nline two\r\nline three\x1b[201~")).toBe( + "line one line two line three", + ); + // Outside a paste, everything passes through untouched (Enter stays Enter). + expect(sanitize("abc\r")).toBe("abc\r"); + }); + + it("handles markers split across chunks", async () => { + const { makePasteSanitizer } = await import( + "@/cli/commands/imported/paste.js" + ); + const sanitize = makePasteSanitizer(); + const out = + sanitize("\x1b[20") + + sanitize("0~hello\nworld\x1b[2") + + sanitize("01~tail"); + expect(out).toBe("hello worldtail"); + }); +}); + describe("turnSettled", () => { const user = (id: string, outcome: unknown): ConversationMessage => ({ id, From 5853a71515a55ed116a2141161b3cbb6b0c4d260 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:27:12 +0300 Subject: [PATCH 31/73] fix(imported): stable layout while the input wraps The bottom spacer now subtracts the input's wrapped row count, so typing or pasting a long prompt grows the box downward-in-place instead of bouncing the whole page height. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 9456297cd..2975774cb 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -135,14 +135,21 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { {(() => { // A shrinking spacer keeps the widget on the terminal's bottom row // until the conversation fills the screen; from then on only the - // conversation scrolls and the widget stays put. + // conversation scrolls and the widget stays put. The input's own + // wrapped height is part of the widget, or typing a long prompt + // would bounce the whole layout. const columns = process.stdout.columns || 80; const rows = process.stdout.rows || 24; const used = history.reduce( (sum, item) => sum + lineCount(item, columns), 0, ); - const widgetHeight = 5 + footer.length; // status + bordered input + hint + const innerWidth = Math.max(10, width - 4); // border + padding + const inputRows = Math.max( + 1, + Math.ceil((input.length + 2) / innerWidth), + ); + const widgetHeight = 4 + inputRows + footer.length; // status + border + hint const spacer = Math.max(0, rows - used - widgetHeight - 1); return spacer > 0 ? <Box height={spacer} /> : null; })()} From ed925c7815280c9e1027a4ea95ddd6e5af98b797 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:39:05 +0300 Subject: [PATCH 32/73] feat(imported): clickable link chips, breathing room, params on their own line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Footer links are now OSC-8 hyperlinks with short labels on ONE line (repo · editor · preview) — no more URL wrapping that bounced the layout and broke click targets mid-URL; non-interactive output keeps full URLs. Every stream item gets a blank line after it, and a titled tool call's raw params render on their own dim line under the title instead of inline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 7 ++++-- .../cli/src/cli/commands/imported/create.ts | 21 +++++++++++------ .../cli/src/cli/commands/imported/render.ts | 23 ++++++++++++------- .../cli/src/cli/commands/imported/session.tsx | 16 ++++++------- .../cli/tests/core/imported-stream.spec.ts | 4 ++-- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 29237c453..473ef31a2 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,5 +1,8 @@ import chalk from "chalk"; -import { createTurnStream } from "@/cli/commands/imported/render.js"; +import { + createTurnStream, + terminalLink, +} from "@/cli/commands/imported/render.js"; import { runInteractiveSession } from "@/cli/commands/imported/session.js"; import type { CLIContext, RunCommandResult } from "@/cli/types.js"; import { Base44Command } from "@/cli/utils/index.js"; @@ -27,7 +30,7 @@ function lastAssistantReply(turn: ImportedChatTurn): string | undefined { function chatFooter(): string[] { try { const editorUrl = `${getBase44ApiUrl()}/apps/${getAppContext().id}/editor/preview`; - return [chalk.dim(`editor ${editorUrl}`)]; + return [terminalLink("editor", editorUrl)]; } catch { return []; } diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index 93015ad28..dfee5e2a6 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import { createTurnStream, formatDuration, + terminalLink, } from "@/cli/commands/imported/render.js"; import { runInteractiveSession, @@ -173,14 +174,20 @@ async function createImportedAction( // front instead. const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; const interactive = !jsonMode && process.stdout.isTTY === true; + // Interactive footers are short OSC-8 hyperlinks (no wrapping, whole-link + // clicks); non-interactive output prints the full URLs instead. const footer = [ ...(created.imported_repo_url - ? [chalk.dim(`repo ${created.imported_repo_url}`)] + ? [terminalLink("repo", created.imported_repo_url)] : []), - chalk.dim(`editor ${editorUrl}`), + terminalLink("editor", editorUrl), ]; if (!jsonMode) { - if (!interactive) for (const line of footer) log.message(line); + if (!interactive) { + if (created.imported_repo_url) + log.message(chalk.dim(`repo ${created.imported_repo_url}`)); + log.message(chalk.dim(`editor ${editorUrl}`)); + } log.message( chalk.dim(dirName ? `linked ./${dirName}` : `linked ${configPath}`), ); @@ -207,7 +214,7 @@ async function createImportedAction( if (turnIndex === 0 && ok && !previewUrl) { try { previewUrl = await getImportedPreviewUrl(); - footer.push(chalk.dim(`preview ${previewUrl}`)); + footer.push(terminalLink("preview", previewUrl)); } catch { // Preview may still be booting; the editor shows it when up. } @@ -378,9 +385,9 @@ export async function bootstrapBlankApp( const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; if (created.imported_repo_url) { - footer.push(chalk.dim(`repo ${created.imported_repo_url}`)); + footer.push(terminalLink("repo", created.imported_repo_url)); } - footer.push(chalk.dim(`editor ${editorUrl}`)); + footer.push(terminalLink("editor", editorUrl)); emit(chalk.dim(`linked ./${repoName} (cd ${repoName} after the session)`)); const branchId = await soleActiveBranchId().catch(() => undefined); @@ -393,7 +400,7 @@ export async function bootstrapBlankApp( try { const previewUrl = await getImportedPreviewUrl(); previewPushed = true; - footer.push(chalk.dim(`preview ${previewUrl}`)); + footer.push(terminalLink("preview", previewUrl)); } catch { // Preview may still be booting; the editor shows it when up. } diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index 2cd73a109..8ca10b57e 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -27,6 +27,12 @@ export function toolAlias(name: string): string { return TOOL_ALIASES[name] ?? name; } +/** OSC 8 terminal hyperlink: a short clickable label instead of a wrapping + * URL — the whole link opens regardless of line width. */ +export function terminalLink(label: string, url: string): string { + return `\u001B]8;;${url}\u0007${chalk.dim.underline(label)}\u001B]8;;\u0007`; +} + export function formatDuration(ms: number): string { const seconds = Math.round(ms / 1000); if (seconds < 90) return `${seconds}s`; @@ -54,18 +60,19 @@ export function eventLine( const alias = toolAlias(event.name); const mark = event.ok ? chalk.green("✓") : chalk.red("✗"); const title = chalk.bold(event.label || alias); - const detail = event.label - ? event.summary - ? ` ${chalk.dim(`${alias}: ${event.summary}`)}` - : ` ${chalk.dim(alias)}` - : event.summary - ? ` ${chalk.dim(event.summary)}` - : ""; const took = elapsedMs != null && elapsedMs >= 3000 ? ` ${chalk.dim(`· ${formatDuration(elapsedMs)}`)}` : ""; - const head = `${mark} ${title}${detail}${took}`; + // With a human title, the raw params move to their own dim line; a bare + // alias keeps a short param (a path) inline. + const inlineDetail = + !event.label && event.summary ? ` ${chalk.dim(event.summary)}` : ""; + const paramsLine = + event.label && event.summary + ? `\n ${chalk.dim(`${alias}: ${event.summary}`)}` + : ""; + const head = `${mark} ${title}${inlineDetail}${took}${paramsLine}`; if (event.ok && (QUIET_OK_RESULTS.has(alias) || !event.result)) { return head; } diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 2975774cb..87635299b 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -97,7 +97,9 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { const [musingSeed] = useState(() => Math.floor(Math.random() * 97)); useEffect( - () => subscribe((line) => setHistory((h) => [...h, line])), + // The trailing newline gives every stream item a blank line after it — + // and the wrap-aware line counter sees it, keeping the spacer honest. + () => subscribe((line) => setHistory((h) => [...h, `${line}\n`])), [subscribe], ); useEffect(() => { @@ -126,9 +128,7 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { </Static> {exiting ? ( <Box flexDirection="column"> - {footer.map((line) => ( - <Text key={line}>{line}</Text> - ))} + {footer.length > 0 && <Text>{footer.join(chalk.dim(" · "))}</Text>} </Box> ) : ( <Box flexDirection="column"> @@ -149,7 +149,7 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { 1, Math.ceil((input.length + 2) / innerWidth), ); - const widgetHeight = 4 + inputRows + footer.length; // status + border + hint + const widgetHeight = 4 + inputRows + (footer.length ? 1 : 0); // status + border + hint + links const spacer = Math.max(0, rows - used - widgetHeight - 1); return spacer > 0 ? <Box height={spacer} /> : null; })()} @@ -170,9 +170,9 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { }} /> </Box> - {footer.map((line) => ( - <Text key={line}>{` ${line}`}</Text> - ))} + {footer.length > 0 && ( + <Text>{` ${footer.join(chalk.dim(" · "))}`}</Text> + )} <Text dimColor> {" Enter to send · Ctrl+C to exit (turns keep running)"} </Text> diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 195026712..4de5e47e1 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -203,7 +203,7 @@ describe("toolMeta", () => { }); describe("render", () => { - it("title-first line: label leads, alias+arg is the dim detail, duration shown", () => { + it("title-first line: label leads, params on their own dim line, duration shown", () => { expect( stripAnsi( eventLine( @@ -220,7 +220,7 @@ describe("render", () => { ) ?? "", ), ).toBe( - '✓ Confirmed Wix login bash: cd /tmp && node bootstrap.mjs · 4s\n {"event":"logged_in"}', + '✓ Confirmed Wix login · 4s\n bash: cd /tmp && node bootstrap.mjs\n {"event":"logged_in"}', ); }); From 917dbff2a79dcefa3a83ad864eb903a84e9344fd Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:41:10 +0300 Subject: [PATCH 33/73] fix(imported): two-tense tool labels split per moment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tool summary argument carries 'Checking X | Checked X' (present form for while-running, past form for done — the editor's contract, documented on the backend schema). The spinner now shows the present form and the settled line the past form, instead of printing the raw pipe-joined pair. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/core/resources/imported/stream.ts | 12 +++++-- .../cli/tests/core/imported-stream.spec.ts | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index 8194302ef..1f89b3c0c 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -115,6 +115,14 @@ export function toolMeta( }; } +/** The tool `summary` argument carries two tenses: "Checking X | Checked X". + * Pick the one matching the moment; a single-form label serves both. */ +function labelTense(label: string, tense: "running" | "done"): string { + const parts = label.split(/\s*\|\s*/); + if (parts.length < 2) return label; + return tense === "running" ? parts[0] : parts[1]; +} + function progressFor(state: StreamState, id: string): MessageProgress { let progress = state.perMessage.get(id); if (!progress) { @@ -170,7 +178,7 @@ export function diffConversation( kind: "tool_start", id: tool.id, name: tool.name, - label: meta.label, + label: labelTense(meta.label, "running"), summary: meta.summary, }); } @@ -182,7 +190,7 @@ export function diffConversation( kind: "tool_end", id: tool.id, name: tool.name, - label: meta.label, + label: labelTense(meta.label, "done"), summary: meta.summary, ok: status === "success", result: oneLine(tool.results, 110), diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 4de5e47e1..93b8f7767 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -77,6 +77,39 @@ describe("diffConversation", () => { expect(diffConversation(state, [settled])).toEqual([]); }); + it("splits two-tense labels: present while running, past when done", () => { + const state = newStreamState(); + const running = assistant({ + id: "m1", + tool_calls: [ + { + id: "t1", + name: "run_shell_command", + arguments_string: + '{"command":"astro dev --help","summary":"Checking astro dev CLI flags | Checked astro dev CLI flags"}', + status: "running", + results: null, + }, + ], + }); + const [start] = diffConversation(state, [running]); + expect(start).toMatchObject({ + kind: "tool_start", + label: "Checking astro dev CLI flags", + }); + const done = assistant({ + ...running, + tool_calls: [ + { ...running.tool_calls?.[0], status: "success", results: "ok" }, + ], + } as ConversationMessage); + const [end] = diffConversation(state, [done]); + expect(end).toMatchObject({ + kind: "tool_end", + label: "Checked astro dev CLI flags", + }); + }); + it("emits only the newly appended part of growing text", () => { const state = newStreamState(); diffConversation(state, [ From 15ed3fffe16d09c8865ef668a5b75f801bbb8f28 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 22:49:44 +0300 Subject: [PATCH 34/73] feat(imported): honest signals for invisible arms and input waits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan/design arms run minutes-long model rounds whose UI is wire-hidden — after 60s with no visible event, the running status says a long private step is rendering in the editor instead of looking frozen. A tool entering waiting_for_user_input prints a yellow one-time notice pointing at the editor (approval cards and secure inputs have no CLI rendering yet). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/render.ts | 6 ++++++ .../src/cli/commands/imported/session-engine.ts | 9 ++++++++- .../cli/src/cli/commands/imported/session.tsx | 10 +++++++++- .../cli/src/core/resources/imported/stream.ts | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index 8ca10b57e..a4af2f978 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -56,6 +56,12 @@ export function eventLine( return event.text; case "tool_start": return null; + case "waiting": { + const what = event.label || toolAlias(event.name); + return chalk.yellow( + `⏸ ${what} — needs your input (answer in the editor)`, + ); + } case "tool_end": { const alias = toolAlias(event.name); const mark = event.ok ? chalk.green("✓") : chalk.red("✗"); diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts index 397a5c493..d367abf1c 100644 --- a/packages/cli/src/cli/commands/imported/session-engine.ts +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -47,6 +47,8 @@ export interface SessionStatus { } | null; lastTurnMs: number | null; lastTurnOk: boolean; + /** ms since the running turn last produced a visible event. */ + quietForMs: number; } interface EngineOptions { @@ -88,6 +90,7 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { let settledCount = 0; let awaitingTurn = options.awaitingTurnLabel ?? null; const awaitingSince = Date.now(); + let lastEventAt = Date.now(); const submit = (raw: string) => { const text = raw.trim(); @@ -140,7 +143,10 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { running.delete(event.id); } const line = eventLine(event, elapsedMs); - if (line != null) options.onLine(line); + if (line != null) { + lastEventAt = Date.now(); + options.onLine(line); + } } } @@ -220,6 +226,7 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { phase, awaitingLabel: awaitingTurn ?? undefined, idleHint: options.idleHint, + quietForMs: Date.now() - lastEventAt, awaitingSince, turnStartedAt, runningTool, diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 87635299b..74bb98757 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -56,7 +56,14 @@ function statusText(status: SessionStatus, musingSeed: number): string { `${frame} ${what}${others} · ${toolFor}s (turn ${turnFor})`, ); } - return `${chalk.magenta("✻")} ${chalk.dim(`${idleMusing(musingSeed)} (${turnFor})`)}`; + // Long silent stretch: some arms (plan/design) run minutes-long model + // calls whose UI renders only in the editor — say so instead of + // looking frozen. + const quiet = + status.quietForMs > 60_000 + ? " · a long private step — details render in the editor" + : ""; + return `${chalk.magenta("✻")} ${chalk.dim(`${idleMusing(musingSeed)} (${turnFor})${quiet}`)}`; } case "sending": return chalk.dim(`${frame} sending…`); @@ -395,6 +402,7 @@ export async function runGenesisSession( runningTool: null, lastTurnMs: null, lastTurnOk: true, + quietForMs: 0, }; const genesis: SessionEngine = { async start() {}, diff --git a/packages/cli/src/core/resources/imported/stream.ts b/packages/cli/src/core/resources/imported/stream.ts index 1f89b3c0c..fadf31935 100644 --- a/packages/cli/src/core/resources/imported/stream.ts +++ b/packages/cli/src/core/resources/imported/stream.ts @@ -4,6 +4,7 @@ import { getFullConversation } from "@/core/resources/imported/api.js"; export type StreamEvent = | { kind: "thinking"; text: string } | { kind: "text"; text: string } + | { kind: "waiting"; id: string; name: string; label: string } | { kind: "tool_start"; id: string; @@ -33,6 +34,7 @@ interface MessageProgress { reasoningLength: number; announcedTools: Map<string, AnnouncedTool>; settledTools: Set<string>; + waitingNotified: Set<string>; } interface StreamState { @@ -131,6 +133,7 @@ function progressFor(state: StreamState, id: string): MessageProgress { reasoningLength: 0, announcedTools: new Map(), settledTools: new Set(), + waitingNotified: new Set(), }; state.perMessage.set(id, progress); } @@ -183,6 +186,19 @@ export function diffConversation( }); } const status = tool.status ?? "running"; + if ( + status === "waiting_for_user_input" && + !progress.waitingNotified.has(tool.id) + ) { + progress.waitingNotified.add(tool.id); + const meta = progress.announcedTools.get(tool.id) as AnnouncedTool; + events.push({ + kind: "waiting", + id: tool.id, + name: tool.name, + label: labelTense(meta.label, "running"), + }); + } if (TOOL_SETTLED.has(status) && !progress.settledTools.has(tool.id)) { progress.settledTools.add(tool.id); const meta = progress.announcedTools.get(tool.id) as AnnouncedTool; From 2c8a7b39f5133952a177bdad16e43981a17b5fdc Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:08:30 +0300 Subject: [PATCH 35/73] =?UTF-8?q?feat(imported):=20alternate-screen=20sess?= =?UTF-8?q?ion=20with=20internal=20scroll=20=E2=80=94=20the=20real=20CC=20?= =?UTF-8?q?model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session now owns the viewport (alt screen; the shell screen restores untouched on exit, with a crash-safe process-exit hook) and scrolls its own transcript: alternate-scroll mode (1007) turns the mouse wheel into arrow keys, which move a window over the hard-wrapped transcript lines. Scrolling up freezes the view with a yellow '↑ scrolled N lines — Esc for live' notice on the status row while the input, status, and footer stay pinned; Esc, scroll-down, PageUp/PageDown navigate. Wrapping is a dependency-free ANSI-aware hard wrap (reset at the break, reopen active codes) — wrap-ansi couldn't be added (VPN blocks the jsr tarball revalidation bun add needs). The boot screen shares the alt screen and restores it if creation fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/render.ts | 35 ++++ .../cli/src/cli/commands/imported/session.tsx | 193 +++++++++++------- .../cli/tests/core/imported-stream.spec.ts | 16 ++ 3 files changed, 171 insertions(+), 73 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index a4af2f978..d49d60d25 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -33,6 +33,41 @@ export function terminalLink(label: string, url: string): string { return `\u001B]8;;${url}\u0007${chalk.dim.underline(label)}\u001B]8;;\u0007`; } +/** Hard-wrap ANSI-styled text at `width` visible columns, keeping style + * continuity across breaks (reset at the break, reopen the active SGR codes). + * Narrow but dependency-free — all input here is our own chalk output. */ +export function hardWrapAnsi(text: string, width: number): string[] { + const ESC = /^(?:\u001b\[[0-9;]*m|\u001b\]8;;[^\u0007]*\u0007)/; + const out: string[] = []; + for (const logical of text.split("\n")) { + let line = ""; + let visible = 0; + let active: string[] = []; + let i = 0; + while (i < logical.length) { + const esc = ESC.exec(logical.slice(i)); + if (esc) { + const seq = esc[0]; + line += seq; + if (seq === "\u001b[0m") active = []; + else if (seq.endsWith("m")) active.push(seq); + i += seq.length; + continue; + } + if (visible >= width) { + out.push(`${line}\u001b[0m`); + line = active.join(""); + visible = 0; + } + line += logical[i]; + visible++; + i++; + } + out.push(line); + } + return out; +} + export function formatDuration(ms: number): string { const seconds = Math.round(ms / 1000); if (seconds < 90) return `${seconds}s`; diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 74bb98757..a72ac685c 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -1,10 +1,14 @@ import chalk from "chalk"; import { Box, render, Static, Text, useApp, useInput } from "ink"; import TextInput from "ink-text-input"; -import { useEffect, useReducer, useState } from "react"; +import { useEffect, useReducer, useRef, useState } from "react"; import stripAnsi from "strip-ansi"; import { createPasteFriendlyStdin } from "@/cli/commands/imported/paste.js"; -import { formatDuration, idleMusing } from "@/cli/commands/imported/render.js"; +import { + formatDuration, + hardWrapAnsi, + idleMusing, +} from "@/cli/commands/imported/render.js"; import type { SessionEngine, SessionStatus, @@ -17,6 +21,27 @@ import packageJson from "../../../../package.json"; const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; +// Alternate screen (Claude Code model): the session owns the viewport with +// its own internal scroll; the shell screen is restored untouched on exit. +// Mode 1007 makes the mouse wheel send arrow keys, which drive the scroll. +let altScreenActive = false; +let altExitHooked = false; +function enterAltScreen(): void { + process.stdout.write("\x1b[?1049h\x1b[?1007h\x1b[2J\x1b[H"); + altScreenActive = true; + if (!altExitHooked) { + altExitHooked = true; + process.on("exit", () => { + if (altScreenActive) process.stdout.write("\x1b[?1007l\x1b[?1049l"); + }); + } +} +function exitAltScreen(): void { + if (!altScreenActive) return; + altScreenActive = false; + process.stdout.write("\x1b[?1007l\x1b[?1049l"); +} + interface SessionOptions { branchId?: string; /** Live footer lines (repo/editor/preview) — pushing appends to the block. */ @@ -97,16 +122,16 @@ function lineCount(item: string, columns: number): number { function SessionView({ engine, footer, subscribe }: ViewProps) { const { exit } = useApp(); - const [history, setHistory] = useState<string[]>([]); + const [items, setItems] = useState<string[]>([]); const [input, setInput] = useState(""); - const [exiting, setExiting] = useState(false); + const [scroll, setScroll] = useState(0); // lines up from the live bottom const [, tick] = useReducer((x: number) => x + 1, 0); const [musingSeed] = useState(() => Math.floor(Math.random() * 97)); + const maxScrollRef = useRef(0); useEffect( - // The trailing newline gives every stream item a blank line after it — - // and the wrap-aware line counter sees it, keeping the spacer honest. - () => subscribe((line) => setHistory((h) => [...h, `${line}\n`])), + // The trailing newline gives every stream item a blank line after it. + () => subscribe((line) => setItems((h) => [...h, `${line}\n`])), [subscribe], ); useEffect(() => { @@ -117,75 +142,86 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { useInput((char, key) => { if (key.ctrl && char === "c") { if (input) setInput(""); - else { - setExiting(true); - exit(); - } - } else if (key.ctrl && char === "d") { - setExiting(true); + else exit(); + return; + } + if (key.ctrl && char === "d") { exit(); + return; + } + // Wheel scrolling: alternate-scroll mode turns it into arrow keys. + if (key.upArrow) { + setScroll((s) => Math.min(s + 3, maxScrollRef.current)); + return; } + if (key.downArrow) { + setScroll((s) => Math.max(0, s - 3)); + return; + } + if (key.pageUp) { + setScroll((s) => Math.min(s + 20, maxScrollRef.current)); + return; + } + if (key.pageDown) { + setScroll((s) => Math.max(0, s - 20)); + return; + } + if (key.escape) setScroll(0); }); - const width = Math.min(process.stdout.columns || 80, 100); + const columns = process.stdout.columns || 80; + const rows = process.stdout.rows || 24; + const width = Math.min(columns, 100); + const innerWidth = Math.max(10, width - 4); // input border + padding + const inputRows = Math.max(1, Math.ceil((input.length + 2) / innerWidth)); + const widgetHeight = 4 + inputRows + (footer.length ? 1 : 0); // status + border + hint + links + const viewHeight = Math.max(3, rows - widgetHeight - 1); + + // Hard-wrapped physical lines of the whole transcript; the view is a + // window over them, pinned to the bottom unless the user scrolled. + const lines = items.flatMap((item) => hardWrapAnsi(item, columns)); + const maxScroll = Math.max(0, lines.length - viewHeight); + maxScrollRef.current = maxScroll; + const clamped = Math.min(scroll, maxScroll); + const end = lines.length - clamped; + const visible = lines.slice(Math.max(0, end - viewHeight), end); + + const scrollNote = + clamped > 0 + ? chalk.yellow( + ` ↑ scrolled ${clamped} lines — Esc or scroll down for live`, + ) + : ""; + return ( - <> - <Static items={history}> - {(line, index) => <Text key={`${index}`}>{line}</Text>} - </Static> - {exiting ? ( - <Box flexDirection="column"> - {footer.length > 0 && <Text>{footer.join(chalk.dim(" · "))}</Text>} - </Box> - ) : ( - <Box flexDirection="column"> - {(() => { - // A shrinking spacer keeps the widget on the terminal's bottom row - // until the conversation fills the screen; from then on only the - // conversation scrolls and the widget stays put. The input's own - // wrapped height is part of the widget, or typing a long prompt - // would bounce the whole layout. - const columns = process.stdout.columns || 80; - const rows = process.stdout.rows || 24; - const used = history.reduce( - (sum, item) => sum + lineCount(item, columns), - 0, - ); - const innerWidth = Math.max(10, width - 4); // border + padding - const inputRows = Math.max( - 1, - Math.ceil((input.length + 2) / innerWidth), - ); - const widgetHeight = 4 + inputRows + (footer.length ? 1 : 0); // status + border + hint + links - const spacer = Math.max(0, rows - used - widgetHeight - 1); - return spacer > 0 ? <Box height={spacer} /> : null; - })()} - <Text>{statusText(engine.status(), musingSeed)}</Text> - <Box - borderStyle="round" - borderColor="gray" - paddingX={1} - width={width} - > - <Text color="cyan">{"❯ "}</Text> - <TextInput - value={input} - onChange={setInput} - onSubmit={(value) => { - if (value.trim()) engine.submit(value); - setInput(""); - }} - /> - </Box> - {footer.length > 0 && ( - <Text>{` ${footer.join(chalk.dim(" · "))}`}</Text> - )} - <Text dimColor> - {" Enter to send · Ctrl+C to exit (turns keep running)"} - </Text> - </Box> + <Box flexDirection="column"> + <Box flexDirection="column" height={viewHeight}> + {visible.map((line, index) => ( + <Text key={`${index}-${line.length}`}>{line || " "}</Text> + ))} + </Box> + <Text> + {statusText(engine.status(), musingSeed)} + {scrollNote} + </Text> + <Box borderStyle="round" borderColor="gray" paddingX={1} width={width}> + <Text color="cyan">{"❯ "}</Text> + <TextInput + value={input} + onChange={setInput} + onSubmit={(value) => { + if (value.trim()) engine.submit(value); + setInput(""); + }} + /> + </Box> + {footer.length > 0 && ( + <Text>{` ${footer.join(chalk.dim(" · "))}`}</Text> )} - </> + <Text dimColor> + {" Enter to send · scroll or Esc for live · Ctrl+C to exit"} + </Text> + </Box> ); } @@ -251,7 +287,7 @@ export async function withBootScreen<T>( work: () => Promise<T>, ): Promise<T> { const header = await buildHeader(); - process.stdout.write("\x1b[2J\x1b[H"); + enterAltScreen(); const rows = process.stdout.rows || 24; const headerLines = header.split("\n").length; const BootScreen = () => { @@ -272,6 +308,9 @@ export async function withBootScreen<T>( const app = render(<BootScreen />, { exitOnCtrlC: false }); try { return await work(); + } catch (error) { + exitAltScreen(); // The error must land on the normal screen. + throw error; } finally { app.unmount(); } @@ -310,7 +349,7 @@ export async function runInteractiveSession( // stays in scrollback) and start at the TOP — the header renders first, and // the dynamic region's fixed height bottom-justifies the input widget at the // terminal's bottom, with the conversation filling the space between. - process.stdout.write("\x1b[2J\x1b[H"); + enterAltScreen(); onLine(await buildHeader()); // Bracketed paste: the terminal wraps pastes in markers (and drops its @@ -334,6 +373,10 @@ export async function runInteractiveSession( process.stdout.write("\x1b[?2004l"); stdinProxy.cleanup(); engine.stop(); + exitAltScreen(); + if (options.footer.length) { + process.stdout.write(`${options.footer.join(chalk.dim(" · "))}\n`); + } const note = engine.turnRunning() ? " — the running turn continues server-side (watch it in the editor)" : ""; @@ -457,7 +500,7 @@ export async function runGenesisSession( }, }; - process.stdout.write("\x1b[2J\x1b[H"); + enterAltScreen(); onLine(await buildHeader()); process.stdout.write("\x1b[?2004h"); @@ -477,6 +520,10 @@ export async function runGenesisSession( process.stdout.write("\x1b[?2004l"); stdinProxy.cleanup(); genesis.stop(); + exitAltScreen(); + if (options.footer.length) { + process.stdout.write(`${options.footer.join(chalk.dim(" · "))}\n`); + } const note = genesis.turnRunning() ? " — the running turn continues server-side (watch it in the editor)" : ""; diff --git a/packages/cli/tests/core/imported-stream.spec.ts b/packages/cli/tests/core/imported-stream.spec.ts index 93b8f7767..2a82b4972 100644 --- a/packages/cli/tests/core/imported-stream.spec.ts +++ b/packages/cli/tests/core/imported-stream.spec.ts @@ -313,6 +313,22 @@ describe("render", () => { }); }); +describe("hardWrapAnsi", () => { + it("wraps at visible width and keeps style continuity across breaks", async () => { + const { hardWrapAnsi } = await import("@/cli/commands/imported/render.js"); + // Raw codes, not chalk — chalk is color-disabled under a non-TTY test run. + const dim = "\u001b[2m"; + const reset = "\u001b[0m"; + const wrapped = hardWrapAnsi(`${dim}${"x".repeat(10)}${reset}`, 4); + expect(wrapped.map((l) => stripAnsi(l))).toEqual(["xxxx", "xxxx", "xx"]); + // Continuation lines reopen the dim code so the style survives the break. + expect(wrapped[1].startsWith(dim)).toBe(true); + expect(wrapped[0].endsWith(reset)).toBe(true); + // Plain text with explicit newlines splits on them. + expect(hardWrapAnsi("ab\ncd", 10)).toEqual(["ab", "cd"]); + }); +}); + describe("makePasteSanitizer", () => { it("strips paste markers and flattens newlines to one line", async () => { const { makePasteSanitizer } = await import( From 361e1dc09f2b6bf2ad2e7e4b2a92dd3b2a62a56d Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:15:09 +0300 Subject: [PATCH 36/73] =?UTF-8?q?feat(imported):=20prompt=20expansions=20?= =?UTF-8?q?=E2=80=94=20/headless?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A /name token in any prompt (session input, chat, new/create --prompt, the base44 code genesis prompt) swaps for a canned instruction block; the echo shows the typed line plus a dim '⤷ expanded /headless' note. Hardcoded registry with one entry for now: /headless appends the Wix Headless Fast skill instructions — phrased without shell syntax, since the edge WAF rejects command-shaped request bodies. Unknown tokens (like /api in prose) pass through untouched; expansion is idempotent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/chat.ts | 2 + .../cli/src/cli/commands/imported/create.ts | 2 + .../src/cli/commands/imported/expansions.ts | 43 +++++++++++++++++++ .../cli/commands/imported/session-engine.ts | 13 ++++-- .../cli/src/cli/commands/imported/session.tsx | 11 ++++- packages/cli/tests/core/expansions.spec.ts | 28 ++++++++++++ 6 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/cli/commands/imported/expansions.ts create mode 100644 packages/cli/tests/core/expansions.spec.ts diff --git a/packages/cli/src/cli/commands/imported/chat.ts b/packages/cli/src/cli/commands/imported/chat.ts index 473ef31a2..5ac064be0 100644 --- a/packages/cli/src/cli/commands/imported/chat.ts +++ b/packages/cli/src/cli/commands/imported/chat.ts @@ -1,4 +1,5 @@ import chalk from "chalk"; +import { expandPrompt } from "@/cli/commands/imported/expansions.js"; import { createTurnStream, terminalLink, @@ -48,6 +49,7 @@ async function chatAction( { runTask, jsonMode, branchId: explicitBranchId }: CLIContext, message: string, ): Promise<RunCommandResult> { + message = expandPrompt(message).text; // Messages must land on the app's working branch: an unscoped send goes to // the main line, whose sandbox is separate and never pushed. const branchId = diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index dfee5e2a6..cb1d4e277 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -1,6 +1,7 @@ import { mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import chalk from "chalk"; +import { expandPrompt } from "@/cli/commands/imported/expansions.js"; import { createTurnStream, formatDuration, @@ -90,6 +91,7 @@ async function createImportedAction( name: string | undefined, options: CreateImportedOptions, ): Promise<RunCommandResult> { + if (options.prompt) options.prompt = expandPrompt(options.prompt).text; // The positional name is the whole identity: directory, GitHub repo, app. let repoName = options.repoName ?? name; // A bare name means "from scratch" — --blank stays for explicitness. diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts new file mode 100644 index 000000000..3759b2da5 --- /dev/null +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -0,0 +1,43 @@ +/** Prompt expansions: a `/name` token in a prompt swaps for a canned block of + * instructions. Hardcoded registry for now — a config file can replace it + * later without touching the call sites. */ + +interface PromptExpansion { + description: string; + text: string; +} + +export const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { + headless: { + description: "Build with the Wix Headless Fast skill", + // Deliberately no shell syntax (`curl -fsSL …`): the platform edge WAF + // rejects command-shaped request bodies; the agent fetches URLs itself. + text: "Fetch and follow this skill: https://www.wix.com/skills/headless-fast/entry/skill.md\nFollow it exactly.", + }, +}; + +export interface ExpandedPrompt { + text: string; + /** Names of the expansions that were applied, in order. */ + applied: string[]; +} + +const TOKEN_RE = /(^|\s)\/([a-z][\w-]*)\b/g; + +/** Replace known `/name` tokens with their expansion blocks (appended after + * the prompt). Unknown tokens pass through untouched. Idempotent: applied + * tokens are removed, and expansion text carries none. */ +export function expandPrompt(prompt: string): ExpandedPrompt { + const applied: string[] = []; + const cleaned = prompt + .replace(TOKEN_RE, (match, lead: string, name: string) => { + if (!PROMPT_EXPANSIONS[name]) return match; + applied.push(name); + return lead ? " " : ""; + }) + .replace(/\s+/g, " ") + .trim(); + if (applied.length === 0) return { text: prompt, applied }; + const blocks = applied.map((name) => PROMPT_EXPANSIONS[name].text); + return { text: [cleaned, ...blocks].join("\n\n"), applied }; +} diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts index d367abf1c..4e08bf718 100644 --- a/packages/cli/src/cli/commands/imported/session-engine.ts +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -1,4 +1,5 @@ import chalk from "chalk"; +import { expandPrompt } from "@/cli/commands/imported/expansions.js"; import { eventLine, formatDuration, @@ -93,9 +94,15 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { let lastEventAt = Date.now(); const submit = (raw: string) => { - const text = raw.trim(); - if (!text) return; - options.onLine(`${chalk.cyan("❯")} ${chalk.bold(text)}`); + const typed = raw.trim(); + if (!typed) return; + const { text, applied } = expandPrompt(typed); + options.onLine(`${chalk.cyan("❯")} ${chalk.bold(typed)}`); + if (applied.length) { + options.onLine( + chalk.dim(` ⤷ expanded ${applied.map((n) => `/${n}`).join(", ")}`), + ); + } pendingSubmitAt = Date.now(); sendsInFlight++; sendImportedChatMessage(text, options.branchId) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index a72ac685c..f6583e811 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -3,6 +3,7 @@ import { Box, render, Static, Text, useApp, useInput } from "ink"; import TextInput from "ink-text-input"; import { useEffect, useReducer, useRef, useState } from "react"; import stripAnsi from "strip-ansi"; +import { expandPrompt } from "@/cli/commands/imported/expansions.js"; import { createPasteFriendlyStdin } from "@/cli/commands/imported/paste.js"; import { formatDuration, @@ -463,9 +464,17 @@ export async function runGenesisSession( } creating = true; creatingSince = Date.now(); + const expanded = expandPrompt(text); onLine(`${chalk.cyan("❯")} ${chalk.bold(text)}`); + if (expanded.applied.length) { + onLine( + chalk.dim( + ` ⤷ expanded ${expanded.applied.map((n) => `/${n}`).join(", ")}`, + ), + ); + } options - .createApp(text, onLine) + .createApp(expanded.text, onLine) .then(async (config) => { const engine = createSessionEngine({ branchId: config.branchId, diff --git a/packages/cli/tests/core/expansions.spec.ts b/packages/cli/tests/core/expansions.spec.ts new file mode 100644 index 000000000..abdc48f5f --- /dev/null +++ b/packages/cli/tests/core/expansions.spec.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; +import { expandPrompt } from "@/cli/commands/imported/expansions.js"; + +describe("expandPrompt", () => { + it("expands /headless into the skill block, dropping the token", () => { + const { text, applied } = expandPrompt( + "online store selling tmnt action figures /headless", + ); + expect(applied).toEqual(["headless"]); + expect(text).toContain("online store selling tmnt action figures"); + expect(text).toContain( + "https://www.wix.com/skills/headless-fast/entry/skill.md", + ); + expect(text).not.toContain("/headless"); + // The expansion must stay WAF-safe: no shell syntax in the request body. + expect(text).not.toContain("curl"); + }); + + it("leaves unknown tokens and plain prompts untouched", () => { + expect(expandPrompt("fix the /api route").text).toBe("fix the /api route"); + expect(expandPrompt("no tokens here").applied).toEqual([]); + }); + + it("is idempotent", () => { + const once = expandPrompt("build a store /headless").text; + expect(expandPrompt(once).text).toBe(once); + }); +}); From f64da269442203981513f87dab236fea52d34302 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:16:52 +0300 Subject: [PATCH 37/73] fix: expansion test matched the token inside the skill URL; unexport internals The /headless token check now matches the bare token only ('headless-fast' in the URL contains the substring), and the registry/type stay module- private per knip. Gate exit codes verified un-piped this time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 4 ++-- packages/cli/tests/core/expansions.spec.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 3759b2da5..b87901f44 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -7,7 +7,7 @@ interface PromptExpansion { text: string; } -export const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { +const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { headless: { description: "Build with the Wix Headless Fast skill", // Deliberately no shell syntax (`curl -fsSL …`): the platform edge WAF @@ -16,7 +16,7 @@ export const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { }, }; -export interface ExpandedPrompt { +interface ExpandedPrompt { text: string; /** Names of the expansions that were applied, in order. */ applied: string[]; diff --git a/packages/cli/tests/core/expansions.spec.ts b/packages/cli/tests/core/expansions.spec.ts index abdc48f5f..2e687bcb3 100644 --- a/packages/cli/tests/core/expansions.spec.ts +++ b/packages/cli/tests/core/expansions.spec.ts @@ -11,7 +11,9 @@ describe("expandPrompt", () => { expect(text).toContain( "https://www.wix.com/skills/headless-fast/entry/skill.md", ); - expect(text).not.toContain("/headless"); + // The token must be gone — but the skill URL legitimately contains the + // substring "/headless" (headless-fast), so match the bare token only. + expect(text).not.toMatch(/\/headless(?![\w-])/); // The expansion must stay WAF-safe: no shell syntax in the request body. expect(text).not.toContain("curl"); }); From 445c47f5743e623153541d77eabae47da10444ac Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:20:14 +0300 Subject: [PATCH 38/73] fix(imported): rock-stable widget heights while scrolling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scroll note could wrap the status row (widget math assumes one row) and odd double-width glyphs could overflow a transcript row — both bounced the widget. The status line is now pre-truncated, every rendered row truncates instead of wrapping, the input-row estimate counts the cursor cell, and the dead pre-window line counter is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index f6583e811..6102b1dad 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -1,5 +1,5 @@ import chalk from "chalk"; -import { Box, render, Static, Text, useApp, useInput } from "ink"; +import { Box, render, Text, useApp, useInput } from "ink"; import TextInput from "ink-text-input"; import { useEffect, useReducer, useRef, useState } from "react"; import stripAnsi from "strip-ansi"; @@ -110,17 +110,6 @@ interface ViewProps { subscribe: (listener: (line: string) => void) => () => void; } -/** Terminal lines a history item occupies, wrap-aware (estimate). */ -function lineCount(item: string, columns: number): number { - return item - .split("\n") - .reduce( - (sum, line) => - sum + Math.max(1, Math.ceil(stripAnsi(line).length / columns)), - 0, - ); -} - function SessionView({ engine, footer, subscribe }: ViewProps) { const { exit } = useApp(); const [items, setItems] = useState<string[]>([]); @@ -174,7 +163,7 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { const rows = process.stdout.rows || 24; const width = Math.min(columns, 100); const innerWidth = Math.max(10, width - 4); // input border + padding - const inputRows = Math.max(1, Math.ceil((input.length + 2) / innerWidth)); + const inputRows = Math.max(1, Math.ceil((input.length + 3) / innerWidth)); // +cursor cell const widgetHeight = 4 + inputRows + (footer.length ? 1 : 0); // status + border + hint + links const viewHeight = Math.max(3, rows - widgetHeight - 1); @@ -188,23 +177,25 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { const visible = lines.slice(Math.max(0, end - viewHeight), end); const scrollNote = - clamped > 0 - ? chalk.yellow( - ` ↑ scrolled ${clamped} lines — Esc or scroll down for live`, - ) - : ""; + clamped > 0 ? chalk.yellow(` ↑ ${clamped} lines — Esc for live`) : ""; + // The status row must stay EXACTLY one row or the whole widget bounces — + // truncate it (and every transcript row) instead of letting them wrap. + const statusLine = hardWrapAnsi( + `${statusText(engine.status(), musingSeed)}${scrollNote}`, + Math.max(10, columns - 1), + )[0]; return ( <Box flexDirection="column"> <Box flexDirection="column" height={viewHeight}> {visible.map((line, index) => ( - <Text key={`${index}-${line.length}`}>{line || " "}</Text> + // biome-ignore lint/suspicious/noArrayIndexKey: windowed slice re-renders wholesale each frame; position is the identity + <Text key={`${index}-${line.length}`} wrap="truncate-end"> + {line || " "} + </Text> ))} </Box> - <Text> - {statusText(engine.status(), musingSeed)} - {scrollNote} - </Text> + <Text wrap="truncate-end">{statusLine}</Text> <Box borderStyle="round" borderColor="gray" paddingX={1} width={width}> <Text color="cyan">{"❯ "}</Text> <TextInput @@ -217,9 +208,9 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { /> </Box> {footer.length > 0 && ( - <Text>{` ${footer.join(chalk.dim(" · "))}`}</Text> + <Text wrap="truncate-end">{` ${footer.join(chalk.dim(" · "))}`}</Text> )} - <Text dimColor> + <Text dimColor wrap="truncate-end"> {" Enter to send · scroll or Esc for live · Ctrl+C to exit"} </Text> </Box> From b6c35078bba109ce0877d36c22c4265350b82a64 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:41:09 +0300 Subject: [PATCH 39/73] fix(imported): don't cry 'send failed' when the edge drops a long turn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chat request stays open for the whole turn, so a turn longer than the edge's request timeout (Cloudflare ~100s) returns a 5xx even though the message reached the backend and the turn is running — verified in logs (7 tool iterations completed after a POST 502'd). The session poller is the real source of truth for turns, so a 502/503/504 (or timeout) is suppressed when a new turn has since appeared; genuine failures (undelivered, or non- gateway errors) still surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/commands/imported/session-engine.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts index 4e08bf718..a9c2dec81 100644 --- a/packages/cli/src/cli/commands/imported/session-engine.ts +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -5,6 +5,7 @@ import { formatDuration, toolAlias, } from "@/cli/commands/imported/render.js"; +import { ApiError } from "@/core/errors.js"; import { getFullConversation, sendImportedChatMessage, @@ -104,6 +105,7 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { ); } pendingSubmitAt = Date.now(); + const submitTurnId = activeTurnId; sendsInFlight++; sendImportedChatMessage(text, options.branchId) .then((turn) => { @@ -112,6 +114,22 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { } }) .catch((error: unknown) => { + // The chat request stays open for the whole turn, so a long turn trips + // the edge's request timeout (Cloudflare ~100s) with a 5xx even though + // the message reached the backend and the turn is running. If the + // poller has since picked up a new turn (activeTurnId advanced, or the + // submit marker was consumed), the send was delivered — not a failure. + const delivered = + activeTurnId !== submitTurnId || + turnStartedAt != null || + pendingSubmitAt == null; + const status = error instanceof ApiError ? error.statusCode : undefined; + const edgeDrop = + status === 502 || + status === 503 || + status === 504 || + /timeout|gateway/i.test(error instanceof Error ? error.message : ""); + if (edgeDrop && delivered) return; // Running — the stream shows it. pendingSubmitAt = null; const message = error instanceof Error ? error.message : String(error); options.onLine(chalk.red(`✗ send failed: ${message}`)); From 6858c69a982bcefb79c07157936bc3b94d0fc918 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:48:45 +0300 Subject: [PATCH 40/73] feat(imported): sunset-slat logo + shimmer animation on the boot screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mark is now a solid circle with three thin horizontal slats cut from the lower half (the real Base44 sunset), rasterized with half-blocks. On the full-page create frame — which already re-renders — a brighter band sweeps down the circle (~5fps) for an animated logo while the app is being made. The scrollback header (a committed history item) keeps the static version, since Ink's <Static> can't animate once printed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 82 +++++++++++++------ 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 6102b1dad..2a3be141a 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -226,18 +226,40 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { * TTY only — callers gate on interactivity. */ const BRAND_ORANGE = "#E86B3C"; +const BRAND_ORANGE_BRIGHT = "#FFAA6E"; -/** The Base44 Code welcome box — the session's first history item, so it - * scrolls away naturally like Claude Code's header does. */ -async function buildHeader(): Promise<string> { +// The Base44 mark: a solid circle with three thin horizontal slats cut from +// the lower half (sunset). Rasterized with half-blocks for double the vertical +// resolution. +const LOGO_ROWS = [ + " ▄▄▄▄▄▄▄▄", + " ▄▄██████████▄▄", + " ▄██████████████▄", + " ████████████████", + "▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀", + " ████████████████", + " ▄▄▄▄▄▄▄▄▄▄▄▄▄▄", + " ▀▀▀▀▀▀▀▀▀▀▀▀▀▀", + " ▀▀▀▀▀▀▀▀", +]; + +/** Logo rows in brand orange. `highlight` (a row index, or null) brightens one + * row — sweep it down across frames for the shimmer animation. */ +function logoRows(highlight: number | null = null): string[] { + const normal = chalk.hex(BRAND_ORANGE); + const bright = chalk.hex(BRAND_ORANGE_BRIGHT); + return LOGO_ROWS.map((row, i) => + i === highlight ? bright(row) : normal(row), + ); +} + +/** Render the welcome box synchronously. `logoHighlight` brightens one logo + * row (sweep it for the boot-screen shimmer); null = static (scrollback). */ +function renderHeader( + who: string, + logoHighlight: number | null = null, +): string { const orange = chalk.hex(BRAND_ORANGE); - let who = ""; - try { - const auth = await readAuth(); - who = auth.name || auth.email || ""; - } catch { - // Not logged in yet — the welcome stays generic. - } const cwd = process.cwd().replace(process.env.HOME ?? "", "~"); const inner = Math.min((process.stdout.columns || 80) - 2, 64); const stripLength = (s: string) => stripAnsi(s).length; @@ -248,27 +270,33 @@ async function buildHeader(): Promise<string> { }; const title = ` ${orange.bold("Base44 Code")} ${chalk.dim(`v${packageJson.version}`)} `; const top = `╭─${title}${"─".repeat(Math.max(0, inner - stripLength(title) - 1))}╮`; - const rowsOut = [ + return [ top, center(""), center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), center(""), - // The Base44 mark: a rasterized circle (half-blocks double the vertical - // resolution) with the slice above the bottom cap missing. - center(orange(" ▄▄██████▄▄ ")), - center(orange(" ▄████████████▄ ")), - center(orange("▄██████████████▄")), - center(orange("████████████████")), - center(orange("▀██████████████▀")), - center(""), - center(orange(" ▀▀██████▀▀ ")), + ...logoRows(logoHighlight).map(center), center(""), center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), center(chalk.dim(cwd)), center(""), `╰${"─".repeat(inner)}╯`, - ]; - return rowsOut.join("\n"); + ].join("\n"); +} + +async function currentUserName(): Promise<string> { + try { + const auth = await readAuth(); + return auth.name || auth.email || ""; + } catch { + return ""; // Not logged in yet — the welcome stays generic. + } +} + +/** The Base44 Code welcome box — the session's first history item, so it + * scrolls away naturally like Claude Code's header does. */ +async function buildHeader(): Promise<string> { + return renderHeader(await currentUserName()); } /** Run `work` (e.g. the create call) inside the full-page frame: header at @@ -278,10 +306,12 @@ export async function withBootScreen<T>( label: string, work: () => Promise<T>, ): Promise<T> { - const header = await buildHeader(); + const who = await currentUserName(); enterAltScreen(); const rows = process.stdout.rows || 24; - const headerLines = header.split("\n").length; + const headerLines = renderHeader(who).split("\n").length; + // Which logo row the shimmer highlight sits on, sweeping down the circle. + const logoRowCount = LOGO_ROWS.length; const BootScreen = () => { const [, tick] = useReducer((x: number) => x + 1, 0); useEffect(() => { @@ -289,9 +319,11 @@ export async function withBootScreen<T>( return () => clearInterval(timer); }, []); const frame = FRAMES[Math.floor(Date.now() / 120) % FRAMES.length]; + // ~5 fps sweep so the shimmer is legible, not frantic. + const highlight = Math.floor(Date.now() / 200) % logoRowCount; return ( <Box flexDirection="column"> - <Text>{header}</Text> + <Text>{renderHeader(who, highlight)}</Text> <Box height={Math.max(0, rows - headerLines - 2)} /> <Text>{chalk.dim(`${frame} ${label}`)}</Text> </Box> From f0f28824d4779644976104dbd2c1a89c48348d35 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Tue, 15 Sep 2026 23:55:03 +0300 Subject: [PATCH 41/73] =?UTF-8?q?fix(imported):=20correct=20Base44=20mark?= =?UTF-8?q?=20=E2=80=94=20dome=20+=20shortening=20bars;=20drop=20the=20ani?= =?UTF-8?q?mation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the wrong full-circle-with-slats art (and the shimmer) with the actual logo: a rounded dome sun above three horizontal bars that shorten toward the bottom. Static everywhere, including the boot screen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 2a3be141a..6f85110ac 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -226,39 +226,32 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { * TTY only — callers gate on interactivity. */ const BRAND_ORANGE = "#E86B3C"; -const BRAND_ORANGE_BRIGHT = "#FFAA6E"; -// The Base44 mark: a solid circle with three thin horizontal slats cut from -// the lower half (sunset). Rasterized with half-blocks for double the vertical -// resolution. +// The Base44 mark: a rounded dome (the sun) above three horizontal bars that +// shorten toward the bottom. Half-blocks give the dome its curve; the bars are +// full blocks, blank rows between them are the gaps. const LOGO_ROWS = [ " ▄▄▄▄▄▄▄▄", - " ▄▄██████████▄▄", + " ▄████████████▄", " ▄██████████████▄", + "▄████████████████▄", + "██████████████████", + "", " ████████████████", - "▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀", - " ████████████████", - " ▄▄▄▄▄▄▄▄▄▄▄▄▄▄", - " ▀▀▀▀▀▀▀▀▀▀▀▀▀▀", - " ▀▀▀▀▀▀▀▀", + "", + " ████████████", + "", + " ██████", ]; -/** Logo rows in brand orange. `highlight` (a row index, or null) brightens one - * row — sweep it down across frames for the shimmer animation. */ -function logoRows(highlight: number | null = null): string[] { - const normal = chalk.hex(BRAND_ORANGE); - const bright = chalk.hex(BRAND_ORANGE_BRIGHT); - return LOGO_ROWS.map((row, i) => - i === highlight ? bright(row) : normal(row), - ); +/** Logo rows in brand orange. */ +function logoRows(): string[] { + const orange = chalk.hex(BRAND_ORANGE); + return LOGO_ROWS.map((row) => (row ? orange(row) : "")); } -/** Render the welcome box synchronously. `logoHighlight` brightens one logo - * row (sweep it for the boot-screen shimmer); null = static (scrollback). */ -function renderHeader( - who: string, - logoHighlight: number | null = null, -): string { +/** Render the welcome box synchronously. */ +function renderHeader(who: string): string { const orange = chalk.hex(BRAND_ORANGE); const cwd = process.cwd().replace(process.env.HOME ?? "", "~"); const inner = Math.min((process.stdout.columns || 80) - 2, 64); @@ -275,7 +268,7 @@ function renderHeader( center(""), center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), center(""), - ...logoRows(logoHighlight).map(center), + ...logoRows().map(center), center(""), center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), center(chalk.dim(cwd)), @@ -309,9 +302,8 @@ export async function withBootScreen<T>( const who = await currentUserName(); enterAltScreen(); const rows = process.stdout.rows || 24; - const headerLines = renderHeader(who).split("\n").length; - // Which logo row the shimmer highlight sits on, sweeping down the circle. - const logoRowCount = LOGO_ROWS.length; + const header = renderHeader(who); + const headerLines = header.split("\n").length; const BootScreen = () => { const [, tick] = useReducer((x: number) => x + 1, 0); useEffect(() => { @@ -319,11 +311,9 @@ export async function withBootScreen<T>( return () => clearInterval(timer); }, []); const frame = FRAMES[Math.floor(Date.now() / 120) % FRAMES.length]; - // ~5 fps sweep so the shimmer is legible, not frantic. - const highlight = Math.floor(Date.now() / 200) % logoRowCount; return ( <Box flexDirection="column"> - <Text>{renderHeader(who, highlight)}</Text> + <Text>{header}</Text> <Box height={Math.max(0, rows - headerLines - 2)} /> <Text>{chalk.dim(`${frame} ${label}`)}</Text> </Box> From 83c1ceeff6a08bbc011865e184a81c8d9bda4eb7 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 00:18:58 +0300 Subject: [PATCH 42/73] feat(imported): /headless warns the agent about the scaffold's nested git repo The Wix fast-path.mjs scaffolds the site into a subfolder named after the business and git-inits it, which makes Base44's turn-end commit record the folder as an empty gitlink instead of the files. The expansion now tells the agent to collapse that subfolder's .git into the one Base44 repo (or move the project to the root) and verify with git status before finishing. Phrasing avoids the fetch-command shape the edge WAF rejects (verified the expanded body passes); the CLI backstop still de-vendors nested repos server-side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index b87901f44..57d3d46ee 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -12,7 +12,14 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { description: "Build with the Wix Headless Fast skill", // Deliberately no shell syntax (`curl -fsSL …`): the platform edge WAF // rejects command-shaped request bodies; the agent fetches URLs itself. - text: "Fetch and follow this skill: https://www.wix.com/skills/headless-fast/entry/skill.md\nFollow it exactly.", + // The nested-git note prevents the scaffold from committing as a gitlink. + text: [ + "Fetch and follow this skill: https://www.wix.com/skills/headless-fast/entry/skill.md", + "Follow it exactly.", + "", + "One repository — this is important. The skill's `fast-path.mjs` scaffolds the site into a subfolder named after the business, and that scaffolder initializes its own git repository (a `.git`) inside that subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", + "So once the scaffold exists, collapse it into this one repository before you finish: delete the scaffolded subfolder's `.git` directory (e.g. `rm -rf <folder>/.git`) so its files are tracked here — or move the project up to the repository root. Then confirm with `git status` that the subfolder's individual files are staged, not the folder appearing as a single submodule entry. Keep the dev server and everything else working after the move.", + ].join("\n"), }, }; From 91da8f4d6eb7235d67ca3fa58101e7cef77d0bb9 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 00:20:04 +0300 Subject: [PATCH 43/73] fix(imported): correct /headless git-init attribution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fast-path.mjs has no git code — it scaffolds via 'npm create @wix/new', and that generator is what inits the nested repo. The expansion now names the generator, not the fast-path script. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 57d3d46ee..f80632828 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -17,7 +17,7 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "Fetch and follow this skill: https://www.wix.com/skills/headless-fast/entry/skill.md", "Follow it exactly.", "", - "One repository — this is important. The skill's `fast-path.mjs` scaffolds the site into a subfolder named after the business, and that scaffolder initializes its own git repository (a `.git`) inside that subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", + "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "So once the scaffold exists, collapse it into this one repository before you finish: delete the scaffolded subfolder's `.git` directory (e.g. `rm -rf <folder>/.git`) so its files are tracked here — or move the project up to the repository root. Then confirm with `git status` that the subfolder's individual files are staged, not the folder appearing as a single submodule entry. Keep the dev server and everything else working after the move.", ].join("\n"), }, From fa0a2a1972365db04c1792786dfcd478e854bd2b Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:11:47 +0300 Subject: [PATCH 44/73] feat(cli): pick the builder model (command + in-session /model), timer, round logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - base44 model [name]: list/set the account-wide builder model, or `default` to clear. Sends X-Builder-Model-Selection: user-v1 on every request so the saved pick is honored (backend falls back to app default when unset). - In-session /model: `/model` opens an arrow-navigable picker (↑↓/Enter/Esc); `/model <name>` switches directly. Picker owns the keyboard while open. - Footer shows the current model and a live whole-session timer. - Logo is now computed, not hand-drawn: a half-block disc (each text row is two ~square pixels, so a mathematical circle reads round instead of as a tall oval) over shortening bars, centred symmetrically. - Shared catalog/api in core/model.ts, used by both the command and the session. --- .../cli/src/cli/commands/imported/session.tsx | 215 +++++++++++++++--- packages/cli/src/cli/commands/model.ts | 66 ++++++ packages/cli/src/cli/program.ts | 2 + .../cli/src/core/clients/base44-client.ts | 6 + packages/cli/src/core/model.ts | 81 +++++++ 5 files changed, 337 insertions(+), 33 deletions(-) create mode 100644 packages/cli/src/cli/commands/model.ts create mode 100644 packages/cli/src/core/model.ts diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 6f85110ac..7e3294ece 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -18,9 +18,17 @@ import type { import { createSessionEngine } from "@/cli/commands/imported/session-engine.js"; import { readAuth } from "@/core/auth/config.js"; import { getBase44ApiUrl } from "@/core/config.js"; +import { + displayName, + getMe, + MODELS, + resolvePick, + saveBuilderModel, +} from "@/core/model.js"; import packageJson from "../../../../package.json"; const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; +const BRAND_ORANGE = "#E86B3C"; // Alternate screen (Claude Code model): the session owns the viewport with // its own internal scroll; the shell screen is restored untouched on exit. @@ -108,16 +116,29 @@ interface ViewProps { engine: SessionEngine; footer: string[]; subscribe: (listener: (line: string) => void) => () => void; + sessionStartedAt: number; } -function SessionView({ engine, footer, subscribe }: ViewProps) { +function SessionView({ + engine, + footer, + subscribe, + sessionStartedAt, +}: ViewProps) { const { exit } = useApp(); const [items, setItems] = useState<string[]>([]); const [input, setInput] = useState(""); const [scroll, setScroll] = useState(0); // lines up from the live bottom const [, tick] = useReducer((x: number) => x + 1, 0); const [musingSeed] = useState(() => Math.floor(Math.random() * 97)); + const [currentModel, setCurrentModel] = useState<string | null>(null); + const [pickerIndex, setPickerIndex] = useState<number | null>(null); // null = closed const maxScrollRef = useRef(0); + const meIdRef = useRef<string | null>(null); + + // Append a line straight into the transcript (for /command output that isn't + // an engine event). + const emit = (line: string) => setItems((h) => [...h, `${line}\n`]); useEffect( // The trailing newline gives every stream item a blank line after it. @@ -128,8 +149,82 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { const timer = setInterval(tick, 120); return () => clearInterval(timer); }, []); + // Load the account's current builder-model pick for the footer (non-blocking). + useEffect(() => { + getMe() + .then((me) => { + meIdRef.current = me.id; + setCurrentModel(me.builder_model ?? null); + }) + .catch(() => {}); + }, []); + + // Persist a pick and reflect it in the footer. + const applyModel = async (pick: (typeof MODELS)[number]) => { + const orange = chalk.hex(BRAND_ORANGE); + try { + if ((pick.id ?? null) === currentModel) { + emit(chalk.dim(` already on ${pick.name}`)); + return; + } + let id = meIdRef.current; + if (!id) { + id = (await getMe()).id; + meIdRef.current = id; + } + await saveBuilderModel(id, pick.id); + setCurrentModel(pick.id); + emit( + pick.id === null + ? chalk.dim(" model reset — Base44 chooses per app") + : ` ${orange("●")} model set to ${chalk.bold(pick.name)}`, + ); + } catch (error) { + emit( + chalk.red( + ` /model: ${error instanceof Error ? error.message : String(error)}`, + ), + ); + } + }; + + // `/model` alone opens the arrow-navigable picker; `/model <name>` switches + // straight away. + const runModelSlash = (arg: string) => { + if (!arg) { + const cur = MODELS.findIndex((m) => (m.id ?? null) === currentModel); + setPickerIndex(cur >= 0 ? cur : 0); + return; + } + try { + void applyModel(resolvePick(arg)); + } catch (error) { + emit( + chalk.red( + ` /model: ${error instanceof Error ? error.message : String(error)}`, + ), + ); + } + }; useInput((char, key) => { + // Model picker owns the keyboard while open: arrows move the selection, + // Enter commits, Esc/Ctrl-C cancels. Swallow everything else so it doesn't + // scroll the transcript or type into the (hidden) input. + if (pickerIndex !== null) { + if (key.upArrow) + setPickerIndex((i) => ((i ?? 0) - 1 + MODELS.length) % MODELS.length); + else if (key.downArrow) + setPickerIndex((i) => ((i ?? 0) + 1) % MODELS.length); + else if (key.return) { + const pick = MODELS[pickerIndex]; + setPickerIndex(null); + void applyModel(pick); + } else if (key.escape || (key.ctrl && char === "c")) { + setPickerIndex(null); + } + return; + } if (key.ctrl && char === "c") { if (input) setInput(""); else exit(); @@ -164,7 +259,12 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { const width = Math.min(columns, 100); const innerWidth = Math.max(10, width - 4); // input border + padding const inputRows = Math.max(1, Math.ceil((input.length + 3) / innerWidth)); // +cursor cell - const widgetHeight = 4 + inputRows + (footer.length ? 1 : 0); // status + border + hint + links + const pickerOpen = pickerIndex !== null; + // The bottom block is either the input box (inputRows + 2 border) or the model + // picker (title + one row per model + 2 border). +3 = status + model/timer + + // hint; +1 more for the footer links when present. + const inputBlockHeight = pickerOpen ? MODELS.length + 3 : inputRows + 2; + const widgetHeight = inputBlockHeight + 3 + (footer.length ? 1 : 0); const viewHeight = Math.max(3, rows - widgetHeight - 1); // Hard-wrapped physical lines of the whole transcript; the view is a @@ -196,22 +296,61 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { ))} </Box> <Text wrap="truncate-end">{statusLine}</Text> - <Box borderStyle="round" borderColor="gray" paddingX={1} width={width}> - <Text color="cyan">{"❯ "}</Text> - <TextInput - value={input} - onChange={setInput} - onSubmit={(value) => { - if (value.trim()) engine.submit(value); - setInput(""); - }} - /> - </Box> + {pickerOpen ? ( + <Box + flexDirection="column" + borderStyle="round" + borderColor="cyan" + paddingX={1} + width={width} + > + <Text> + {chalk.bold("Pick a model")} + {chalk.dim(" ↑↓ move · Enter select · Esc cancel")} + </Text> + {MODELS.map((m, i) => { + const selected = i === pickerIndex; + const isCurrent = (m.id ?? null) === currentModel; + const label = `${selected ? "▸" : " "} ${isCurrent ? "●" : "○"} ${m.name}${m.note ? ` (${m.note})` : ""}`; + return ( + <Text + key={m.name} + color={selected ? "cyan" : undefined} + wrap="truncate-end" + > + {selected ? label : chalk.dim(label)} + </Text> + ); + })} + </Box> + ) : ( + <Box borderStyle="round" borderColor="gray" paddingX={1} width={width}> + <Text color="cyan">{"❯ "}</Text> + <TextInput + value={input} + onChange={setInput} + onSubmit={(value) => { + const trimmed = value.trim(); + if (trimmed === "/model" || trimmed.startsWith("/model ")) { + runModelSlash(trimmed.slice("/model".length).trim()); + } else if (trimmed) { + engine.submit(value); + } + setInput(""); + }} + /> + </Box> + )} {footer.length > 0 && ( <Text wrap="truncate-end">{` ${footer.join(chalk.dim(" · "))}`}</Text> )} + <Text wrap="truncate-end"> + {` ${chalk.dim("model")} ${chalk.hex(BRAND_ORANGE)(displayName(currentModel))}${chalk.dim(" · session ")}${formatDuration(Date.now() - sessionStartedAt)}`} + </Text> <Text dimColor wrap="truncate-end"> - {" Enter to send · scroll or Esc for live · Ctrl+C to exit"} + {pickerOpen + ? " ↑↓ to move · Enter to select · Esc to cancel" + : " Enter to send · /model to switch model · scroll or Esc for live · Ctrl+C to exit"} </Text> </Box> ); @@ -225,29 +364,37 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { * clears the input, then exits; turns keep running server-side after exit. * TTY only — callers gate on interactivity. */ -const BRAND_ORANGE = "#E86B3C"; - -// The Base44 mark: a rounded dome (the sun) above three horizontal bars that -// shorten toward the bottom. Half-blocks give the dome its curve; the bars are -// full blocks, blank rows between them are the gaps. -const LOGO_ROWS = [ - " ▄▄▄▄▄▄▄▄", - " ▄████████████▄", - " ▄██████████████▄", - "▄████████████████▄", - "██████████████████", - "", - " ████████████████", - "", - " ████████████", - "", - " ██████", -]; +// The Base44 mark, rendered rather than hand-drawn: a round sun over shortening +// bars. Terminal cells are ~2:1, so a naive block grid reads as a tall oval; +// half-blocks make each text row two ~square pixels, so a mathematical disc +// comes out round. An even grid centred between pixels keeps the top and bottom +// caps symmetric. Every row is trimmed so renderHeader's center() aligns them. +const LOGO_RADIUS = 7; +function buildLogoRows(): string[] { + const n = LOGO_RADIUS * 2; + const c = (n - 1) / 2; + const rad = LOGO_RADIUS - 0.5; + const inside = (px: number, py: number) => + (px - c) ** 2 + (py - c) ** 2 <= rad * rad + 0.5; + const rows: string[] = []; + for (let ty = 0; ty < n; ty += 2) { + let row = ""; + for (let px = 0; px < n; px++) { + const top = inside(px, ty); + const bot = inside(px, ty + 1); + row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " "; + } + rows.push(row.trim()); + } + const bar = (w: number) => "█".repeat(w); + rows.push("", bar(n), bar(Math.round(n * 0.6)), bar(Math.round(n * 0.28))); + return rows.map((row) => row.trim()); +} /** Logo rows in brand orange. */ function logoRows(): string[] { const orange = chalk.hex(BRAND_ORANGE); - return LOGO_ROWS.map((row) => (row ? orange(row) : "")); + return buildLogoRows().map((row) => (row ? orange(row) : "")); } /** Render the welcome box synchronously. */ @@ -375,6 +522,7 @@ export async function runInteractiveSession( engine={engine} footer={options.footer} subscribe={subscribe} + sessionStartedAt={sessionStartedAt} />, { exitOnCtrlC: false, stdin: stdinProxy }, ); @@ -532,6 +680,7 @@ export async function runGenesisSession( engine={genesis} footer={options.footer} subscribe={subscribe} + sessionStartedAt={sessionStartedAt} />, { exitOnCtrlC: false, stdin: stdinProxy }, ); diff --git a/packages/cli/src/cli/commands/model.ts b/packages/cli/src/cli/commands/model.ts new file mode 100644 index 000000000..7a0c13248 --- /dev/null +++ b/packages/cli/src/cli/commands/model.ts @@ -0,0 +1,66 @@ +import type { CLIContext, RunCommandResult } from "@/cli/types.js"; +import { Base44Command, theme } from "@/cli/utils/index.js"; +import { + displayName, + getMe, + MODELS, + resolvePick, + saveBuilderModel, +} from "@/core/model.js"; + +async function modelAction( + { log, jsonMode }: CLIContext, + input: string | undefined, +): Promise<RunCommandResult> { + const me = await getMe(); + const current = me.builder_model ?? null; + + if (!input) { + if (jsonMode) { + return { + stdout: `${JSON.stringify({ + current, + models: MODELS.map((m) => ({ name: m.name, id: m.id })), + })}\n`, + }; + } + for (const m of MODELS) { + const active = (m.id ?? null) === current; + const marker = active ? theme.styles.bold("●") : theme.styles.dim("○"); + const note = m.note ? theme.styles.dim(` (${m.note})`) : ""; + const name = active ? theme.styles.bold(m.name) : m.name; + log.message(`${marker} ${name}${note}`); + } + return { + outroMessage: `Current: ${theme.styles.bold(displayName(current))}. Set with \`base44 model <name>\`.`, + }; + } + + const pick = resolvePick(input); + if ((pick.id ?? null) === current) { + return { outroMessage: `Already on ${theme.styles.bold(pick.name)}.` }; + } + await saveBuilderModel(me.id, pick.id); + if (jsonMode) { + return { stdout: `${JSON.stringify({ current: pick.id })}\n` }; + } + return { + outroMessage: + pick.id === null + ? "Model reset — Base44 chooses per app again." + : `Builder model set to ${theme.styles.bold(pick.name)} for every new turn.`, + }; +} + +export function getModelCommand(): Base44Command { + const command = new Base44Command("model", { + requireAppContext: false, + }); + command + .description( + "Pick the builder model used for your turns (account-wide). No argument lists models and shows the current pick; `default` clears it", + ) + .argument("[model]", 'Model name or id, e.g. "Opus 5" or default') + .action(modelAction); + return command; +} diff --git a/packages/cli/src/cli/program.ts b/packages/cli/src/cli/program.ts index e9f505605..63c1c48d9 100644 --- a/packages/cli/src/cli/program.ts +++ b/packages/cli/src/cli/program.ts @@ -14,6 +14,7 @@ import { getEntitiesPushCommand } from "@/cli/commands/entities/push.js"; import { getFunctionsCommand } from "@/cli/commands/functions/index.js"; import { getNewCommand } from "@/cli/commands/imported/create.js"; import { getImportedCommand } from "@/cli/commands/imported/index.js"; +import { getModelCommand } from "@/cli/commands/model.js"; import { getBuildCommand } from "@/cli/commands/project/build.js"; import { getCreateCommand } from "@/cli/commands/project/create.js"; import { getDeployCommand } from "@/cli/commands/project/deploy.js"; @@ -120,6 +121,7 @@ export function createProgram(context: CLIContext): Command { program.addCommand(getImportedCommand()); program.addCommand(getNewCommand()); program.addCommand(getCodeCommand()); + program.addCommand(getModelCommand()); // Register the target command (staging/preview host selection) program.addCommand(getTargetCommand()); diff --git a/packages/cli/src/core/clients/base44-client.ts b/packages/cli/src/core/clients/base44-client.ts index c413bcbcf..d08361a9e 100644 --- a/packages/cli/src/core/clients/base44-client.ts +++ b/packages/cli/src/core/clients/base44-client.ts @@ -101,6 +101,12 @@ export const base44Client = ky.create({ beforeRequest: [ (request) => { request.headers.set("X-Request-ID", randomUUID()); + // Honor the caller's account-wide builder model pick (`base44 model`). + // Without this header the backend ignores the saved choice and + // auto-selects; with it and no saved pick it still falls back to the + // app default, so it is safe to send unconditionally — same contract the + // web editor's axios client uses. + request.headers.set("X-Builder-Model-Selection", "user-v1"); // Staging/preview only: lets a dev flip PostHog flags per request // (BASE44_FF_OVERRIDE env, or the persisted `base44 target --ff`); // prod ignores the header. diff --git a/packages/cli/src/core/model.ts b/packages/cli/src/core/model.ts new file mode 100644 index 000000000..125d8e7fd --- /dev/null +++ b/packages/cli/src/core/model.ts @@ -0,0 +1,81 @@ +import { HTTPError } from "ky"; +import { base44Client } from "./clients/index.js"; +import { ApiError, InvalidInputError } from "./errors.js"; + +/** + * Main customer-facing builder models, mirroring the web model picker's customer + * set (modelPickerRegistry). Name shown to the user -> backend picker id; the + * backend validates the id and the workspace entitlement per turn, so a pick it + * rejects surfaces as a clear error rather than a silent fallback. `default` + * clears the pick (builder_model = null) and lets Base44 choose. + */ +export const MODELS: { name: string; id: string | null; note?: string }[] = [ + { name: "default", id: null, note: "let Base44 choose" }, + { name: "Opus 5", id: "claude_opus_5" }, + { name: "Sonnet 5", id: "claude-sonnet-5" }, + { name: "Fable 5", id: "claude_fable_5", note: "uses more credits" }, + { name: "GPT-5.6 Sol", id: "gpt_5_6_sol" }, + { name: "Gemini 3.8 Flash", id: "gemini_3_8_flash", note: "fast" }, + { name: "Base 1", id: "base1" }, +]; + +/** Fold a name or id to a comparable key: lowercase, drop every non-alphanumeric + * so "Opus 5", "opus-5", "opus_5" and "claude_opus_5" all match sensibly. */ +const fold = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]/g, ""); + +export function resolvePick(input: string): (typeof MODELS)[number] { + const key = fold(input); + const byExact = MODELS.find( + (m) => fold(m.name) === key || (m.id && fold(m.id) === key), + ); + if (byExact) return byExact; + // Loose contains: "opus" -> Opus 5, "gemini" -> Gemini 3.8 Flash. + const byContains = MODELS.filter( + (m) => fold(m.name).includes(key) || (m.id && fold(m.id).includes(key)), + ); + if (byContains.length === 1) return byContains[0]; + const names = MODELS.map((m) => m.name).join(", "); + throw new InvalidInputError( + byContains.length > 1 + ? `"${input}" is ambiguous — matches ${byContains.map((m) => m.name).join(", ")}.` + : `Unknown model "${input}". Choose one of: ${names}.`, + ); +} + +export interface MeResponse { + id: string; + builder_model?: string | null; +} + +export async function getMe(): Promise<MeResponse> { + try { + return await base44Client.get("api/auth/me").json<MeResponse>(); + } catch (error) { + throw await ApiError.fromHttpError(error, "reading your account"); + } +} + +export async function saveBuilderModel( + userId: string, + modelId: string | null, +): Promise<void> { + try { + await base44Client.post(`api/auth/${userId}/update-user`, { + json: { builder_model: modelId }, + }); + } catch (error) { + // The write is gated on the PER_USER_BUILDER_MODEL_SELECTION flag, which the + // backend checks ignoring header overrides — a 400 here means the account + // lacks it (or the model isn't runnable in this workspace). + if (error instanceof HTTPError && error.response.status === 400) { + throw new InvalidInputError( + "This account can't pick a builder model yet — enable the PER_USER_BUILDER_MODEL_SELECTION flag for it in PostHog, or the model isn't available in this workspace.", + ); + } + throw await ApiError.fromHttpError(error, "saving your model choice"); + } +} + +/** Display name for a stored builder_model id (may be one the CLI doesn't list). */ +export const displayName = (id: string | null | undefined): string => + MODELS.find((m) => m.id === id)?.name ?? id ?? "default"; From 7237536f1e3e6d5a0fe529177dd1775f52bae5cb Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:15:09 +0300 Subject: [PATCH 45/73] fix(cli): logo is the real Base44 setting-sun (disc with slit bars), not circle+triangle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matched to Logo_v5.png: a solid disc whose lower half is cut by thin horizontal slits, so the bands shorten with the circle's curve — one mark, not a circle plus a separate triangle of bars. Below the split each row draws on its top pixel only (▀), leaving the gap beneath. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 7e3294ece..385d025d8 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -364,12 +364,15 @@ function SessionView({ * clears the input, then exits; turns keep running server-side after exit. * TTY only — callers gate on interactivity. */ -// The Base44 mark, rendered rather than hand-drawn: a round sun over shortening -// bars. Terminal cells are ~2:1, so a naive block grid reads as a tall oval; -// half-blocks make each text row two ~square pixels, so a mathematical disc -// comes out round. An even grid centred between pixels keeps the top and bottom -// caps symmetric. Every row is trimmed so renderHeader's center() aligns them. -const LOGO_RADIUS = 7; +// The Base44 mark, rendered rather than hand-drawn: a setting sun — a solid disc +// on top, then thin horizontal slits cut through the lower half so it reads as +// bars that shorten with the circle's curve (the real logo, Logo_v5.png). +// Terminal cells are ~2:1, so a naive block grid is a tall oval; half-blocks +// make each text row two ~square pixels, so the disc comes out round. Below the +// split, each row is drawn on its TOP pixel only (▀), leaving a gap beneath it — +// that is the slit. Rows are trimmed so renderHeader's center() aligns them. +const LOGO_RADIUS = 8; +const LOGO_SPLIT = 10; // pixel-row where the solid sun gives way to slit bars function buildLogoRows(): string[] { const n = LOGO_RADIUS * 2; const c = (n - 1) / 2; @@ -380,14 +383,16 @@ function buildLogoRows(): string[] { for (let ty = 0; ty < n; ty += 2) { let row = ""; for (let px = 0; px < n; px++) { - const top = inside(px, ty); - const bot = inside(px, ty + 1); - row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " "; + if (ty < LOGO_SPLIT) { + const top = inside(px, ty); + const bot = inside(px, ty + 1); + row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " "; + } else { + row += inside(px, ty) ? "▀" : " "; + } } rows.push(row.trim()); } - const bar = (w: number) => "█".repeat(w); - rows.push("", bar(n), bar(Math.round(n * 0.6)), bar(Math.round(n * 0.28))); return rows.map((row) => row.trim()); } From f2e21bdd76f76636aa0f0fa51db8f5844aa832d6 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:16:14 +0300 Subject: [PATCH 46/73] fix(cli): rounder logo sun (radius 10) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 385d025d8..8684015f3 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -371,8 +371,8 @@ function SessionView({ // make each text row two ~square pixels, so the disc comes out round. Below the // split, each row is drawn on its TOP pixel only (▀), leaving a gap beneath it — // that is the slit. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_RADIUS = 8; -const LOGO_SPLIT = 10; // pixel-row where the solid sun gives way to slit bars +const LOGO_RADIUS = 10; +const LOGO_SPLIT = 14; // pixel-row where the solid sun gives way to slit bars function buildLogoRows(): string[] { const n = LOGO_RADIUS * 2; const c = (n - 1) / 2; From 85aeaba6b9877dd012e48bd269cbe0eaa59e41ac Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:18:08 +0300 Subject: [PATCH 47/73] fix(cli): smaller logo, 3 stripes starting from the middle Split at the circle's centre so the top half is a solid sun and the bottom half is exactly three shortening slit-bars; radius 6 makes it smaller. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 8684015f3..de942c757 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -371,8 +371,8 @@ function SessionView({ // make each text row two ~square pixels, so the disc comes out round. Below the // split, each row is drawn on its TOP pixel only (▀), leaving a gap beneath it — // that is the slit. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_RADIUS = 10; -const LOGO_SPLIT = 14; // pixel-row where the solid sun gives way to slit bars +const LOGO_RADIUS = 6; +const LOGO_SPLIT = 6; // split at the centre: solid top half, 3 slit-bars below function buildLogoRows(): string[] { const n = LOGO_RADIUS * 2; const c = (n - 1) / 2; From d201ca0a1764fa2d8afc733d3768569bd29f8788 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:22:08 +0300 Subject: [PATCH 48/73] fix(cli): logo is a round sun with a single blank stripe Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index de942c757..d3ae0f116 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -364,32 +364,26 @@ function SessionView({ * clears the input, then exits; turns keep running server-side after exit. * TTY only — callers gate on interactivity. */ -// The Base44 mark, rendered rather than hand-drawn: a setting sun — a solid disc -// on top, then thin horizontal slits cut through the lower half so it reads as -// bars that shorten with the circle's curve (the real logo, Logo_v5.png). -// Terminal cells are ~2:1, so a naive block grid is a tall oval; half-blocks -// make each text row two ~square pixels, so the disc comes out round. Below the -// split, each row is drawn on its TOP pixel only (▀), leaving a gap beneath it — -// that is the slit. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_RADIUS = 6; -const LOGO_SPLIT = 6; // split at the centre: solid top half, 3 slit-bars below +// The Base44 mark, rendered rather than hand-drawn: a round sun with a single +// thin blank stripe across it. Terminal cells are ~2:1, so a naive block grid is +// a tall oval; half-blocks make each text row two ~square pixels, so the disc +// comes out round. One blanked pixel-row (LOGO_GAP) is the stripe. Rows are +// trimmed so renderHeader's center() aligns them. +const LOGO_RADIUS = 9; +const LOGO_GAP = 9; // the single blank stripe: this pixel-row is cleared function buildLogoRows(): string[] { const n = LOGO_RADIUS * 2; const c = (n - 1) / 2; const rad = LOGO_RADIUS - 0.5; const inside = (px: number, py: number) => - (px - c) ** 2 + (py - c) ** 2 <= rad * rad + 0.5; + py !== LOGO_GAP && (px - c) ** 2 + (py - c) ** 2 <= rad * rad + 0.5; const rows: string[] = []; for (let ty = 0; ty < n; ty += 2) { let row = ""; for (let px = 0; px < n; px++) { - if (ty < LOGO_SPLIT) { - const top = inside(px, ty); - const bot = inside(px, ty + 1); - row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " "; - } else { - row += inside(px, ty) ? "▀" : " "; - } + const top = inside(px, ty); + const bot = inside(px, ty + 1); + row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " "; } rows.push(row.trim()); } From 4000674929dd45d8b8f52794083e68e1c3555616 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:26:23 +0300 Subject: [PATCH 49/73] fix(cli): compact left-aligned header (logo left, text right), smaller sun, low stripe Claude-Code-style header: the sun mark on the left with the title/account/cwd lines stacked to its right, no box. Smaller sun (radius 5) with the single blank stripe low in the circle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index d3ae0f116..b9ca518d7 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -2,7 +2,6 @@ import chalk from "chalk"; import { Box, render, Text, useApp, useInput } from "ink"; import TextInput from "ink-text-input"; import { useEffect, useReducer, useRef, useState } from "react"; -import stripAnsi from "strip-ansi"; import { expandPrompt } from "@/cli/commands/imported/expansions.js"; import { createPasteFriendlyStdin } from "@/cli/commands/imported/paste.js"; import { @@ -369,8 +368,8 @@ function SessionView({ // a tall oval; half-blocks make each text row two ~square pixels, so the disc // comes out round. One blanked pixel-row (LOGO_GAP) is the stripe. Rows are // trimmed so renderHeader's center() aligns them. -const LOGO_RADIUS = 9; -const LOGO_GAP = 9; // the single blank stripe: this pixel-row is cleared +const LOGO_RADIUS = 5; +const LOGO_GAP = 7; // the single blank stripe, low in the circle: this pixel-row is cleared function buildLogoRows(): string[] { const n = LOGO_RADIUS * 2; const c = (n - 1) / 2; @@ -390,37 +389,33 @@ function buildLogoRows(): string[] { return rows.map((row) => row.trim()); } -/** Logo rows in brand orange. */ -function logoRows(): string[] { - const orange = chalk.hex(BRAND_ORANGE); - return buildLogoRows().map((row) => (row ? orange(row) : "")); -} - -/** Render the welcome box synchronously. */ +/** The welcome header, Claude-Code style: the sun mark on the left, the title / + * account / cwd lines stacked to its right. No box. */ function renderHeader(who: string): string { const orange = chalk.hex(BRAND_ORANGE); const cwd = process.cwd().replace(process.env.HOME ?? "", "~"); - const inner = Math.min((process.stdout.columns || 80) - 2, 64); - const stripLength = (s: string) => stripAnsi(s).length; + const logo = buildLogoRows(); + const logoW = Math.max(...logo.map((r) => r.length)); const center = (s: string) => { - const pad = Math.max(0, inner - stripLength(s)); - const left = Math.floor(pad / 2); - return `│${" ".repeat(left)}${s}${" ".repeat(pad - left)}│`; + const total = Math.max(0, logoW - s.length); + const left = Math.floor(total / 2); + return " ".repeat(left) + s + " ".repeat(total - left); }; - const title = ` ${orange.bold("Base44 Code")} ${chalk.dim(`v${packageJson.version}`)} `; - const top = `╭─${title}${"─".repeat(Math.max(0, inner - stripLength(title) - 1))}╮`; - return [ - top, - center(""), - center(chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!")), - center(""), - ...logoRows().map(center), - center(""), - center(chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, ""))), - center(chalk.dim(cwd)), - center(""), - `╰${"─".repeat(inner)}╯`, - ].join("\n"); + const text = [ + `${orange.bold("Base44 Code")} ${chalk.dim(`v${packageJson.version}`)}`, + chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!"), + chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, "")), + chalk.dim(cwd), + ]; + const height = Math.max(logo.length, text.length); + const textTop = Math.max(0, Math.floor((logo.length - text.length) / 2)); + const out: string[] = []; + for (let i = 0; i < height; i++) { + const left = i < logo.length ? orange(center(logo[i])) : " ".repeat(logoW); + const right = text[i - textTop] ?? ""; + out.push(` ${left} ${right}`.trimEnd()); + } + return out.join("\n"); } async function currentUserName(): Promise<string> { From 634027fd0f4c4c315241f20ae9a001cc0dac1458 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:30:34 +0300 Subject: [PATCH 50/73] feat(cli): Esc stops the running turn (like the editor's stop button) Adds a server-side stop: chat/stop (branch-scoped) via engine.stopTurn(). Esc stops when a turn is running; when nothing runs it still snaps to live. The poller settles the stopped turn through the normal transcript path. Genesis delegates to the inner engine (app creation isn't stoppable). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/commands/imported/session-engine.ts | 23 +++++++++++++++++++ .../cli/src/cli/commands/imported/session.tsx | 15 ++++++++++-- .../cli/src/core/resources/imported/api.ts | 12 ++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts index a9c2dec81..da00f43ec 100644 --- a/packages/cli/src/cli/commands/imported/session-engine.ts +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -9,6 +9,7 @@ import { ApiError } from "@/core/errors.js"; import { getFullConversation, sendImportedChatMessage, + stopImportedChat, } from "@/core/resources/imported/api.js"; import { diffConversation, @@ -65,6 +66,8 @@ export interface SessionEngine { start(primeFirstPoll: boolean): Promise<void>; stop(): void; submit(text: string): void; + /** Stop the running turn server-side (like the editor's stop button). */ + stopTurn(): void; status(): SessionStatus; turnRunning(): boolean; } @@ -236,6 +239,26 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { stopped = true; if (timer) clearInterval(timer); }, + stopTurn() { + // Nothing running (or already sending nothing) — no-op so Esc stays free + // for scroll-to-live when idle. + if ( + turnStartedAt == null && + sendsInFlight === 0 && + pendingSubmitAt == null + ) + return; + options.onLine(chalk.dim("· stopping…")); + // Fire-and-forget: the backend persists the stopped status, and the poller + // settles the turn from the transcript — same path as a natural finish. + stopImportedChat(options.branchId).catch((error: unknown) => { + options.onLine( + chalk.red( + ` stop failed: ${error instanceof Error ? error.message : String(error)}`, + ), + ); + }); + }, submit, status(): SessionStatus { let phase: SessionPhase = "idle"; diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index b9ca518d7..df778051d 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -250,7 +250,12 @@ function SessionView({ setScroll((s) => Math.max(0, s - 20)); return; } - if (key.escape) setScroll(0); + // Esc stops the running turn (like the editor's stop button); when nothing + // is running it snaps the transcript back to live. + if (key.escape) { + if (engine.turnRunning()) engine.stopTurn(); + else setScroll(0); + } }); const columns = process.stdout.columns || 80; @@ -349,7 +354,9 @@ function SessionView({ <Text dimColor wrap="truncate-end"> {pickerOpen ? " ↑↓ to move · Enter to select · Esc to cancel" - : " Enter to send · /model to switch model · scroll or Esc for live · Ctrl+C to exit"} + : engine.turnRunning() + ? " Esc to stop · type to queue · scroll to read · Ctrl+C to exit" + : " Enter to send · /model to switch model · Esc for live · Ctrl+C to exit"} </Text> </Box> ); @@ -608,6 +615,10 @@ export async function runGenesisSession( stop() { inner?.stop(); }, + stopTurn() { + // Only a real engine can stop a server turn; app creation isn't stoppable. + inner?.stopTurn(); + }, submit(text: string) { if (inner) { inner.submit(text); diff --git a/packages/cli/src/core/resources/imported/api.ts b/packages/cli/src/core/resources/imported/api.ts index 1d3470c59..1f21ddeee 100644 --- a/packages/cli/src/core/resources/imported/api.ts +++ b/packages/cli/src/core/resources/imported/api.ts @@ -188,6 +188,18 @@ export async function sendImportedChatMessage( return parseOrThrow(ChatTurnSchema, await response.json(), "chat turn"); } +/** User-authoritative stop: unlocks the builder and marks the running turn + * stopped. Branch-scoped — it stops the turn on the line you're looking at. */ +export async function stopImportedChat(branchId?: string): Promise<void> { + try { + await getAppClient().post("chat/stop", { + searchParams: branchScope(branchId), + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "stopping the turn"); + } +} + export async function getFullConversation( limit: number, branchId?: string, From 14781136b56ad353392da4fce51521e6f0cd1051 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:35:00 +0300 Subject: [PATCH 51/73] fix(cli): remove the session timer from the footer Footer now shows only the current model; drops the SessionView sessionStartedAt prop (the session-end line keeps its own local timer). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/session.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index df778051d..639f0b600 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -115,15 +115,9 @@ interface ViewProps { engine: SessionEngine; footer: string[]; subscribe: (listener: (line: string) => void) => () => void; - sessionStartedAt: number; } -function SessionView({ - engine, - footer, - subscribe, - sessionStartedAt, -}: ViewProps) { +function SessionView({ engine, footer, subscribe }: ViewProps) { const { exit } = useApp(); const [items, setItems] = useState<string[]>([]); const [input, setInput] = useState(""); @@ -349,7 +343,7 @@ function SessionView({ <Text wrap="truncate-end">{` ${footer.join(chalk.dim(" · "))}`}</Text> )} <Text wrap="truncate-end"> - {` ${chalk.dim("model")} ${chalk.hex(BRAND_ORANGE)(displayName(currentModel))}${chalk.dim(" · session ")}${formatDuration(Date.now() - sessionStartedAt)}`} + {` ${chalk.dim("model")} ${chalk.hex(BRAND_ORANGE)(displayName(currentModel))}`} </Text> <Text dimColor wrap="truncate-end"> {pickerOpen @@ -523,7 +517,6 @@ export async function runInteractiveSession( engine={engine} footer={options.footer} subscribe={subscribe} - sessionStartedAt={sessionStartedAt} />, { exitOnCtrlC: false, stdin: stdinProxy }, ); @@ -685,7 +678,6 @@ export async function runGenesisSession( engine={genesis} footer={options.footer} subscribe={subscribe} - sessionStartedAt={sessionStartedAt} />, { exitOnCtrlC: false, stdin: stdinProxy }, ); From 7ba9246a84edf39fe02474680ade1c4f8d048740 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 08:41:25 +0300 Subject: [PATCH 52/73] fix(cli): wrapped URLs in the stream stay fully clickable (OSC 8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bare http(s) URLs in agent text/results are wrapped as OSC 8 hyperlinks (linkifyUrls) with a per-link id, and hardWrapAnsi now closes+reopens the active OSC 8 link on every continuation row. So a URL split by the hard wrap re-emits its full target on each row (same id) — ctrl/cmd-click opens the whole URL instead of just the fragment up to the newline. Control bytes are built from char codes to keep the source free of raw ESC/BEL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/render.ts | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/render.ts b/packages/cli/src/cli/commands/imported/render.ts index d49d60d25..c45192908 100644 --- a/packages/cli/src/cli/commands/imported/render.ts +++ b/packages/cli/src/cli/commands/imported/render.ts @@ -27,36 +27,68 @@ export function toolAlias(name: string): string { return TOOL_ALIASES[name] ?? name; } +// Terminal control bytes, built from char codes so this source carries no raw +// ESC/BEL and no ambiguous escape literals. +const ESC_CHAR = String.fromCharCode(27); +const BEL = String.fromCharCode(7); +const OSC8_CLOSE = `${ESC_CHAR}]8;;${BEL}`; + /** OSC 8 terminal hyperlink: a short clickable label instead of a wrapping - * URL — the whole link opens regardless of line width. */ + * URL - the whole link opens regardless of line width. */ export function terminalLink(label: string, url: string): string { - return `\u001B]8;;${url}\u0007${chalk.dim.underline(label)}\u001B]8;;\u0007`; + return `${ESC_CHAR}]8;;${url}${BEL}${chalk.dim.underline(label)}${OSC8_CLOSE}`; +} + +/** Wrap bare http(s) URLs in a plain-text string as OSC 8 hyperlinks, so a URL + * a hard wrap would split still opens in full on ctrl/cmd-click. Each link gets + * an `id=` so terminals join its segments across wrapped rows (paired with + * hardWrapAnsi, which reopens the active link on every continuation row). The + * visible text stays the URL. Input must be plain text (no existing OSC 8), so + * only call it on raw backend text, never on already-linked output. */ +export function linkifyUrls(text: string): string { + let n = 0; + return text.replace(/https?:\/\/[^\s]+/g, (raw) => { + // Trailing sentence punctuation is not part of the URL. + const trailing = raw.match(/[.,;:!?)\]}'"]+$/)?.[0] ?? ""; + const url = trailing ? raw.slice(0, -trailing.length) : raw; + const id = `b44-${n++}`; + return `${ESC_CHAR}]8;id=${id};${url}${BEL}${url}${OSC8_CLOSE}${trailing}`; + }); } /** Hard-wrap ANSI-styled text at `width` visible columns, keeping style - * continuity across breaks (reset at the break, reopen the active SGR codes). - * Narrow but dependency-free — all input here is our own chalk output. */ + * continuity across breaks (reset at the break, reopen the active SGR codes and + * any active OSC 8 hyperlink so a wrapped link stays whole). Narrow but + * dependency-free - all input here is our own chalk / linkifyUrls output. */ export function hardWrapAnsi(text: string, width: number): string[] { - const ESC = /^(?:\u001b\[[0-9;]*m|\u001b\]8;;[^\u0007]*\u0007)/; + const ESC = new RegExp( + `^(?:${ESC_CHAR}\\[[0-9;]*m|${ESC_CHAR}\\]8;[^${BEL}]*${BEL})`, + ); + const RESET = `${ESC_CHAR}[0m`; const out: string[] = []; for (const logical of text.split("\n")) { let line = ""; let visible = 0; let active: string[] = []; + let link = ""; // the active OSC 8 open sequence, or "" when none is open let i = 0; while (i < logical.length) { const esc = ESC.exec(logical.slice(i)); if (esc) { const seq = esc[0]; line += seq; - if (seq === "\u001b[0m") active = []; + if (seq === RESET) active = []; else if (seq.endsWith("m")) active.push(seq); + else if (seq === OSC8_CLOSE) link = ""; + else link = seq; // an OSC 8 open (carries id + url) i += seq.length; continue; } if (visible >= width) { - out.push(`${line}\u001b[0m`); - line = active.join(""); + // Close the link before the break, then reopen it (same id) on the next + // row so the terminal treats both halves as one hyperlink. + out.push(`${line}${link ? OSC8_CLOSE : ""}${RESET}`); + line = active.join("") + link; visible = 0; } line += logical[i]; @@ -88,7 +120,7 @@ export function eventLine( case "thinking": return chalk.dim(`✻ ${event.text}`); case "text": - return event.text; + return linkifyUrls(event.text); case "tool_start": return null; case "waiting": { @@ -118,8 +150,8 @@ export function eventLine( return head; } const result = event.ok - ? chalk.dim(event.result) - : chalk.red(event.result); + ? chalk.dim(linkifyUrls(event.result)) + : chalk.red(linkifyUrls(event.result)); return `${head}${event.result ? `\n ${result}` : ""}`; } } From 252f81b14c1b471f9f113d03ed45e2f97d1f5f3b Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 09:13:40 +0300 Subject: [PATCH 53/73] feat(cli): /headless expansion front-loads Wix preview setup Adds the known facts the agent otherwise rediscovers: dev command is `wix dev` (wraps Astro) on port 3000; bind 0.0.0.0 + allow all hosts via astro.config.mjs vite.server; first boot is slow (auth + content sync + type-gen) so use high timeouts; and verify with curl, not the in-editor preview tools (unavailable for imported apps here). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index f80632828..e3c551959 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -19,6 +19,12 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "", "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "So once the scaffold exists, collapse it into this one repository before you finish: delete the scaffolded subfolder's `.git` directory (e.g. `rm -rf <folder>/.git`) so its files are tracked here — or move the project up to the repository root. Then confirm with `git status` that the subfolder's individual files are staged, not the folder appearing as a single submodule entry. Keep the dev server and everything else working after the move.", + "", + "Getting it to preview — take these as known facts, don't rediscover them:", + "- The dev command is the Wix CLI's `wix dev` (it wraps Astro's dev server, and is the `dev` script in package.json). Run that as a docker-compose.base44.yml service published on port 3000 — never a bare `astro dev` and never a production build.", + "- The preview shows whatever serves on port 3000, but `wix dev`/Astro bind localhost and reject foreign hosts by default. In astro.config.mjs set `vite: { server: { host: true, allowedHosts: true } }` (bind 0.0.0.0 and allow ALL hosts — never a single exact host), so the sandbox's rotating external hostname is accepted.", + "- `wix dev` authenticates against the linked Wix site (wix.config.json) and syncs content and generates types before it serves — so first boot is slow on top of the dependency install. Give `docker compose ... up --build` and the first request a high timeout and wait, rather than reading the not-yet-listening port as a failure. If the CLI can't authenticate in the container, make its credentials/config available to the service.", + "- Verify by curling the port-3000 entry point (with the preview Host header) until it returns real HTML. The in-editor preview screenshot / verify tools are NOT available for imported apps in this environment — do not call them or wait on them; a healthy curl is the signal that the preview is live.", ].join("\n"), }, }; From f019db0efb521f5bcc35ef892102a9b92a9775da Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 10:07:38 +0300 Subject: [PATCH 54/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20give?= =?UTF-8?q?=20the=20exact=20wix-dev=20host-allowlist=20fix=20so=20the=20ag?= =?UTF-8?q?ent=20stops=20looping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The big time-sink is the host allowlist: wix dev wraps astro dev, which empties allowedHosts unless --allowed-hosts is passed, overriding astro.config. Tell the agent the working command (wix dev --allowed-hosts .$BASE44_SANDBOX_HOST_DOMAIN), the creds mount, node_modules-volume to avoid reinstall churn, and the right Host header to verify with. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index e3c551959..4b9f9909d 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -20,11 +20,12 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "So once the scaffold exists, collapse it into this one repository before you finish: delete the scaffolded subfolder's `.git` directory (e.g. `rm -rf <folder>/.git`) so its files are tracked here — or move the project up to the repository root. Then confirm with `git status` that the subfolder's individual files are staged, not the folder appearing as a single submodule entry. Keep the dev server and everything else working after the move.", "", - "Getting it to preview — take these as known facts, don't rediscover them:", - "- The dev command is the Wix CLI's `wix dev` (it wraps Astro's dev server, and is the `dev` script in package.json). Run that as a docker-compose.base44.yml service published on port 3000 — never a bare `astro dev` and never a production build.", - "- The preview shows whatever serves on port 3000, but `wix dev`/Astro bind localhost and reject foreign hosts by default. In astro.config.mjs set `vite: { server: { host: true, allowedHosts: true } }` (bind 0.0.0.0 and allow ALL hosts — never a single exact host), so the sandbox's rotating external hostname is accepted.", - "- `wix dev` authenticates against the linked Wix site (wix.config.json) and syncs content and generates types before it serves — so first boot is slow on top of the dependency install. Give `docker compose ... up --build` and the first request a high timeout and wait, rather than reading the not-yet-listening port as a failure. If the CLI can't authenticate in the container, make its credentials/config available to the service.", - "- Verify by curling the port-3000 entry point (with the preview Host header) until it returns real HTML. The in-editor preview screenshot / verify tools are NOT available for imported apps in this environment — do not call them or wait on them; a healthy curl is the signal that the preview is live.", + "Getting it to preview — take these as known facts, do NOT reverse-engineer @wix/cli, astro, or vite to rediscover them:", + "- The dev command is the Wix CLI's `wix dev` (the `dev` script in package.json). It wraps `astro dev`, which wraps Vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare `astro dev`, never a production build.", + "- HOST ALLOWLIST — this is the one that eats turns if you don't know it: `wix dev` forwards to `astro dev`, and astro's CLI sets `allowedHosts` to an EMPTY array whenever the `--allowed-hosts` flag is absent, which OVERRIDES whatever you put in astro.config.mjs `server.allowedHosts`. So editing the config alone does nothing. The reliable fix is to pass the flag on the dev command: `npx wix dev --port 3000 --allowed-hosts .$BASE44_SANDBOX_HOST_DOMAIN` (leading dot = wildcard for that domain; the platform sets $BASE44_SANDBOX_HOST_DOMAIN). Also bind all interfaces — set `vite.server.host: true` in astro.config.mjs. Don't chase the `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var; the flag is what works.", + "- `wix dev` authenticates against the linked Wix site (wix.config.json) and syncs content + generates types before it serves — slow on top of the install. Give `up --build` and the first request a high timeout and wait; a not-yet-listening port is not a failure. The CLI credentials live at `/root/.wix` — mount that dir into the service so it can authenticate.", + "- Keep node_modules on a named volume (or don't pass `--build`/recreate repeatedly): every recreate re-runs `npm install` and adds ~30s to each retry loop.", + '- Verify by curling `http://localhost:3000/` with `-H "Host: 3000-<sandbox-id>.$BASE44_SANDBOX_HOST_DOMAIN"` (the host the proxy actually forwards) until it returns real HTML — NOT the public preview-proxy host, which is a different check. A healthy curl is the signal the preview is live.', ].join("\n"), }, }; From ca0c9912ba35aadae9c5538b03b221304c2fe9e7 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 10:24:05 +0300 Subject: [PATCH 55/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20separ?= =?UTF-8?q?ate=20the=20two=20Astro=20host=20settings=20+=20fix=20the=20ver?= =?UTF-8?q?ify=20trap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Binding and allowlist are different: astro binds localhost unless the TOP-LEVEL server.host:true is set (vite.server.host is HMR-only and does not bind); the allowlist needs --allowed-hosts on wix dev. And verify with a plain curl localhost:3000 (200 = serving+bound) — the previous Host: 3000-<sandbox-id> hint looped on 000 because there is no sandbox-id var. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 4b9f9909d..6e9b8e7de 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -22,10 +22,10 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "", "Getting it to preview — take these as known facts, do NOT reverse-engineer @wix/cli, astro, or vite to rediscover them:", "- The dev command is the Wix CLI's `wix dev` (the `dev` script in package.json). It wraps `astro dev`, which wraps Vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare `astro dev`, never a production build.", - "- HOST ALLOWLIST — this is the one that eats turns if you don't know it: `wix dev` forwards to `astro dev`, and astro's CLI sets `allowedHosts` to an EMPTY array whenever the `--allowed-hosts` flag is absent, which OVERRIDES whatever you put in astro.config.mjs `server.allowedHosts`. So editing the config alone does nothing. The reliable fix is to pass the flag on the dev command: `npx wix dev --port 3000 --allowed-hosts .$BASE44_SANDBOX_HOST_DOMAIN` (leading dot = wildcard for that domain; the platform sets $BASE44_SANDBOX_HOST_DOMAIN). Also bind all interfaces — set `vite.server.host: true` in astro.config.mjs. Don't chase the `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var; the flag is what works.", + "- TWO SEPARATE host settings, both required — this is what eats turns. (1) BINDING: astro's dev server binds localhost-only unless you set the TOP-LEVEL `server: { host: true }` in astro.config.mjs — NOT `vite.server.host` (that's only Vite/HMR and does NOT bind the dev server). If the container log says \"Network — use --host to expose\" or the port answers in-container but 000 from the host, this is why. (2) ALLOWLIST: `wix dev` forwards to `astro dev`, which sets `allowedHosts` to an EMPTY array unless `--allowed-hosts` is passed, OVERRIDING astro.config's `server.allowedHosts` (so editing the config alone is dead code). Pass the flag on the dev command: `npx wix dev --port 3000 --allowed-hosts .$BASE44_SANDBOX_HOST_DOMAIN` (leading dot = wildcard; the platform sets $BASE44_SANDBOX_HOST_DOMAIN). Don't chase the `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var — and note any platform env var only reaches the service if it's under compose `environment:`.", "- `wix dev` authenticates against the linked Wix site (wix.config.json) and syncs content + generates types before it serves — slow on top of the install. Give `up --build` and the first request a high timeout and wait; a not-yet-listening port is not a failure. The CLI credentials live at `/root/.wix` — mount that dir into the service so it can authenticate.", "- Keep node_modules on a named volume (or don't pass `--build`/recreate repeatedly): every recreate re-runs `npm install` and adds ~30s to each retry loop.", - '- Verify by curling `http://localhost:3000/` with `-H "Host: 3000-<sandbox-id>.$BASE44_SANDBOX_HOST_DOMAIN"` (the host the proxy actually forwards) until it returns real HTML — NOT the public preview-proxy host, which is a different check. A healthy curl is the signal the preview is live.', + '- Verify with a plain `curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/` from the sandbox — 200 means it is serving AND bound to 0.0.0.0 (the whole point). Do NOT build a Host header out of a sandbox-id variable (there is none; you will loop on 000). If in-container curl is 200 but host curl is 000, it is the binding setting (1) above, not the app. The platform verifies the real external host allowlist at end of turn.', ].join("\n"), }, }; From 7913f0ca2b8e95a8d15e8645c004f1dc650c4b76 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 10:31:22 +0300 Subject: [PATCH 56/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20rewor?= =?UTF-8?q?d=20preview=20guidance=20to=20prose=20so=20the=20WAF=20stops=20?= =?UTF-8?q?403ing=20create?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My earlier additions (npx wix dev …, curl … -w %{http_code} http://…) were command-shaped, which the edge WAF rejects in the create request body — exactly what the file's own comment warns against. Reworded to config-key/prose form (same guidance: top-level server.host to bind, allowed-hosts on the wix dev command, creds mount, node_modules volume, 200-check verify). Verified the full expanded prompt now passes the WAF (200, not a Blocked 403). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 6e9b8e7de..b09b1ab67 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -20,12 +20,12 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "So once the scaffold exists, collapse it into this one repository before you finish: delete the scaffolded subfolder's `.git` directory (e.g. `rm -rf <folder>/.git`) so its files are tracked here — or move the project up to the repository root. Then confirm with `git status` that the subfolder's individual files are staged, not the folder appearing as a single submodule entry. Keep the dev server and everything else working after the move.", "", - "Getting it to preview — take these as known facts, do NOT reverse-engineer @wix/cli, astro, or vite to rediscover them:", - "- The dev command is the Wix CLI's `wix dev` (the `dev` script in package.json). It wraps `astro dev`, which wraps Vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare `astro dev`, never a production build.", - "- TWO SEPARATE host settings, both required — this is what eats turns. (1) BINDING: astro's dev server binds localhost-only unless you set the TOP-LEVEL `server: { host: true }` in astro.config.mjs — NOT `vite.server.host` (that's only Vite/HMR and does NOT bind the dev server). If the container log says \"Network — use --host to expose\" or the port answers in-container but 000 from the host, this is why. (2) ALLOWLIST: `wix dev` forwards to `astro dev`, which sets `allowedHosts` to an EMPTY array unless `--allowed-hosts` is passed, OVERRIDING astro.config's `server.allowedHosts` (so editing the config alone is dead code). Pass the flag on the dev command: `npx wix dev --port 3000 --allowed-hosts .$BASE44_SANDBOX_HOST_DOMAIN` (leading dot = wildcard; the platform sets $BASE44_SANDBOX_HOST_DOMAIN). Don't chase the `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS` env var — and note any platform env var only reaches the service if it's under compose `environment:`.", - "- `wix dev` authenticates against the linked Wix site (wix.config.json) and syncs content + generates types before it serves — slow on top of the install. Give `up --build` and the first request a high timeout and wait; a not-yet-listening port is not a failure. The CLI credentials live at `/root/.wix` — mount that dir into the service so it can authenticate.", - "- Keep node_modules on a named volume (or don't pass `--build`/recreate repeatedly): every recreate re-runs `npm install` and adds ~30s to each retry loop.", - '- Verify with a plain `curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/` from the sandbox — 200 means it is serving AND bound to 0.0.0.0 (the whole point). Do NOT build a Host header out of a sandbox-id variable (there is none; you will loop on 000). If in-container curl is 200 but host curl is 000, it is the binding setting (1) above, not the app. The platform verifies the real external host allowlist at end of turn.', + "Getting it to preview — known facts; do not reverse-engineer the Wix CLI, astro, or vite to rediscover them:", + "The dev command is the Wix CLI dev command (the dev script in package.json); it wraps astro dev, which wraps vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare astro dev, never a production build.", + "TWO separate host settings, both required, or it eats turns. First, BINDING: astro's dev server listens on localhost only unless the TOP-LEVEL server host option in astro.config.mjs is true — the vite server host option is HMR-only and does NOT bind the listener. The tell: the container log says to pass a flag to expose the network, or the port answers inside the container but not from the docker host. Second, the ALLOWLIST: the Wix dev command forwards to astro dev, which resets its allowed-hosts to empty unless its allowed-hosts option is given on the command line — that overrides the server allowedHosts in astro.config, so editing the config alone is dead code. Give the Wix dev command its allowed-hosts option, set to a leading-dot wildcard of the sandbox host domain the platform provides in BASE44_SANDBOX_HOST_DOMAIN. Ignore the vite additional-allowed-hosts env var; and note a platform env var reaches the service only if you list it under the compose environment section.", + "The Wix dev command authenticates against the linked Wix site (wix.config.json) and syncs content and generates types before it serves — slow on top of the install; give the build and first request a high timeout and wait, a not-yet-listening port is not a failure. Its credentials live under the container root's .wix directory — mount that into the service so it can authenticate.", + "Keep node_modules on a named volume, and avoid recreating or rebuilding the service repeatedly: every recreate re-runs the install and adds about half a minute per retry.", + "Verify by requesting the local port-3000 root and checking for a 200 status — 200 means it is serving AND bound. Do NOT build a host header out of a sandbox-id variable; there is none, and you will loop on connection failures. If it answers inside the container but not from the host, it is the binding setting above, not the app. The platform verifies the real external host allowlist at end of turn.", ].join("\n"), }, }; From 787096b294f7bed9db601716a65f8894385c8a37 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 10:48:37 +0300 Subject: [PATCH 57/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20prefe?= =?UTF-8?q?r=20a=20flat=20root=20layout=20to=20stop=20later-turn=20path=20?= =?UTF-8?q?confusion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill scaffolds into a subfolder; leaving the app there while docker-compose.base44.yml sits at the repo root is a split layout that makes later turns cd into the wrong directory. Tell it to move the whole project to the root (one flat layout), and only as a fallback keep compose inside the app subfolder + record the dir in .base44/environment.json. Still prose (WAF-safe). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index b09b1ab67..9feb45115 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -18,7 +18,7 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "Follow it exactly.", "", "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", - "So once the scaffold exists, collapse it into this one repository before you finish: delete the scaffolded subfolder's `.git` directory (e.g. `rm -rf <folder>/.git`) so its files are tracked here — or move the project up to the repository root. Then confirm with `git status` that the subfolder's individual files are staged, not the folder appearing as a single submodule entry. Keep the dev server and everything else working after the move.", + "So once the scaffold exists, MOVE the whole scaffolded project up to the repository root — one flat layout where the app, docker-compose.base44.yml, and config all live at the root. Prefer this over leaving the app in its subfolder: a split layout (app in a subfolder, compose at the root) is a recurring trap — later turns run a command from the wrong directory because the project's real location isn't obvious. Moving to the root also removes the scaffold's own nested .git; delete any nested .git that remains, so git tracks the individual files here rather than recording the folder as a submodule pointer (an empty gitlink) that reaches the repository with none of your code. Confirm with git status that individual files are staged, not a single folder entry, and that the dev server still works from the root. If you truly cannot move it, keep docker-compose.base44.yml INSIDE the same subfolder as the app (never split them) and record the project directory in .base44/environment.json so later turns don't guess.", "", "Getting it to preview — known facts; do not reverse-engineer the Wix CLI, astro, or vite to rediscover them:", "The dev command is the Wix CLI dev command (the dev script in package.json); it wraps astro dev, which wraps vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare astro dev, never a production build.", From 874ed28310c58fd074d201126b41f3472a3c085f Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 11:15:06 +0300 Subject: [PATCH 58/73] fix(cli): unstick the session poller + stop the post-move dep reinstall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - session-engine: the poll re-entrancy guard was a plain boolean, so a wedged in-flight request blocked every future poll — the turn settled server-side but the UI stayed on Shmoozing/running with the timer ticking. Time-bound the guard (45s) so settle is still detected after a hung poll. - /headless: moving the project to root made the agent delete+reinstall node_modules (slow, redundant — the compose service installs its own in a volume). Tell it to move as-is and not reinstall on the host. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 1 + .../cli/src/cli/commands/imported/session-engine.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 9feb45115..40a9437fc 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -19,6 +19,7 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "", "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "So once the scaffold exists, MOVE the whole scaffolded project up to the repository root — one flat layout where the app, docker-compose.base44.yml, and config all live at the root. Prefer this over leaving the app in its subfolder: a split layout (app in a subfolder, compose at the root) is a recurring trap — later turns run a command from the wrong directory because the project's real location isn't obvious. Moving to the root also removes the scaffold's own nested .git; delete any nested .git that remains, so git tracks the individual files here rather than recording the folder as a submodule pointer (an empty gitlink) that reaches the repository with none of your code. Confirm with git status that individual files are staged, not a single folder entry, and that the dev server still works from the root. If you truly cannot move it, keep docker-compose.base44.yml INSIDE the same subfolder as the app (never split them) and record the project directory in .base44/environment.json so later turns don't guess.", + "When you move the project, move it as-is — do NOT delete and reinstall node_modules afterward. The dev server runs in the compose service, which provisions its own node_modules in a named volume; the host copy is only needed for the pre-move seed step, so a host reinstall after moving is pure wasted time.", "", "Getting it to preview — known facts; do not reverse-engineer the Wix CLI, astro, or vite to rediscover them:", "The dev command is the Wix CLI dev command (the dev script in package.json); it wraps astro dev, which wraps vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare astro dev, never a production build.", diff --git a/packages/cli/src/cli/commands/imported/session-engine.ts b/packages/cli/src/cli/commands/imported/session-engine.ts index da00f43ec..b487b11b2 100644 --- a/packages/cli/src/cli/commands/imported/session-engine.ts +++ b/packages/cli/src/cli/commands/imported/session-engine.ts @@ -85,6 +85,7 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { let stopped = false; let polling = false; + let pollStartedAt = 0; let timer: ReturnType<typeof setInterval> | null = null; let sendsInFlight = 0; let activeTurnId: string | null = null; @@ -143,8 +144,15 @@ export function createSessionEngine(options: EngineOptions): SessionEngine { }; const poll = async (prime: boolean) => { - if (polling) return; + // Re-entrancy guard, but time-bounded: if a previous poll's request wedged + // (a hung fetch that never resolves or rejects), a plain boolean would block + // every future poll forever — the turn settles server-side but the UI stays + // stuck on "running" with the timer ticking. After STUCK_POLL_MS, let a new + // poll through so settle is still detected. + const STUCK_POLL_MS = 45_000; + if (polling && Date.now() - pollStartedAt < STUCK_POLL_MS) return; polling = true; + pollStartedAt = Date.now(); try { let messages: Awaited<ReturnType<typeof getFullConversation>>; try { From ad6d7e83b3f5d202bb9e289d7f7717077b2bac13 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 11:29:17 +0300 Subject: [PATCH 59/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20ancho?= =?UTF-8?q?r=20all=20work=20on=20/app,=20never=20the=20OS=20filesystem=20r?= =?UTF-8?q?oot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agents were operating from / (installed skills to /.agents, scaffolded to /mutant-metropolis, then 'moved to repo root' = moved into /, which trashed the container root and forced a recovery + reinstall). Spell out that the repo root is /app: cd there first, scaffold under /app, and 'move to the repo root' means into /app, never /. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 40a9437fc..2ea762502 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -18,7 +18,8 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "Follow it exactly.", "", "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", - "So once the scaffold exists, MOVE the whole scaffolded project up to the repository root — one flat layout where the app, docker-compose.base44.yml, and config all live at the root. Prefer this over leaving the app in its subfolder: a split layout (app in a subfolder, compose at the root) is a recurring trap — later turns run a command from the wrong directory because the project's real location isn't obvious. Moving to the root also removes the scaffold's own nested .git; delete any nested .git that remains, so git tracks the individual files here rather than recording the folder as a submodule pointer (an empty gitlink) that reaches the repository with none of your code. Confirm with git status that individual files are staged, not a single folder entry, and that the dev server still works from the root. If you truly cannot move it, keep docker-compose.base44.yml INSIDE the same subfolder as the app (never split them) and record the project directory in .base44/environment.json so later turns don't guess.", + "FIRST, anchor on the repository root: it is /app — the git repository this Base44 app lives in (the directory that holds its .git). Do ALL work there: cd to /app before scaffolding, run the scaffold from /app so it lands in a subfolder OF /app, and treat 'the repo root' as /app everywhere below. NEVER cd to / or operate from the OS filesystem root — scaffolding or moving files into / is a destructive mistake (it litters the container's root filesystem, not your project).", + "Then, once the scaffold exists under /app, MOVE the whole scaffolded project up into /app itself — one flat layout where the app, docker-compose.base44.yml, and config all live directly in /app. 'Move to the repo root' means move the subfolder's contents into /app, NOT into /. Prefer this flat layout over leaving the app in its subfolder: a split layout (app in a subfolder, compose in /app) is a recurring trap — later turns run a command from the wrong directory because the project's real location isn't obvious. Moving also removes the scaffold's own nested .git; delete any nested .git that remains, so git tracks the individual files rather than recording the folder as a submodule pointer (an empty gitlink) that reaches the repository with none of your code. Confirm with git status (run in /app) that individual files are staged, not a single folder entry, and that the dev server still works. If you truly cannot move it, keep docker-compose.base44.yml INSIDE the same subfolder as the app (never split them) and record the project directory in .base44/environment.json so later turns don't guess.", "When you move the project, move it as-is — do NOT delete and reinstall node_modules afterward. The dev server runs in the compose service, which provisions its own node_modules in a named volume; the host copy is only needed for the pre-move seed step, so a host reinstall after moving is pure wasted time.", "", "Getting it to preview — known facts; do not reverse-engineer the Wix CLI, astro, or vite to rediscover them:", From a3713cee9be408ebb0d1b94a90c999cab8c2b2a7 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 11:41:23 +0300 Subject: [PATCH 60/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20flatt?= =?UTF-8?q?en=20timing=20(wait=20for=20bg=20install+seed)=20+=20check-if-a?= =?UTF-8?q?lready-flat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real failure is timing: fast-path starts the dep install and seed in the background the instant it returns, in the scaffold subfolder; moving the folder mid-install corrupts node_modules and forces a reinstall. Prose now: check if fast-path already flattened to /app (don't double-move); else wait for BOTH done-markers before moving, move as-is (no reinstall), into /app never /. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 2ea762502..b59388603 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -19,8 +19,8 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "", "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "FIRST, anchor on the repository root: it is /app — the git repository this Base44 app lives in (the directory that holds its .git). Do ALL work there: cd to /app before scaffolding, run the scaffold from /app so it lands in a subfolder OF /app, and treat 'the repo root' as /app everywhere below. NEVER cd to / or operate from the OS filesystem root — scaffolding or moving files into / is a destructive mistake (it litters the container's root filesystem, not your project).", - "Then, once the scaffold exists under /app, MOVE the whole scaffolded project up into /app itself — one flat layout where the app, docker-compose.base44.yml, and config all live directly in /app. 'Move to the repo root' means move the subfolder's contents into /app, NOT into /. Prefer this flat layout over leaving the app in its subfolder: a split layout (app in a subfolder, compose in /app) is a recurring trap — later turns run a command from the wrong directory because the project's real location isn't obvious. Moving also removes the scaffold's own nested .git; delete any nested .git that remains, so git tracks the individual files rather than recording the folder as a submodule pointer (an empty gitlink) that reaches the repository with none of your code. Confirm with git status (run in /app) that individual files are staged, not a single folder entry, and that the dev server still works. If you truly cannot move it, keep docker-compose.base44.yml INSIDE the same subfolder as the app (never split them) and record the project directory in .base44/environment.json so later turns don't guess.", - "When you move the project, move it as-is — do NOT delete and reinstall node_modules afterward. The dev server runs in the compose service, which provisions its own node_modules in a named volume; the host copy is only needed for the pre-move seed step, so a host reinstall after moving is pure wasted time.", + "You want a flat layout — the app, docker-compose.base44.yml, and config all directly in /app — because the platform runs the compose file from /app, and a split (app in a subfolder, compose in /app) makes later turns cd into the wrong directory. Newer fast-path already flattens the scaffold into /app for you: CHECK FIRST (is wix.config.json / package.json directly in /app?), and if it is flat, do NOT move anything.", + "If the scaffold is still in a subfolder, MIND THE TIMING — this is the single most common failure here: the moment fast-path returns it has already started the dependency install AND the seed running in the background, inside that subfolder. Moving the folder while node_modules is being written corrupts it and forces a slow reinstall. So do NOT move mid-install: WAIT for both background jobs to finish (their done-markers: node_modules/.package-lock.json for the install, .seed-exit for the seed), and only THEN move the subfolder's contents into /app — as-is, node_modules included, no delete-and-reinstall. 'Into /app' means the git repo root, never / (the OS filesystem root). Remove any nested .git so git tracks the files, not a submodule gitlink, and confirm with git status (in /app) that individual files are staged, not a single folder entry.", "", "Getting it to preview — known facts; do not reverse-engineer the Wix CLI, astro, or vite to rediscover them:", "The dev command is the Wix CLI dev command (the dev script in package.json); it wraps astro dev, which wraps vite. Run it as a docker-compose.base44.yml service on port 3000 — never a bare astro dev, never a production build.", From 2362c2b5514984f4004cd6b4730c18c64943ef64 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 11:45:04 +0300 Subject: [PATCH 61/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20pass?= =?UTF-8?q?=20--flatten=20to=20fast-path=20(opt-in,=20deploy-lag-safe)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer the skill's --flatten scaffold flag (lands the project flat in /app; older skill versions ignore it harmlessly) over the fragile hand-flatten; keep the wait-for-done-markers timing net for when the flag isn't available yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index b59388603..ff6b3ddc9 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -19,7 +19,7 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "", "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", "FIRST, anchor on the repository root: it is /app — the git repository this Base44 app lives in (the directory that holds its .git). Do ALL work there: cd to /app before scaffolding, run the scaffold from /app so it lands in a subfolder OF /app, and treat 'the repo root' as /app everywhere below. NEVER cd to / or operate from the OS filesystem root — scaffolding or moving files into / is a destructive mistake (it litters the container's root filesystem, not your project).", - "You want a flat layout — the app, docker-compose.base44.yml, and config all directly in /app — because the platform runs the compose file from /app, and a split (app in a subfolder, compose in /app) makes later turns cd into the wrong directory. Newer fast-path already flattens the scaffold into /app for you: CHECK FIRST (is wix.config.json / package.json directly in /app?), and if it is flat, do NOT move anything.", + "You want a flat layout — the app, docker-compose.base44.yml, and config all directly in /app — because the platform runs the compose file from /app, and a split (app in a subfolder, compose in /app) makes later turns cd into the wrong directory. Best way to get it: pass --flatten to the skill's fast-path scaffolder so it lands the project directly in /app (older skill versions ignore the flag harmlessly). Then CHECK: if wix.config.json / package.json are directly in /app, it flattened — do NOT move anything.", "If the scaffold is still in a subfolder, MIND THE TIMING — this is the single most common failure here: the moment fast-path returns it has already started the dependency install AND the seed running in the background, inside that subfolder. Moving the folder while node_modules is being written corrupts it and forces a slow reinstall. So do NOT move mid-install: WAIT for both background jobs to finish (their done-markers: node_modules/.package-lock.json for the install, .seed-exit for the seed), and only THEN move the subfolder's contents into /app — as-is, node_modules included, no delete-and-reinstall. 'Into /app' means the git repo root, never / (the OS filesystem root). Remove any nested .git so git tracks the files, not a submodule gitlink, and confirm with git status (in /app) that individual files are staged, not a single folder entry.", "", "Getting it to preview — known facts; do not reverse-engineer the Wix CLI, astro, or vite to rediscover them:", From baeaeba00c640bc04754de95701832b7ae257def Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 12:05:47 +0300 Subject: [PATCH 62/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20trim?= =?UTF-8?q?=20nested-.git=20guidance=20now=20that=20fast-path=20scaffolds?= =?UTF-8?q?=20--skip-git?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified in the wix-cli source that the scaffold git-inits by default but honors --skip-git; the skill's fast-path now passes it, so there is usually no nested .git. Keep only a brief conditional cleanup note for the deploy-lag window. --- packages/cli/src/cli/commands/imported/expansions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index ff6b3ddc9..9356f5c98 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -17,7 +17,7 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "Fetch and follow this skill: https://www.wix.com/skills/headless-fast/entry/skill.md", "Follow it exactly.", "", - "One repository — this is important. The skill scaffolds the site into a subfolder named after the business (via `npm create @wix/new`), and that generator initializes its own git repository (a `.git`) inside the new subfolder. But this Base44 app is already a single git repository, and it commits and pushes your work automatically at the end of the turn. A nested `.git` breaks that: git records the whole scaffolded subfolder as a submodule pointer (an empty gitlink) instead of its files, so none of your code reaches the repository.", + "One repository — this is important. The scaffolder can initialize its OWN git repo inside the new subfolder. This Base44 app is already a single git repository (it commits and pushes your work at the end of the turn), so a nested `.git` breaks it: git records the whole subfolder as an empty submodule pointer (a gitlink) instead of its files, and none of your code reaches the repository. The skill's fast-path now scaffolds with git skipped, so usually there is nothing to do — but if a nested `.git` did get created, delete it so git tracks the real files.", "FIRST, anchor on the repository root: it is /app — the git repository this Base44 app lives in (the directory that holds its .git). Do ALL work there: cd to /app before scaffolding, run the scaffold from /app so it lands in a subfolder OF /app, and treat 'the repo root' as /app everywhere below. NEVER cd to / or operate from the OS filesystem root — scaffolding or moving files into / is a destructive mistake (it litters the container's root filesystem, not your project).", "You want a flat layout — the app, docker-compose.base44.yml, and config all directly in /app — because the platform runs the compose file from /app, and a split (app in a subfolder, compose in /app) makes later turns cd into the wrong directory. Best way to get it: pass --flatten to the skill's fast-path scaffolder so it lands the project directly in /app (older skill versions ignore the flag harmlessly). Then CHECK: if wix.config.json / package.json are directly in /app, it flattened — do NOT move anything.", "If the scaffold is still in a subfolder, MIND THE TIMING — this is the single most common failure here: the moment fast-path returns it has already started the dependency install AND the seed running in the background, inside that subfolder. Moving the folder while node_modules is being written corrupts it and forces a slow reinstall. So do NOT move mid-install: WAIT for both background jobs to finish (their done-markers: node_modules/.package-lock.json for the install, .seed-exit for the seed), and only THEN move the subfolder's contents into /app — as-is, node_modules included, no delete-and-reinstall. 'Into /app' means the git repo root, never / (the OS filesystem root). Remove any nested .git so git tracks the files, not a submodule gitlink, and confirm with git status (in /app) that individual files are staged, not a single folder entry.", From af64ea0e1cca4cb65f9a650b125c2a34e4101bef Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 12:20:17 +0300 Subject: [PATCH 63/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20fetch?= =?UTF-8?q?=20the=20skill=20with=20curl,=20not=20a=20web-page=20reader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fetch_website re-escapes the markdown (\> \*\* …), so the agent doesn't get an exact copy. Ask for a raw curl fetch to a file. Phrased as prose (no command syntax) so it doesn't trip the create-body WAF; verified create still passes. --- packages/cli/src/cli/commands/imported/expansions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 9356f5c98..29b25e545 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -14,7 +14,7 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { // rejects command-shaped request bodies; the agent fetches URLs itself. // The nested-git note prevents the scaffold from committing as a gitlink. text: [ - "Fetch and follow this skill: https://www.wix.com/skills/headless-fast/entry/skill.md", + "Retrieve this skill file with curl (a raw fetch to a file), NOT a web-page/markdown reader tool — a reader re-escapes the content and you must have the exact bytes: https://www.wix.com/skills/headless-fast/entry/skill.md", "Follow it exactly.", "", "One repository — this is important. The scaffolder can initialize its OWN git repo inside the new subfolder. This Base44 app is already a single git repository (it commits and pushes your work at the end of the turn), so a nested `.git` breaks it: git records the whole subfolder as an empty submodule pointer (a gitlink) instead of its files, and none of your code reaches the repository. The skill's fast-path now scaffolds with git skipped, so usually there is nothing to do — but if a nested `.git` did get created, delete it so git tracks the real files.", From b892d487939c734bfeaede7c6e8b6f61e953bd18 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 13:58:54 +0300 Subject: [PATCH 64/73] =?UTF-8?q?fix(cli):=20rounder=20logo=20=E2=80=94=20?= =?UTF-8?q?quadrant=20blocks=20(2x2)=20+=20aspect=20correction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Half-blocks only add vertical resolution, so the sun's sides came out jagged. Switch to quadrant blocks (2x2 sub-pixels per cell) for smooth curved edges, and use 2x sub-columns to correct the ~1:2 quadrant sub-cell aspect so it reads round, not as a tall oval. Two knobs: LOGO_ROWS (size) and LOGO_GAP_SUBROW (stripe). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --- .../cli/src/cli/commands/imported/session.tsx | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 639f0b600..ae8788b10 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -364,26 +364,40 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { * clears the input, then exits; turns keep running server-side after exit. * TTY only — callers gate on interactivity. */ -// The Base44 mark, rendered rather than hand-drawn: a round sun with a single -// thin blank stripe across it. Terminal cells are ~2:1, so a naive block grid is -// a tall oval; half-blocks make each text row two ~square pixels, so the disc -// comes out round. One blanked pixel-row (LOGO_GAP) is the stripe. Rows are -// trimmed so renderHeader's center() aligns them. -const LOGO_RADIUS = 5; -const LOGO_GAP = 7; // the single blank stripe, low in the circle: this pixel-row is cleared +// The Base44 mark, rendered rather than hand-drawn: a round sun with one thin +// blank stripe (the setting-sun slit). Built from QUADRANT blocks — 2x2 sub-pixels +// per character — for smooth curved edges (half-blocks only round top/bottom, so +// the sides came out jagged). Terminal cells are ~2:1, and a quadrant sub-cell is +// ~1:2, so we use 2x as many sub-columns as sub-rows (`sx = 2 * sy`) to correct +// the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in +// text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the +// disc. Rows are trimmed so renderHeader's center() aligns them. +const LOGO_ROWS = 6; +const LOGO_GAP_SUBROW = 9; +// Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). +const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { - const n = LOGO_RADIUS * 2; - const c = (n - 1) / 2; - const rad = LOGO_RADIUS - 0.5; - const inside = (px: number, py: number) => - py !== LOGO_GAP && (px - c) ** 2 + (py - c) ** 2 <= rad * rad + 0.5; + const sy = 2 * LOGO_ROWS; + const sx = 2 * sy; + const cx = (sx - 1) / 2; + const cy = (sy - 1) / 2; + const rad = sy / 2 - 0.5; + const on = (px: number, py: number): boolean => { + if (py === LOGO_GAP_SUBROW) return false; + const dx = (px - cx) * 0.5; + const dy = py - cy; + return dx * dx + dy * dy <= rad * rad + 0.3; + }; const rows: string[] = []; - for (let ty = 0; ty < n; ty += 2) { + for (let ty = 0; ty < sy; ty += 2) { let row = ""; - for (let px = 0; px < n; px++) { - const top = inside(px, ty); - const bot = inside(px, ty + 1); - row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " "; + for (let tx = 0; tx < sx; tx += 2) { + const bits = + (on(tx, ty) ? 1 : 0) | + (on(tx + 1, ty) ? 2 : 0) | + (on(tx, ty + 1) ? 4 : 0) | + (on(tx + 1, ty + 1) ? 8 : 0); + row += QUAD[bits]; } rows.push(row.trim()); } From 189be0bf91048f1e8e75a552503bcf3406da3533 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:05:40 +0300 Subject: [PATCH 65/73] =?UTF-8?q?fix(cli):=20smooth=20logo=20poles=20?= =?UTF-8?q?=E2=80=94=20rows=3D7,=20drop=20the=20+0.3=20fill=20tolerance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tolerance over-filled the nearly-flat poles, bulging the top/bottom (▟▙ in the middle of the top row). At radius 7 with an exact boundary the poles read as a smooth arc. --- packages/cli/src/cli/commands/imported/session.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index ae8788b10..1b87b0f93 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -372,8 +372,8 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_ROWS = 6; -const LOGO_GAP_SUBROW = 9; +const LOGO_ROWS = 7; +const LOGO_GAP_SUBROW = 11; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { @@ -386,7 +386,9 @@ function buildLogoRows(): string[] { if (py === LOGO_GAP_SUBROW) return false; const dx = (px - cx) * 0.5; const dy = py - cy; - return dx * dx + dy * dy <= rad * rad + 0.3; + // No tolerance: a positive fudge over-fills the poles (nearly-flat there) and + // makes the top/bottom bulge instead of reading as a smooth arc. + return dx * dx + dy * dy <= rad * rad; }; const rows: string[] = []; for (let ty = 0; ty < sy; ty += 2) { From 62bc314a6cc44291edcda4bc2df6d5995efec203 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:09:56 +0300 Subject: [PATCH 66/73] fix(cli): bigger, rounder logo sun (rows=10) --- packages/cli/src/cli/commands/imported/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 1b87b0f93..123d94a48 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -372,8 +372,8 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_ROWS = 7; -const LOGO_GAP_SUBROW = 11; +const LOGO_ROWS = 10; +const LOGO_GAP_SUBROW = 16; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { From e4bc9c35eecd48f4aa26eb4f1228d09e9144d222 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:10:57 +0300 Subject: [PATCH 67/73] =?UTF-8?q?fix(cli):=20logo=20size=20back=20to=20com?= =?UTF-8?q?pact=20(rows=3D8)=20=E2=80=94=20round=20without=20being=20huge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cli/src/cli/commands/imported/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 123d94a48..05cdaf310 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -372,8 +372,8 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_ROWS = 10; -const LOGO_GAP_SUBROW = 16; +const LOGO_ROWS = 8; +const LOGO_GAP_SUBROW = 13; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { From 6f0d1545cd3de266785c04e59a3a3f67cc3d85bc Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:13:02 +0300 Subject: [PATCH 68/73] fix(cli): smaller logo (rows=6) with a visible slit --- packages/cli/src/cli/commands/imported/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 05cdaf310..1cdb947bd 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -372,8 +372,8 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_ROWS = 8; -const LOGO_GAP_SUBROW = 13; +const LOGO_ROWS = 6; +const LOGO_GAP_SUBROW = 8; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { From 90763cde80cef3ec269df9010372644c05855fe2 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:14:30 +0300 Subject: [PATCH 69/73] =?UTF-8?q?fix(cli):=20logo=20=E2=80=94=20one=20more?= =?UTF-8?q?=20row=20below=20the=20slit=20(rows=3D7,=20stripe=20at=208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cli/src/cli/commands/imported/session.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 1cdb947bd..db2e9046f 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -372,7 +372,7 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_ROWS = 6; +const LOGO_ROWS = 7; const LOGO_GAP_SUBROW = 8; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; From 821d81406d56c7cbe21b9e3d8cb47902cadc33fd Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:16:11 +0300 Subject: [PATCH 70/73] =?UTF-8?q?fix(cli):=20logo=20=E2=80=94=20move=20the?= =?UTF-8?q?=20slit=20one=20row=20down=20(stripe=20at=2010)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cli/src/cli/commands/imported/session.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index db2e9046f..86b26f06f 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -373,7 +373,7 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. const LOGO_ROWS = 7; -const LOGO_GAP_SUBROW = 8; +const LOGO_GAP_SUBROW = 10; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { From d6eaa52ea7b87125e3569ff79e9a2c85774c6ca8 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Wed, 16 Sep 2026 14:17:51 +0300 Subject: [PATCH 71/73] fix(cli): smaller logo circle (rows=6) --- packages/cli/src/cli/commands/imported/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 86b26f06f..1cdb947bd 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -372,8 +372,8 @@ function SessionView({ engine, footer, subscribe }: ViewProps) { // the aspect and keep the disc round instead of a tall oval. LOGO_ROWS = height in // text rows; LOGO_GAP_SUBROW = the cleared sub-row (0..2*LOGO_ROWS-1), low in the // disc. Rows are trimmed so renderHeader's center() aligns them. -const LOGO_ROWS = 7; -const LOGO_GAP_SUBROW = 10; +const LOGO_ROWS = 6; +const LOGO_GAP_SUBROW = 8; // Index by tl | tr<<1 | bl<<2 | br<<3 (the four 2x2 sub-pixels of one cell). const QUAD = " ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"; function buildLogoRows(): string[] { From 1a0bb954d848e081f04d4cce934eaf65e4ccfe00 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Thu, 17 Sep 2026 09:25:45 +0300 Subject: [PATCH 72/73] =?UTF-8?q?fix(cli):=20/headless=20=E2=80=94=20navig?= =?UTF-8?q?ate=20MPA=20pages=20via=20full=20reload,=20don't=20click-throug?= =?UTF-8?q?h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The preview bridge's window.__base44_preview.navigate() default is SPA client-routing; a Wix-Headless (astro MPA) app never emits the route-render signal it waits for, so a plain navigate() hangs ~35s and reports the iframe unresponsive. Tell the agent to move pages with navigate(path, { reload: true }) (a real load) and to verify with screenshots + non-navigating reads, not click-to-navigate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- packages/cli/src/cli/commands/imported/expansions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/cli/commands/imported/expansions.ts b/packages/cli/src/cli/commands/imported/expansions.ts index 29b25e545..1289fd8d0 100644 --- a/packages/cli/src/cli/commands/imported/expansions.ts +++ b/packages/cli/src/cli/commands/imported/expansions.ts @@ -28,6 +28,8 @@ const PROMPT_EXPANSIONS: Record<string, PromptExpansion> = { "The Wix dev command authenticates against the linked Wix site (wix.config.json) and syncs content and generates types before it serves — slow on top of the install; give the build and first request a high timeout and wait, a not-yet-listening port is not a failure. Its credentials live under the container root's .wix directory — mount that into the service so it can authenticate.", "Keep node_modules on a named volume, and avoid recreating or rebuilding the service repeatedly: every recreate re-runs the install and adds about half a minute per retry.", "Verify by requesting the local port-3000 root and checking for a 200 status — 200 means it is serving AND bound. Do NOT build a host header out of a sandbox-id variable; there is none, and you will loop on connection failures. If it answers inside the container but not from the host, it is the binding setting above, not the app. The platform verifies the real external host allowlist at end of turn.", + "", + "Verifying pages with the preview tools (preview_execute_code / preview_screenshot) — one adjustment for this app type: a Wix Headless app is multi-page (astro), not a single-page React app, so the default in-iframe navigation helper waits for a client-side route re-render that never happens here and times out as 'iframe did not respond', burning ~35s a call. So when you need to MOVE to another page, pass the reload option — window.__base44_preview.navigate(path, { reload: true }) — which does a real full page load and returns right away. For the same reason do NOT click an element that navigates to a new page (the full reload tears the page down mid-command); instead navigate to the target URL directly with { reload: true }, then verify. Read-only checks on the current page — window.__base44_preview.domSnapshot(), .inspect(selector), .consoleLogs({ level: 'error' }) — and preview_screenshot work normally and need no reload option; prefer them plus a screenshot for verifying appearance.", ].join("\n"), }, }; From e60e0da326de98af4c02887eed8a4d1726a78f51 Mon Sep 17 00:00:00 2001 From: ayal <ayal@wix.com> Date: Thu, 17 Sep 2026 09:25:59 +0300 Subject: [PATCH 73/73] feat(cli): base44 code builds a normal Base44 app by default; --import for repos; reconnect link on expired GitHub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Genesis mode now picks the app type. `base44 code` (or --builder) creates a normal user_app — the standard Base44 builder agent + React template — via POST /api/apps with the first prompt as initial_message (no app_type: the backend defaults to user_app; no imported repo fields). `base44 code --import` keeps the imported flow (a fresh repo, or /headless → Wix Headless). The session, stream, model picker and preview are shared: the chat and sandbox/preview-url endpoints are app-type-agnostic. The active mode shows in the header (a labelled line) and footer (a chip). Also: when a create fails because the caller's GitHub OAuth token is expired (a 401 from api.github.com while verifying repo access), both create paths now print a clickable Reconnect-GitHub link (POST /api/github/oauth/initiate) instead of a bare 401 — a plain retry just 401s again. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- packages/cli/src/cli/commands/code.ts | 67 +++++++++++--- .../cli/src/cli/commands/imported/create.ts | 87 +++++++++++++++++-- .../cli/src/cli/commands/imported/session.tsx | 33 +++++-- .../cli/src/core/resources/imported/api.ts | 69 +++++++++++++++ 4 files changed, 236 insertions(+), 20 deletions(-) diff --git a/packages/cli/src/cli/commands/code.ts b/packages/cli/src/cli/commands/code.ts index 119fcc19d..56b1a974c 100644 --- a/packages/cli/src/cli/commands/code.ts +++ b/packages/cli/src/cli/commands/code.ts @@ -1,4 +1,8 @@ -import { bootstrapBlankApp } from "@/cli/commands/imported/create.js"; +import chalk from "chalk"; +import { + bootstrapBlankApp, + bootstrapBuilderApp, +} from "@/cli/commands/imported/create.js"; import { runGenesisSession, runInteractiveSession, @@ -9,12 +13,26 @@ import { InvalidInputError } from "@/core/errors.js"; import { initAppContext } from "@/core/project/app-config.js"; import { soleActiveBranchId } from "@/core/resources/imported/api.js"; -async function codeAction(_ctx: CLIContext): Promise<RunCommandResult> { +interface CodeOptions { + import?: boolean; + builder?: boolean; +} + +async function codeAction( + _ctx: CLIContext, + options: CodeOptions, +): Promise<RunCommandResult> { if (process.stdout.isTTY !== true) { throw new InvalidInputError( "base44 code is an interactive session and needs a terminal.", ); } + if (options.import && options.builder) { + throw new InvalidInputError( + "Pass either --builder or --import, not both (the default is --builder).", + ); + } + const importMode = options.import === true; // Inside a linked app project, open the session on that app; anywhere // else, the first prompt creates one from scratch. Resolution must be the @@ -38,13 +56,34 @@ async function codeAction(_ctx: CLIContext): Promise<RunCommandResult> { return { outroMessage: "Done." }; } - const footer: string[] = []; - await runGenesisSession({ - idleHint: "describe the app you want to build", - creatingLabel: "creating your repository and app", - footer, - createApp: (prompt, emit) => bootstrapBlankApp(prompt, footer, emit), - }); + // Genesis mode picks the app type: --builder (default) makes a normal Base44 + // app (template + agent); --import makes an imported app (a repo, or + // /headless → Wix Headless). The choice is a header/footer label + which + // bootstrap runs; the session itself is identical either way. + const footer: string[] = [ + importMode + ? chalk.dim(`${chalk.hex("#E86B3C")("●")} import`) + : chalk.dim(`${chalk.hex("#E86B3C")("●")} builder`), + ]; + await runGenesisSession( + importMode + ? { + idleHint: + "describe the app, or import a repo — /headless for Wix Headless", + creatingLabel: "creating your repository and app", + modeLabel: "Import — your repo or /headless", + footer, + createApp: (prompt, emit) => bootstrapBlankApp(prompt, footer, emit), + } + : { + idleHint: "describe the app you want to build", + creatingLabel: "creating your app", + modeLabel: "Builder — Base44 template + agent", + footer, + createApp: (prompt, emit) => + bootstrapBuilderApp(prompt, footer, emit), + }, + ); return { outroMessage: "Done." }; } @@ -52,7 +91,15 @@ export function getCodeCommand(): Base44Command { const command = new Base44Command("code", { requireAppContext: false }); command .description( - "Open Base44 Code: an interactive agent session — in an empty directory, your first prompt creates the app", + "Open Base44 Code: an interactive agent session — in an empty directory, your first prompt creates the app (default: the Base44 builder; --import for a repo / Wix Headless)", + ) + .option( + "--builder", + "From scratch with the Base44 builder agent + template (default)", + ) + .option( + "--import", + "Start from a GitHub repo, or /headless for Wix Headless, instead of the Base44 template", ) .action(codeAction); return command; diff --git a/packages/cli/src/cli/commands/imported/create.ts b/packages/cli/src/cli/commands/imported/create.ts index cb1d4e277..8106cc57f 100644 --- a/packages/cli/src/cli/commands/imported/create.ts +++ b/packages/cli/src/cli/commands/imported/create.ts @@ -21,10 +21,13 @@ import { writeAppConfig, } from "@/core/project/app-config.js"; import { + createBuilderApp, createImportedApp, getImportedAppState, getImportedPreviewUrl, + isGithubUserTokenError, soleActiveBranchId, + startGithubReauth, } from "@/core/resources/imported/api.js"; import { streamConversationUntilSettled } from "@/core/resources/imported/stream.js"; @@ -148,12 +151,33 @@ async function createImportedAction( : "importing the repository"; // Interactive runs start the full-page frame immediately — the create call // spins inside it rather than in a clack task outside the page. - const created = interactiveEarly - ? await withBootScreen(bootLabel, createCall) - : await runTask( - blank ? "Creating your repository and app" : "Importing the repository", - createCall, + let created: Awaited<ReturnType<typeof createCall>>; + try { + created = interactiveEarly + ? await withBootScreen(bootLabel, createCall) + : await runTask( + blank + ? "Creating your repository and app" + : "Importing the repository", + createCall, + ); + } catch (error) { + // A stale GitHub connection surfaces as a 401 from api.github.com while the + // create verifies the caller's repo access. Point them at re-auth — a plain + // retry just 401s again. + if (isGithubUserTokenError(error)) { + const link = await startGithubReauth().catch(() => null); + log.message( + `${chalk.yellow("Your GitHub authorization expired.")} Reconnect, then run this again:`, + ); + log.message( + link + ? terminalLink("Reconnect GitHub", link) + : "Open Base44 → GitHub settings to reconnect your account.", ); + } + throw error; + } const configPath = await writeAppConfig(targetDir, created.id); // Root discovery (findProjectRoot) keys on a PROJECT config, not .app.jsonc — @@ -410,3 +434,56 @@ export async function bootstrapBlankApp( }, }; } + +/** Genesis bootstrap for the NORMAL Base44 flow: the session's first prompt + * creates a standard user_app (builder agent + React template — no GitHub + * repo), links a local directory, and hands back the engine wiring. Mirrors + * bootstrapBlankApp, minus the repo. Preview + chat run on the same app-scoped + * endpoints, so the session/stream are identical. */ +export async function bootstrapBuilderApp( + prompt: string, + footer: string[], + emit: (line: string) => void, +): Promise<{ + branchId?: string; + awaitingTurnLabel: string; + onTurnSettled: (info: { turnIndex: number; ok: boolean }) => Promise<void>; +}> { + const appName = inventRepoName(prompt); + const created = await createBuilderApp({ appName, prompt }); + const targetDir = join(process.cwd(), appName); + await mkdir(join(targetDir, "base44"), { recursive: true }); + await writeAppConfig(targetDir, created.id); + try { + await writeFile( + join(targetDir, "base44", "config.jsonc"), + `// Base44 project configuration.\n{\n "name": ${JSON.stringify(appName)}\n}\n`, + { flag: "wx" }, + ); + } catch { + // Already present — fine. + } + setAppContext({ id: created.id, projectRoot: targetDir }); + + const editorUrl = `${getBase44ApiUrl()}/apps/${created.id}/editor/preview`; + footer.push(terminalLink("editor", editorUrl)); + emit(chalk.dim(`linked ./${appName} (cd ${appName} after the session)`)); + + const branchId = await soleActiveBranchId().catch(() => undefined); + let previewPushed = false; + return { + branchId, + awaitingTurnLabel: "provisioning the sandbox and starting the build", + onTurnSettled: async ({ turnIndex, ok }) => { + if (turnIndex === 0 && ok && !previewPushed) { + try { + const previewUrl = await getImportedPreviewUrl(); + previewPushed = true; + footer.push(terminalLink("preview", previewUrl)); + } catch { + // Preview may still be booting; the editor shows it when up. + } + } + }, + }; +} diff --git a/packages/cli/src/cli/commands/imported/session.tsx b/packages/cli/src/cli/commands/imported/session.tsx index 1cdb947bd..f3f566b14 100644 --- a/packages/cli/src/cli/commands/imported/session.tsx +++ b/packages/cli/src/cli/commands/imported/session.tsx @@ -8,6 +8,7 @@ import { formatDuration, hardWrapAnsi, idleMusing, + terminalLink, } from "@/cli/commands/imported/render.js"; import type { SessionEngine, @@ -24,6 +25,10 @@ import { resolvePick, saveBuilderModel, } from "@/core/model.js"; +import { + isGithubUserTokenError, + startGithubReauth, +} from "@/core/resources/imported/api.js"; import packageJson from "../../../../package.json"; const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; @@ -408,7 +413,7 @@ function buildLogoRows(): string[] { /** The welcome header, Claude-Code style: the sun mark on the left, the title / * account / cwd lines stacked to its right. No box. */ -function renderHeader(who: string): string { +function renderHeader(who: string, mode?: string): string { const orange = chalk.hex(BRAND_ORANGE); const cwd = process.cwd().replace(process.env.HOME ?? "", "~"); const logo = buildLogoRows(); @@ -423,6 +428,7 @@ function renderHeader(who: string): string { chalk.bold(who ? `Welcome back, ${who}!` : "Welcome!"), chalk.dim(getBase44ApiUrl().replace(/^https:\/\//, "")), chalk.dim(cwd), + ...(mode ? [`${orange("●")} ${chalk.dim(mode)}`] : []), ]; const height = Math.max(logo.length, text.length); const textTop = Math.max(0, Math.floor((logo.length - text.length) / 2)); @@ -446,8 +452,8 @@ async function currentUserName(): Promise<string> { /** The Base44 Code welcome box — the session's first history item, so it * scrolls away naturally like Claude Code's header does. */ -async function buildHeader(): Promise<string> { - return renderHeader(await currentUserName()); +async function buildHeader(mode?: string): Promise<string> { + return renderHeader(await currentUserName(), mode); } /** Run `work` (e.g. the create call) inside the full-page frame: header at @@ -571,6 +577,8 @@ interface GenesisOptions { creatingLabel: string; /** Live footer array — `createApp` pushes the links as they exist. */ footer: string[]; + /** Short mode label rendered in the header (e.g. "Builder" / "Import"). */ + modeLabel?: string; /** Turn the first prompt into an app; returns the wiring for the real * engine, which takes over every later prompt. */ createApp: ( @@ -660,11 +668,26 @@ export async function runGenesisSession( await engine.start(false); inner = engine; }) - .catch((error: unknown) => { + .catch(async (error: unknown) => { creating = false; const message = error instanceof Error ? error.message : String(error); onLine(chalk.red(`✗ create failed: ${message}`)); + // A stale GitHub connection 401s while the create verifies repo + // access. Hand back a reconnect link — a plain retry just 401s again. + if (isGithubUserTokenError(error)) { + const link = await startGithubReauth().catch(() => null); + onLine( + chalk.yellow( + "GitHub authorization expired — reconnect, then try again:", + ), + ); + onLine( + link + ? terminalLink("Reconnect GitHub", link) + : "Open Base44 → GitHub settings to reconnect your account.", + ); + } }); }, status(): SessionStatus { @@ -685,7 +708,7 @@ export async function runGenesisSession( }; enterAltScreen(); - onLine(await buildHeader()); + onLine(await buildHeader(options.modeLabel)); process.stdout.write("\x1b[?2004h"); const stdinProxy = createPasteFriendlyStdin(process.stdin); diff --git a/packages/cli/src/core/resources/imported/api.ts b/packages/cli/src/core/resources/imported/api.ts index 1f21ddeee..abf9462c0 100644 --- a/packages/cli/src/core/resources/imported/api.ts +++ b/packages/cli/src/core/resources/imported/api.ts @@ -153,6 +153,75 @@ export async function createImportedApp( return parseOrThrow(CreatedAppSchema, await response.json(), "imported app"); } +interface CreateBuilderAppOptions { + appName?: string; + prompt?: string; + organizationId?: string; +} + +/** + * Create a NORMAL Base44 app (the standard builder-agent + React template + * flow). No app_type is sent — the backend defaults to "user_app" — and none + * of the imported repo fields apply. A non-empty prompt auto-starts the + * builder's first turn in the background; the scaffold sandbox provisions + * async, so the caller polls the conversation and the preview URL afterward. + */ +export async function createBuilderApp( + options: CreateBuilderAppOptions, +): Promise<CreatedImportedApp> { + let response: KyResponse; + try { + // The first builder turn kicks off server-side; the create itself returns + // fast, but keep ky patient in case the platform is slow to insert. + response = await base44Client.post("api/apps", { + timeout: false, + json: { + ...(options.appName ? { name: options.appName } : {}), + ...(options.organizationId + ? { organization_id: options.organizationId } + : {}), + ...(options.prompt + ? { initial_message: { content: options.prompt } } + : {}), + }, + }); + } catch (error) { + throw await ApiError.fromHttpError(error, "creating app"); + } + return parseOrThrow(CreatedAppSchema, await response.json(), "app"); +} + +const OAuthInitiateSchema = z.object({ authorization_url: z.string().min(1) }); + +/** + * Start a fresh GitHub OAuth (account-only, no re-install) and return the URL + * to open. Recovers from a stale connection: create/import verifies the + * caller's GitHub access with their OAuth token, and GitHub 401s an expired + * one — the fix is re-authorizing, not retrying. + */ +export async function startGithubReauth(): Promise<string> { + const response = await base44Client.post( + "api/github/oauth/initiate?skip_installation=true", + ); + return parseOrThrow( + OAuthInitiateSchema, + await response.json(), + "github oauth initiate", + ).authorization_url; +} + +/** + * True when a create/import failure is GitHub rejecting the caller's OAuth + * token (expired/revoked connection) — a 401 against api.github.com. Retrying + * without reconnecting just 401s again. + */ +export function isGithubUserTokenError(error: unknown): boolean { + const message = error instanceof Error ? error.message : String(error); + return ( + /api\.github\.com/i.test(message) && /\b401\b|unauthorized/i.test(message) + ); +} + export async function getImportedAppState( appId: string, ): Promise<ImportedAppState> {