diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cc86bd63..3ef430e67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -277,6 +277,8 @@ jobs: checks-macos: if: github.event_name == 'pull_request' runs-on: macos-latest + env: + NODE_OPTIONS: --max-old-space-size=4096 strategy: fail-fast: false matrix: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1c55dd8d..9801fe158 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -409,6 +409,8 @@ importers: extensions/open-prose: {} + extensions/poe: {} + extensions/signal: {} extensions/slack: {} diff --git a/scripts/bundle-a2ui.sh b/scripts/bundle-a2ui.sh index a1cf7ff2b..e0a6dffed 100755 --- a/scripts/bundle-a2ui.sh +++ b/scripts/bundle-a2ui.sh @@ -30,10 +30,20 @@ collect_files() { } compute_hash() { + # Use sha256sum on Linux/Windows (Git Bash), shasum on macOS + local sha_cmd + if command -v sha256sum &>/dev/null; then + sha_cmd="sha256sum" + elif command -v shasum &>/dev/null; then + sha_cmd="shasum -a 256" + else + echo "No sha256 tool found (sha256sum or shasum)" >&2 + exit 1 + fi collect_files \ | LC_ALL=C sort -z \ - | xargs -0 shasum -a 256 \ - | shasum -a 256 \ + | xargs -0 $sha_cmd \ + | $sha_cmd \ | awk '{print $1}' } diff --git a/src/canvas-host/a2ui.ts b/src/canvas-host/a2ui.ts index 876436c57..0b35832d2 100644 --- a/src/canvas-host/a2ui.ts +++ b/src/canvas-host/a2ui.ts @@ -12,6 +12,31 @@ export const CANVAS_WS_PATH = "/__clawdbot/ws"; let cachedA2uiRootReal: string | null | undefined; let resolvingA2uiRoot: Promise | null = null; +/** Reset the A2UI root cache (for testing). */ +export function resetA2uiCache(): void { + cachedA2uiRootReal = undefined; + resolvingA2uiRoot = null; +} + +async function findRepoRoot(startDir: string): Promise { + let dir = startDir; + for (let i = 0; i < 10; i++) { + try { + const pkgPath = path.join(dir, "package.json"); + await fs.stat(pkgPath); + // Verify it's the clawdbot package + const pkg = JSON.parse(await fs.readFile(pkgPath, "utf8")); + if (pkg.name === "clawdbot") return dir; + } catch { + // not found, go up + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + async function resolveA2uiRoot(): Promise { const here = path.dirname(fileURLToPath(import.meta.url)); const candidates = [ @@ -26,6 +51,12 @@ async function resolveA2uiRoot(): Promise { if (process.execPath) { candidates.unshift(path.resolve(path.dirname(process.execPath), "a2ui")); } + // Find repo root by walking up from `here` (handles vitest/vite transforms). + const repoRoot = await findRepoRoot(here); + if (repoRoot) { + candidates.push(path.resolve(repoRoot, "src/canvas-host/a2ui")); + candidates.push(path.resolve(repoRoot, "dist/canvas-host/a2ui")); + } for (const dir of candidates) { try { diff --git a/src/canvas-host/server.test.ts b/src/canvas-host/server.test.ts index f51e2f5e0..0bd6bbffc 100644 --- a/src/canvas-host/server.test.ts +++ b/src/canvas-host/server.test.ts @@ -3,14 +3,22 @@ import { createServer } from "node:http"; import type { AddressInfo } from "node:net"; import os from "node:os"; import path from "node:path"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { WebSocket } from "ws"; import { rawDataToString } from "../infra/ws.js"; import { defaultRuntime } from "../runtime.js"; -import { CANVAS_HOST_PATH, CANVAS_WS_PATH, injectCanvasLiveReload } from "./a2ui.js"; +import { + CANVAS_HOST_PATH, + CANVAS_WS_PATH, + injectCanvasLiveReload, + resetA2uiCache, +} from "./a2ui.js"; import { createCanvasHostHandler, startCanvasHost } from "./server.js"; describe("canvas host", () => { + beforeEach(() => { + resetA2uiCache(); + }); it("injects live reload script", () => { const out = injectCanvasLiveReload("Hello"); expect(out).toContain(CANVAS_WS_PATH);