From c2a4863b15c1fffc4a57ef5ea4221afafe2b64f5 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Mon, 26 Jan 2026 22:59:02 -0500 Subject: [PATCH] Build: stop tracking bundled artifacts (#2455) (thanks @0oAstro) Co-authored-by: 0oAstro <0oAstro@users.noreply.github.com> --- .gitignore | 1 - CHANGELOG.md | 1 + .../Tools/CanvasA2UI/rolldown.config.mjs | 2 +- docs/start/getting-started.md | 1 + package.json | 2 +- scripts/bundle-a2ui.sh | 6 +++ scripts/canvas-a2ui-copy.ts | 45 ++++++++++++++----- src/browser/client-fetch.ts | 2 +- .../register.element.ts | 2 +- .../register.files-downloads.ts | 6 +-- .../register.form-wait-eval.ts | 6 +-- src/scripts/canvas-a2ui-copy.test.ts | 40 +++++++++++++++++ src/security/audit.ts | 2 +- 13 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 src/scripts/canvas-a2ui-copy.test.ts diff --git a/.gitignore b/.gitignore index e513b071b..d3fdee6b5 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,6 @@ apps/ios/*.xcfilelist vendor/a2ui/renderers/lit/dist/ src/canvas-host/a2ui/*.bundle.js src/canvas-host/a2ui/*.map -src/canvas-host/a2ui/index.html .bundle.hash # fastlane (iOS) diff --git a/CHANGELOG.md b/CHANGELOG.md index e37880ccd..abfd5fc34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Status: unreleased. - Browser: route browser control via gateway/node; remove standalone browser control command and control URL config. - Browser: route `browser.request` via node proxies when available; honor proxy timeouts; derive browser ports from `gateway.port`. - Update: ignore dist/control-ui for dirty checks and restore after ui builds. (#1976) Thanks @Glucksberg. +- Build: bundle A2UI assets during build and stop tracking generated bundles. (#2455) Thanks @0oAstro. - Telegram: allow caption param for media sends. (#1888) Thanks @mguellsegarra. - Telegram: support plugin sendPayload channelData (media/buttons) and validate plugin commands. (#1917) Thanks @JoshuaLelon. - Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco. diff --git a/apps/shared/ClawdbotKit/Tools/CanvasA2UI/rolldown.config.mjs b/apps/shared/ClawdbotKit/Tools/CanvasA2UI/rolldown.config.mjs index 52c392fdd..75bef97e4 100644 --- a/apps/shared/ClawdbotKit/Tools/CanvasA2UI/rolldown.config.mjs +++ b/apps/shared/ClawdbotKit/Tools/CanvasA2UI/rolldown.config.mjs @@ -39,7 +39,7 @@ export default defineConfig({ output: { file: outputFile, format: "esm", - inlineDynamicImports: true, + codeSplitting: false, sourcemap: false, }, }); diff --git a/docs/start/getting-started.md b/docs/start/getting-started.md index 00bc00efb..790a79dfc 100644 --- a/docs/start/getting-started.md +++ b/docs/start/getting-started.md @@ -175,6 +175,7 @@ clawdbot onboard --install-daemon ``` If you don’t have a global install yet, run the onboarding step via `pnpm clawdbot ...` from the repo. +`pnpm build` also bundles A2UI assets; if you need to run just that step, use `pnpm canvas:a2ui:bundle`. Gateway (from this repo): diff --git a/package.json b/package.json index 1299d72d5..1a6d65178 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "docs:bin": "node scripts/build-docs-list.mjs", "docs:dev": "cd docs && mint dev", "docs:build": "cd docs && pnpm dlx --reporter append-only mint broken-links", - "build": "tsc -p tsconfig.json && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/write-build-info.ts", + "build": "pnpm canvas:a2ui:bundle && tsc -p tsconfig.json && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/write-build-info.ts", "plugins:sync": "node --import tsx scripts/sync-plugin-versions.ts", "release:check": "node --import tsx scripts/release-check.ts", "ui:install": "node scripts/ui.js install", diff --git a/scripts/bundle-a2ui.sh b/scripts/bundle-a2ui.sh index 7e3418fa5..0407b3386 100755 --- a/scripts/bundle-a2ui.sh +++ b/scripts/bundle-a2ui.sh @@ -1,6 +1,12 @@ #!/usr/bin/env bash set -euo pipefail +on_error() { + echo "A2UI bundling failed. Re-run with: pnpm canvas:a2ui:bundle" >&2 + echo "If this persists, verify pnpm deps and try again." >&2 +} +trap on_error ERR + ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" HASH_FILE="$ROOT_DIR/src/canvas-host/a2ui/.bundle.hash" diff --git a/scripts/canvas-a2ui-copy.ts b/scripts/canvas-a2ui-copy.ts index 3105dbfcf..b8a80675f 100644 --- a/scripts/canvas-a2ui-copy.ts +++ b/scripts/canvas-a2ui-copy.ts @@ -1,19 +1,44 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { fileURLToPath } from "node:url"; +import { fileURLToPath, pathToFileURL } from "node:url"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const srcDir = path.join(repoRoot, "src", "canvas-host", "a2ui"); -const outDir = path.join(repoRoot, "dist", "canvas-host", "a2ui"); -async function main() { - await fs.stat(path.join(srcDir, "index.html")); - await fs.stat(path.join(srcDir, "a2ui.bundle.js")); +export function getA2uiPaths(env = process.env) { + const srcDir = + env.CLAWDBOT_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui"); + const outDir = + env.CLAWDBOT_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui"); + return { srcDir, outDir }; +} + +export async function copyA2uiAssets({ + srcDir, + outDir, +}: { + srcDir: string; + outDir: string; +}) { + try { + await fs.stat(path.join(srcDir, "index.html")); + await fs.stat(path.join(srcDir, "a2ui.bundle.js")); + } catch (err) { + const message = + 'Missing A2UI bundle assets. Run "pnpm canvas:a2ui:bundle" and retry.'; + throw new Error(message, { cause: err }); + } await fs.mkdir(path.dirname(outDir), { recursive: true }); await fs.cp(srcDir, outDir, { recursive: true }); } -main().catch((err) => { - console.error(String(err)); - process.exit(1); -}); +async function main() { + const { srcDir, outDir } = getA2uiPaths(); + await copyA2uiAssets({ srcDir, outDir }); +} + +if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { + main().catch((err) => { + console.error(String(err)); + process.exit(1); + }); +} diff --git a/src/browser/client-fetch.ts b/src/browser/client-fetch.ts index 43bc7c07b..06facc416 100644 --- a/src/browser/client-fetch.ts +++ b/src/browser/client-fetch.ts @@ -48,7 +48,7 @@ export async function fetchBrowserJson( const timeoutMs = init?.timeoutMs ?? 5000; try { if (isAbsoluteHttp(url)) { - return await fetchHttpJson(url, { ...(init ?? {}), timeoutMs }); + return await fetchHttpJson(url, init ? { ...init, timeoutMs } : { timeoutMs }); } const started = await startBrowserControlServiceFromConfig(); if (!started) { diff --git a/src/cli/browser-cli-actions-input/register.element.ts b/src/cli/browser-cli-actions-input/register.element.ts index c018e7e24..fff318ccf 100644 --- a/src/cli/browser-cli-actions-input/register.element.ts +++ b/src/cli/browser-cli-actions-input/register.element.ts @@ -27,7 +27,7 @@ export function registerBrowserElementCommands( .filter(Boolean) : undefined; try { - const result = await callBrowserAct({ + const result = await callBrowserAct<{ url?: string }>({ parent, profile, body: { diff --git a/src/cli/browser-cli-actions-input/register.files-downloads.ts b/src/cli/browser-cli-actions-input/register.files-downloads.ts index baa3c71a1..0abe9f066 100644 --- a/src/cli/browser-cli-actions-input/register.files-downloads.ts +++ b/src/cli/browser-cli-actions-input/register.files-downloads.ts @@ -26,7 +26,7 @@ export function registerBrowserFilesAndDownloadsCommands( const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); try { const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; - const result = await callBrowserRequest( + const result = await callBrowserRequest<{ download: { path: string } }>( parent, { method: "POST", @@ -68,7 +68,7 @@ export function registerBrowserFilesAndDownloadsCommands( const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); try { const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; - const result = await callBrowserRequest( + const result = await callBrowserRequest<{ download: { path: string } }>( parent, { method: "POST", @@ -108,7 +108,7 @@ export function registerBrowserFilesAndDownloadsCommands( const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); try { const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; - const result = await callBrowserRequest( + const result = await callBrowserRequest<{ download: { path: string } }>( parent, { method: "POST", diff --git a/src/cli/browser-cli-actions-input/register.form-wait-eval.ts b/src/cli/browser-cli-actions-input/register.form-wait-eval.ts index f71662700..f5e90c132 100644 --- a/src/cli/browser-cli-actions-input/register.form-wait-eval.ts +++ b/src/cli/browser-cli-actions-input/register.form-wait-eval.ts @@ -21,7 +21,7 @@ export function registerBrowserFormWaitEvalCommands( fields: opts.fields, fieldsFile: opts.fieldsFile, }); - const result = await callBrowserAct({ + const result = await callBrowserAct<{ result?: unknown }>({ parent, profile, body: { @@ -66,7 +66,7 @@ export function registerBrowserFormWaitEvalCommands( ? (opts.load as "load" | "domcontentloaded" | "networkidle") : undefined; const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; - const result = await callBrowserAct({ + const result = await callBrowserAct<{ result?: unknown }>({ parent, profile, body: { @@ -108,7 +108,7 @@ export function registerBrowserFormWaitEvalCommands( return; } try { - const result = await callBrowserAct({ + const result = await callBrowserAct<{ result?: unknown }>({ parent, profile, body: { diff --git a/src/scripts/canvas-a2ui-copy.test.ts b/src/scripts/canvas-a2ui-copy.test.ts new file mode 100644 index 000000000..ffdd6dc1b --- /dev/null +++ b/src/scripts/canvas-a2ui-copy.test.ts @@ -0,0 +1,40 @@ +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { copyA2uiAssets } from "../../scripts/canvas-a2ui-copy.js"; + +describe("canvas a2ui copy", () => { + it("throws a helpful error when assets are missing", async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-a2ui-")); + + try { + await expect(copyA2uiAssets({ srcDir: dir, outDir: path.join(dir, "out") })).rejects.toThrow( + 'Run "pnpm canvas:a2ui:bundle"', + ); + } finally { + await fs.rm(dir, { recursive: true, force: true }); + } + }); + + it("copies bundled assets to dist", async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-a2ui-")); + const srcDir = path.join(dir, "src"); + const outDir = path.join(dir, "dist"); + + try { + await fs.mkdir(srcDir, { recursive: true }); + await fs.writeFile(path.join(srcDir, "index.html"), "", "utf8"); + await fs.writeFile(path.join(srcDir, "a2ui.bundle.js"), "console.log(1);", "utf8"); + + await copyA2uiAssets({ srcDir, outDir }); + + await expect(fs.stat(path.join(outDir, "index.html"))).resolves.toBeTruthy(); + await expect(fs.stat(path.join(outDir, "a2ui.bundle.js"))).resolves.toBeTruthy(); + } finally { + await fs.rm(dir, { recursive: true, force: true }); + } + }); +}); diff --git a/src/security/audit.ts b/src/security/audit.ts index a96213134..032f0f40a 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -2,7 +2,7 @@ import { listChannelPlugins } from "../channels/plugins/index.js"; import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js"; import type { ChannelId } from "../channels/plugins/types.js"; import type { ClawdbotConfig } from "../config/config.js"; -import { resolveBrowserConfig } from "../browser/config.js"; +import { resolveBrowserConfig, resolveProfile } from "../browser/config.js"; import { resolveConfigPath, resolveStateDir } from "../config/paths.js"; import { resolveGatewayAuth } from "../gateway/auth.js"; import { formatCliCommand } from "../cli/command-format.js";