Skip to content

fix(exec): ship the SDK with the CLI instead of fetching it at run time - #601

Open
davidsu wants to merge 2 commits into
mainfrom
fix/exec-vendor-sdk
Open

fix(exec): ship the SDK with the CLI instead of fetching it at run time#601
davidsu wants to merge 2 commits into
mainfrom
fix/exec-vendor-sdk

Conversation

@davidsu

@davidsu davidsu commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The bug

base44 exec spawns a Deno wrapper whose first job is import { createClient } from "npm:@base44/sdk" — a live registry fetch on every invocation. That breaks wherever the npm registry is proxied behind a TLS-intercepting gateway (the wix-embargo setup): the gateway serves its CA certificate as the server certificate, and Deno's TLS stack rejects a CA cert used as a server identity before consulting any trust store:

error: Failed loading https://registry.npmjs.org/@base44%2fsdk for package "@base44/sdk"
  invalid peer certificate: Other(OtherError(CaUsedAsEndEntity))

No client-side configuration can fix that — DENO_CERT, DENO_TLS_CA_STORE, NODE_EXTRA_CA_CERTS, keychain trust and --cert were all verified failing, because the rejection is about the certificate's role, not its trust. npm tolerates the same certificate, which is why only Deno breaks.

Impact today: 14 of 17 exec.spec.ts tests red on any branch on an embargo machine, and the same in CI — see main's run 31506489004 (104× CaUsedAsEndEntity across all four matrix legs).

The fix

Stop fetching at run time. The SDK is bundled into the runtime assets at build time, and the specifier is redirected there through the import map the Deno runtimes already ship — the same mechanism function-manager.ts uses for base44:runtime.

Touchpoint Change
infra/vendor/base44-sdk.ts new 1-line bundle entry (export * from "@base44/sdk")
infra/build.ts bundles it to dist/assets/backend-runtime/vendor/base44-sdk.js
backend-runtime/import-map.json maps npm:@base44/sdk./vendor/base44-sdk.js
src/core/exec/run-script.ts passes --import-map to the Deno spawn (it passed none)
src/core/assets.ts exports getImportMapPath()

No new dependencies: @base44/sdk was already a devDependency (^0.8.23), and the bundle is produced by the existing Bun build. Import map entries resolve relative to the map, which is what makes this correct even though run-script.ts copies the wrapper to a temp file before running it.

Verification

The interesting failure mode only appears with a cold Deno cache and no .npmrc — a warm cache or a dev machine's ~/.npmrc (which points Deno at an internal mirror) hides it, which is exactly why this looked CI-only. So the mechanism was proved directly, replaying the CLI's own Deno invocation with the shipped artifacts:

  • with the shipped import map → wrapper runs, base44 global is live, zero network access
  • identical invocation without --import-map → still fails with CaUsedAsEndEntity (control, so the test can fail)

Suite results on an embargo machine: exec.spec.ts 17/17 (was 3/17), full suite 732/732, typecheck / lint / knip clean.

Decisions & trade-offs recorded

  1. SDK version is now pinned to the CLI release (0.8.23 per the lockfile) instead of resolving to whatever is latest at run time. More reproducible, but an SDK release now needs a CLI release to reach exec. Registry latest is 0.8.41 — bumping is a separate, deliberate call, not folded in here.
  2. The map is shared with the functions runtime, so functions that import npm:@base44/sdk get the vendored copy too. Deliberate: same benefit, one artifact.
  3. This covers our own import only. A user script that imports npm packages under base44 exec still needs a working registry path — the general fix remains gateway-side (serve a CA:FALSE leaf signed by the existing embargo CA), which is being pursued separately with the platform team.
  4. --unsafely-ignore-certificate-errors was rejected as a shipped default: it works and would also rescue user scripts, but it disables TLS verification for a host on every user's machine. If ever wanted, it should be an explicit opt-in env var.
  5. No sourcemap for the vendored bundle — third-party code nobody debugs through us; it would add ~1MB to the published package.
  6. ensureNpmAssets guard left alone. I first extended it to require the new vendor/ dir and a test correctly caught that this breaks its one-time-bootstrap contract (local edits must survive). A published version always ships identical assets, so a same-version dir can never legitimately lack the bundle — the guard would have been ghost code. Dev machines using bun link still need rm -rf ~/.base44/assets/<version> after changing assets, which is pre-existing and documented.

Background and full evidence: suss-tasks/exec_tests_broken_by_wix_embargo.md.

🤖 Generated with Claude Code

The exec wrapper imported npm:@base44/sdk, so every `base44 exec` needed
a live registry fetch from Deno. Wherever the npm registry is proxied
behind a TLS-intercepting gateway that serves a CA certificate as its
server certificate, Deno rejects the connection outright
(CaUsedAsEndEntity) and exec fails — as do all 17 exec tests.

Bundle the SDK into the runtime assets at build time and redirect the
specifier there via the import map the runtimes already ship, the same
way the functions runtime resolves base44:runtime. Import map entries
resolve relative to the map, so this holds even though the wrapper runs
from a temp directory.

Verified with a cold Deno cache and no .npmrc — the failing form: the
wrapper runs and the SDK loads with no network access, while the same
invocation without the import map still fails at the registry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.1.9-pr.601.c76ac1f

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.1.9-pr.601.c76ac1f"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.1.9-pr.601.c76ac1f"
  }
}

Preview published to npm registry — try new features instantly!

@davidsu

davidsu commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

CI status: all functional checks green — Test on all four matrix legs (ubuntu/windows × npm/binary), Lint, Typecheck, Knip, Build, Package Preview Publish, Wix Gateway Proxy.

The two red checks (Claude Code Review, Auto PR Description) are pre-existing and unrelated to this diff — they fail identically on every recent branch (feat/logs-follow-realtime-sse, fix/dev3-pin-github-actions, …). Root cause is the embargo cooldown rejecting the action's own dependency:

error: GET https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.227.tgz - 403

i.e. the package version is younger than the 14-day minimum-release-age the gateway enforces, so the Claude action can't install itself. That needs a pinned older claude-agent-sdk, an allowlist entry, or the embargo bypass those workflows had before — separate from this PR.

Notably, the exec suite passing in this CI run is itself the proof of the fix: the wix-gateway-proxy step is active in these jobs, so registry.npmjs.org is the intercepted host that previously produced 104× CaUsedAsEndEntity in exec.spec on main (run 31506489004).

@guyofeck
guyofeck self-requested a review August 13, 2026 07:26
The runtime import map points at the bundle, so a binary built from a
partial dist would ship an exec that only fails once a user runs it.
The assets archive already picks the file up; this just fails the build
early when it is missing, like the other required assets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@davidsu

davidsu commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Final CI state: every functional check green, all four test legs included (ubuntu/windows × npm/binary).

The test (windows-latest, npm) red on the previous attempt was dev.spec.ts timing out after 5000ms waiting for the dev server to boot — unrelated to this diff (it passed on the prior commit, whose only difference is a build-binaries prerequisite list), and green on re-run. Worth noting the contrast: that same leg fails on main on exec.spec.ts — the bug this PR fixes.

Remaining red: Claude Code Review / Auto PR Description, pre-existing and repo-wide (the embargo's 14-day minimum-release-age 403s the action's own @anthropic-ai/claude-agent-sdk dependency, so the action can't install itself). Detail in the comment above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant