Build: stop tracking bundled artifacts (#2455) (thanks @0oAstro)

Co-authored-by: 0oAstro <0oAstro@users.noreply.github.com>
This commit is contained in:
Gustavo Madeira Santana
2026-01-26 22:59:02 -05:00
parent 615ccf6411
commit c2a4863b15
13 changed files with 94 additions and 22 deletions

1
.gitignore vendored
View File

@@ -42,7 +42,6 @@ apps/ios/*.xcfilelist
vendor/a2ui/renderers/lit/dist/ vendor/a2ui/renderers/lit/dist/
src/canvas-host/a2ui/*.bundle.js src/canvas-host/a2ui/*.bundle.js
src/canvas-host/a2ui/*.map src/canvas-host/a2ui/*.map
src/canvas-host/a2ui/index.html
.bundle.hash .bundle.hash
# fastlane (iOS) # fastlane (iOS)

View File

@@ -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 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`. - 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. - 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: allow caption param for media sends. (#1888) Thanks @mguellsegarra.
- Telegram: support plugin sendPayload channelData (media/buttons) and validate plugin commands. (#1917) Thanks @JoshuaLelon. - 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. - Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco.

View File

@@ -39,7 +39,7 @@ export default defineConfig({
output: { output: {
file: outputFile, file: outputFile,
format: "esm", format: "esm",
inlineDynamicImports: true, codeSplitting: false,
sourcemap: false, sourcemap: false,
}, },
}); });

View File

@@ -175,6 +175,7 @@ clawdbot onboard --install-daemon
``` ```
If you dont have a global install yet, run the onboarding step via `pnpm clawdbot ...` from the repo. If you dont 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): Gateway (from this repo):

View File

@@ -82,7 +82,7 @@
"docs:bin": "node scripts/build-docs-list.mjs", "docs:bin": "node scripts/build-docs-list.mjs",
"docs:dev": "cd docs && mint dev", "docs:dev": "cd docs && mint dev",
"docs:build": "cd docs && pnpm dlx --reporter append-only mint broken-links", "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", "plugins:sync": "node --import tsx scripts/sync-plugin-versions.ts",
"release:check": "node --import tsx scripts/release-check.ts", "release:check": "node --import tsx scripts/release-check.ts",
"ui:install": "node scripts/ui.js install", "ui:install": "node scripts/ui.js install",

View File

@@ -1,6 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail 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)" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HASH_FILE="$ROOT_DIR/src/canvas-host/a2ui/.bundle.hash" HASH_FILE="$ROOT_DIR/src/canvas-host/a2ui/.bundle.hash"

View File

@@ -1,19 +1,44 @@
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import path from "node:path"; 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 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() { export function getA2uiPaths(env = process.env) {
await fs.stat(path.join(srcDir, "index.html")); const srcDir =
await fs.stat(path.join(srcDir, "a2ui.bundle.js")); 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.mkdir(path.dirname(outDir), { recursive: true });
await fs.cp(srcDir, outDir, { recursive: true }); await fs.cp(srcDir, outDir, { recursive: true });
} }
main().catch((err) => { async function main() {
console.error(String(err)); const { srcDir, outDir } = getA2uiPaths();
process.exit(1); await copyA2uiAssets({ srcDir, outDir });
}); }
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
main().catch((err) => {
console.error(String(err));
process.exit(1);
});
}

View File

@@ -48,7 +48,7 @@ export async function fetchBrowserJson<T>(
const timeoutMs = init?.timeoutMs ?? 5000; const timeoutMs = init?.timeoutMs ?? 5000;
try { try {
if (isAbsoluteHttp(url)) { if (isAbsoluteHttp(url)) {
return await fetchHttpJson<T>(url, { ...(init ?? {}), timeoutMs }); return await fetchHttpJson<T>(url, init ? { ...init, timeoutMs } : { timeoutMs });
} }
const started = await startBrowserControlServiceFromConfig(); const started = await startBrowserControlServiceFromConfig();
if (!started) { if (!started) {

View File

@@ -27,7 +27,7 @@ export function registerBrowserElementCommands(
.filter(Boolean) .filter(Boolean)
: undefined; : undefined;
try { try {
const result = await callBrowserAct({ const result = await callBrowserAct<{ url?: string }>({
parent, parent,
profile, profile,
body: { body: {

View File

@@ -26,7 +26,7 @@ export function registerBrowserFilesAndDownloadsCommands(
const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts);
try { try {
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
const result = await callBrowserRequest( const result = await callBrowserRequest<{ download: { path: string } }>(
parent, parent,
{ {
method: "POST", method: "POST",
@@ -68,7 +68,7 @@ export function registerBrowserFilesAndDownloadsCommands(
const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts);
try { try {
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
const result = await callBrowserRequest( const result = await callBrowserRequest<{ download: { path: string } }>(
parent, parent,
{ {
method: "POST", method: "POST",
@@ -108,7 +108,7 @@ export function registerBrowserFilesAndDownloadsCommands(
const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts); const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts);
try { try {
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
const result = await callBrowserRequest( const result = await callBrowserRequest<{ download: { path: string } }>(
parent, parent,
{ {
method: "POST", method: "POST",

View File

@@ -21,7 +21,7 @@ export function registerBrowserFormWaitEvalCommands(
fields: opts.fields, fields: opts.fields,
fieldsFile: opts.fieldsFile, fieldsFile: opts.fieldsFile,
}); });
const result = await callBrowserAct({ const result = await callBrowserAct<{ result?: unknown }>({
parent, parent,
profile, profile,
body: { body: {
@@ -66,7 +66,7 @@ export function registerBrowserFormWaitEvalCommands(
? (opts.load as "load" | "domcontentloaded" | "networkidle") ? (opts.load as "load" | "domcontentloaded" | "networkidle")
: undefined; : undefined;
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined; const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
const result = await callBrowserAct({ const result = await callBrowserAct<{ result?: unknown }>({
parent, parent,
profile, profile,
body: { body: {
@@ -108,7 +108,7 @@ export function registerBrowserFormWaitEvalCommands(
return; return;
} }
try { try {
const result = await callBrowserAct({ const result = await callBrowserAct<{ result?: unknown }>({
parent, parent,
profile, profile,
body: { body: {

View File

@@ -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"), "<html></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 });
}
});
});

View File

@@ -2,7 +2,7 @@ import { listChannelPlugins } from "../channels/plugins/index.js";
import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js"; import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
import type { ChannelId } from "../channels/plugins/types.js"; import type { ChannelId } from "../channels/plugins/types.js";
import type { ClawdbotConfig } from "../config/config.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 { resolveConfigPath, resolveStateDir } from "../config/paths.js";
import { resolveGatewayAuth } from "../gateway/auth.js"; import { resolveGatewayAuth } from "../gateway/auth.js";
import { formatCliCommand } from "../cli/command-format.js"; import { formatCliCommand } from "../cli/command-format.js";