Conversation
Contributor
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.1.18-pr.635.c3a4ce4Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.1.18-pr.635.c3a4ce4"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.1.18-pr.635.c3a4ce4"
}
}
Preview published to npm registry — try new features instantly! |
A `site` block had to spell out every command, and the platform that runs these projects kept its own copy of the same values to fill the gaps. Defaults live here now, matching what `base44 create` scaffolds: install `npm install`, build `npm run build`, output `./dist`. A block only names what its project does differently. `serveCommand` is deliberately left undefaulted: `base44 dev` reads its absence as "no frontend to run here" and runs the backend alone, and a default would spawn a dev server for every site block — one that fails immediately takes the backend down with it. Commands that exist only to serve a frontend default it themselves. `site` itself stays optional, so "is there a site?" still keys on the block: a backend-only project omits it and has no site. Inside a block the build fields always resolve, which moves two refusals from "this field is missing" to "there is no site block" — `base44 build` and `site deploy` reword their hints, and `deploy` reads the block rather than one field. Projects that declared a partial block change behaviour: they now build and deploy on the defaults instead of being refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
ronnyrin
force-pushed
the
feat/site-config-defaults
branch
from
September 22, 2026 09:34
6ab4533 to
93d2ceb
Compare
ronnyrin
force-pushed
the
feat/site-install-and-serve
branch
2 times, most recently
from
September 22, 2026 09:41
623cd2c to
075f1f7
Compare
`installCommand` and `serveCommand` were fields no command would run on their own: install only happened inside `create`, `scaffold` and `eject`, and serve only inside `base44 dev`, which also boots a local backend on 4400 and points the frontend at it. A host that already has a backend and just needs this project installed and served had to compose npm itself. `base44 install` runs `installCommand` and nothing else — local only, so a machine that has never logged in can use it. `base44 site dev` runs `serveCommand` against a backend the caller names, and since serving is its whole job it falls back to `npm run dev` where the schema deliberately does not. `--host`/`--port` are appended through the project's new `devHostFlag` (`--host`, or Next's `--hostname`). Only an `npm run` invocation can take them — `--` is what forwards arguments, and a bare `vite` would read it as its own — so anything else passes through untouched and says so rather than silently serving an unreachable address. `base44 dev` and `dev --remote` are unchanged; the signal handling they had is now shared rather than copied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
ronnyrin
force-pushed
the
feat/site-install-and-serve
branch
from
September 22, 2026 09:43
075f1f7 to
de2f19d
Compare
Review found that defaulting `outputDirectory` turns a field whose absence
means "nothing built to upload" into one that always resolves, and two callers
act on that:
`deployAll` still keyed the upload on the field, so `base44 deploy` on a
`"site": {"serveCommand": ...}` project (the shape of this repo's own
with-serve-command fixture) would resolve `./dist` and either fail a
previously-green deploy after pushing every other resource, or publish whatever
happened to sit in `./dist` — a backend bundle, typically — as the app's site.
`eject`'s guard `installCommand && buildCommand` became dead, so `eject --yes`
would run a network install, a build and a deploy for any ejected app with a
site block, unprompted.
So `outputDirectory` joins `serveCommand` as deliberately undefaulted, which is
the same rule applied consistently: only the two commands whose absence means
nothing default. `hasResourcesToDeploy` needs no change and drops out of the
diff; eject now keys on `outputDirectory` like every other caller. Added the
deploy test that would have caught the first one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
…feat/site-install-and-serve # Conflicts: # packages/cli/src/core/project/schema.ts
Review found that defaulting `outputDirectory` turns a field whose absence
means "nothing built to upload" into one that always resolves, and two callers
act on that:
`deployAll` still keyed the upload on the field, so `base44 deploy` on a
`"site": {"serveCommand": ...}` project (the shape of this repo's own
with-serve-command fixture) would resolve `./dist` and either fail a
previously-green deploy after pushing every other resource, or publish whatever
happened to sit in `./dist` — a backend bundle, typically — as the app's site.
`eject`'s guard `installCommand && buildCommand` became dead, so `eject --yes`
would run a network install, a build and a deploy for any ejected app with a
site block, unprompted.
So `outputDirectory` joins `serveCommand` as deliberately undefaulted, which is
the same rule applied consistently: only the two commands whose absence means
nothing default. `hasResourcesToDeploy` needs no change and drops out of the
diff; eject now keys on `outputDirectory` like every other caller. Added the
deploy test that would have caught the first one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
ronnyrin
force-pushed
the
feat/site-config-defaults
branch
from
September 22, 2026 10:13
8d9279d to
9c57bb8
Compare
Review findings on this PR, plus the merge of #634's outputDirectory change. `--host`/`--port` were composed only onto an `npm run` invocation and otherwise warned and continued with exit 0. For this command's audience — a hosted sandbox — a warning in a stream nobody reads is not a signal: the dev server comes up on whatever address it likes and the CLI reports success. An address that cannot be delivered is now a refusal. `pnpm`, `yarn` and `bun run` join the forwarding shapes, since all three pass trailing arguments to a script and dropping the address for them was the common case. `withServeAddress` returns whether it dropped the address, so the caller cannot mistake "no address to append" for "the address went nowhere" — the difference being a preview that never loads. That removes the second evaluation of the same regex at the call site, and the predicate it needed. The script token was `\S+`, which matches `dev;evil` and `$(id)`: the predicate answered "this forwards arguments" for a line whose second command is what received them. It is a charset now. The composition also used the untrimmed string while the test used the trimmed one. `--port` went through `Number()`, so an empty string became port 0 — a random port, silently — and `0x10`, `1e3` and `5173.0` were all accepted. It is parsed in a Commander argParser now, digits only and in range. `devHostFlag` does not default in the schema, for the same reason `serveCommand` and `outputDirectory` do not: `site dev` is the only reader, and a default permanently erases the difference between a project that wrote `--host` and one that wrote nothing. `site dev` supplies the fallback. Also: knip was failing on an unused exported type, invisible because none of the five code-checking workflows run on a PR that targets another branch. Tests added for the unauthenticated path on both new commands, `--port` rejection, a failing install command and a custom `devHostFlag`; one assertion that could never fail is gone, and the fixture no longer claims the schema defaults `serveCommand`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
…feat/site-install-and-serve # Conflicts: # packages/cli/src/cli/commands/project/eject.ts # packages/cli/src/core/project/schema.ts # packages/cli/tests/core/project.spec.ts
Both commands default inside a `site` block, so install and build always work there now — the earlier `outputDirectory` check was guarding a config neither writer produces. `ensure_cli_configs` and `eject_service` both emit the full block including `outputDirectory: "./dist"`, and the eject ZIP comes from the latter, so the check could never be the condition that differed. Having a site block is the whole question: a backend-only project has nothing to build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
…feat/site-install-and-serve
…-serve # Conflicts: # packages/cli/src/core/project/schema.ts # packages/cli/tests/cli/build.spec.ts # packages/cli/tests/core/project.spec.ts
`site dev` needed `--host 0.0.0.0 --port 5173` from every caller, which meant the platform retyped its own constants into a command line — the duplication this command exists to remove. Both now default in the CLI, so a hosted sandbox runs `base44 site dev` with no arguments and no knowledge of the values. `0.0.0.0` is the default because the command exists for a sandbox, whose preview is reached from outside the container; a loopback default would make it unreachable. On a developer's own machine that also exposes the dev server to the LAN, which is what `site.devHost` is for. `base44 dev` is untouched — it binds nothing. `site.devHost` and `site.devPort` are optional, and resolution is flag, then config, then default, so a project can narrow the bind without losing the ability to override it per run. Neither defaults in the schema, for the same reason `serveCommand` and `outputDirectory` do not: the single command that reads them supplies the fallback, so a project that said nothing stays distinguishable from one that chose these values. `devPort` is range-checked at parse time, which the flag's own parser could not do for a config value. One behaviour change: a `serveCommand` that takes no forwarded arguments, a bare `vite`, is now refused on every `site dev` rather than only when an address was asked for. There is always an address now, and a sandbox that silently serves on a different port is the failure this refusal exists to prevent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
`devHost`, `devPort` and `devHostFlag` were defaulted in `site dev`, which split the site block across two files: two fields defaulting in the schema and three in a command, with nothing telling a reader which was which. They belong in the schema, because the rule that keeps `serveCommand` and `outputDirectory` out of it does not apply to them. That rule is about absence carrying meaning — `base44 dev` reads a missing `serveCommand` as "no frontend to run here", and `deploy` reads a missing `outputDirectory` as "nothing built to upload", so defaulting either erases a question a reader asks. Nothing asks that of these three. `site dev` is their only reader and it wants a value. So the block is now self-describing: every convention with a default has it in one place, and the two fields without one are the two that mean something by being absent. `withServeAddress` loses its internal host-flag fallback, since the caller can no longer arrive without one, and the test for that case goes with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
… config Two of the three fields should not have existed. Nothing outside `site dev` and the fixture added alongside them ever read `devHost` or `devPort`; they were added to answer an objection about run-scoped values landing in a committed file, and that objection was withdrawn once it turned out the sandbox uses one fixed address. The fields outlived the argument for them. An address is not a fact about a project. Nobody writing a repo has a view on which port one particular run should listen on, so the convention belongs to the command that has to pick one, with `--host`/`--port` for a caller that needs something else. `devHostFlag` stays, and stays in the schema, because it is the opposite case: only the project knows whether its dev server wants `--host` or `--hostname`, and the caller that needs it passes no arguments at all, so config is the only channel. That leaves the site block with one field per question it can actually answer. Net 41 lines removed, including a fixture that only existed to exercise the fields it shipped with. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
`--host` and `--port` are gone. The address now comes from `site.devHost` and `site.devPort`, which default in the schema to `0.0.0.0` and `5173`, so `base44 site dev` takes no arguments for it and a caller needs to know none of the values. Changing where it binds means editing base44/config.jsonc. That removes the last thing the platform was composing. It also removes the flag's own port parser: Zod range-checks `devPort` at parse time, which the parser could not do for a config value, so validation now covers both the file and the one place the value is read. `withServeAddress` takes host and port as required, since a caller can no longer arrive without them, which deletes its no-address branch and the test for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
Left over from the last edit to this spec; caught by lint, not by the suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
The option pointed the frontend at a named backend via VITE_BASE44_APP_BASE_URL. Nothing ever passed it: the sandbox runs `base44 site dev` bare and relies on the frontend reaching its backend same-origin through the vite plugin's /api proxy, and `base44 dev` and `dev --remote` set the same variable internally without going through it. It was the last option on a command meant to take none. The serve runner keeps its optional `appBaseUrl`, since both `dev` paths still use it, so bringing the option back for a real caller is a few lines. The two same-origin and named-backend tests collapse into one, and the CHANGELOG entry drops both this and the `--host`/`--port` flags removed earlier, which it still described. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9vtxAUJV6aZ7FYv5cRjL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Description
Adds the two commands a hosted build sandbox needs to bring a project up without a developer at the keyboard:
base44 install(run the site'sinstallCommandand nothing else) andbase44 site dev(run the site's dev server with no local backend, the frontend reaching its backend same-origin).base44 buildis also relaxed torequireAuth: false, since it resolves the app id locally and runs a local build. All three are local-only paths, so a machine that has never logged in can install, build and serve a checkout.site devtakes no arguments at all: the bind address and the dev server's spelling of its bind-address flag come from newsite.devHost/site.devPort/site.devHostFlagconfig keys, which default to0.0.0.0,5173and--host. AserveCommandthat cannot receive forwarded arguments is refused rather than silently served on a different address.Related Issue
None
Type of Change
Changes Made
base44 install(src/cli/commands/project/install.ts): runs the site block'sinstallCommandviarunTask, registered inprogram.ts.requireAuth: falseandrequireAppContext: false— no app to resolve, no API call. Fails with an actionable hint when the project has nositeblock.base44 site dev(src/cli/commands/site/dev.ts): reads the project config, falls back tonpm run devwhen the site block names noserveCommand, appends the configured bind address, and runs it through the existingServeCommandRunner.requireAuth: false; exits with the child's code and tears down on SIGINT/SIGTERM.withServeAddress()core helper (src/core/site/serve-command.ts): appends<hostFlag> <host> --port <port>tonpm run <script>(through--) and topnpm/yarn/bun runscripts (directly), plusnpm --prefix <path> run <script>. Script and prefix tokens are matched as narrow charsets rather than\S+, so shapes likenpm run dev;evilornpm run $(id)are not treated as forwarding shapes. ReturnsdroppedAddressalongside the command so "no address to append" cannot be confused with "the address went nowhere";site devturns a dropped address into a hard failure instead of a warning plus exit 0.src/core/project/schema.ts):site.devHostFlag(default--host;--hostnamefor Next, which exits on--host),site.devHost(default0.0.0.0) andsite.devPort(default5173, validated 1–65535).serveCommandandoutputDirectorystill deliberately do not default — their absence is the signalbase44 devanddeployread.base44 buildno longer requires auth (src/cli/commands/project/build.ts):requireAuth: false, since the app id comes from--app-id,BASE44_APP_IDor.app.jsoncand the build is local.appBaseUrlis now optional onServeCommandRunnerOptions: when omitted,VITE_BASE44_APP_BASE_URLis not injected, so a same-origin frontend (the Base44 vite plugin proxies/api) is not pointed across origins.base44 devstill passes it.stopRunnerOnProcessSignals()out ofcommands/dev.tsintosrc/cli/dev/stop-runner-on-signals.tsso bothdevandsite devshare it.Testing
npm test)New/updated test coverage:
tests/core/serve-command.spec.ts— cases overwithServeAddress: npm via--, pnpm/yarn/bun direct, custom host flag,--prefixscripts, trimming, the fallback command, plus table-driven cases for non-forwarding shapes (vite,next dev, chained commands, already-pinned ports) and for injection-shaped strings.tests/cli/site_dev.spec.ts— default sandbox address, config-supplied address, rejection of--port, refusal of a non-forwardingserveCommand, serving without a login, the--hostnamespelling, same-origin env (URL=undefined), and the no-site-block failure. Driven byrunLiveagainstserve.jsfixtures that echo their argv and env.tests/cli/install.spec.ts— configuredinstallCommandruns, works without a login, surfaces a failing command, fails without a site block.tests/cli/build.spec.ts— builds without a logged-in user.tests/core/project.spec.ts— the three new schema defaults.with-dev-address,with-hostname-serve-command,with-npm-serve-command,with-installable-site,with-failing-install.Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
Division of labour between the three dev paths:
base44 devis the developer machine (local backend),base44 dev --remoteis the developer machine against the published backend, andbase44 site devis the hosted-sandbox path (frontend only, same-origin backend).base44 devis unaffected by the new config keys — it binds nothing.The commit history walks through several designs for where the bind address lives (command flags, then
--backend-url, then config); the final shape is config-only, sobase44 site devneeds no arguments and no caller has to know the values.docs/topic guides were not touched — the new commands follow the existing factory /Base44Commandpattern rather than changing it — but the newsite.dev*config keys may be worth a mention in the project-structure docs.🤖 Generated by Claude | 2026-09-23 13:04 UTC | c3a4ce4