Conversation
ayal
force-pushed
the
terminal-t1
branch
3 times, most recently
from
September 17, 2026 10:41
95ff29c to
2678499
Compare
Contributor
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.1.15-pr.630.04a5262Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.1.15-pr.630.04a5262"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.1.15-pr.630.04a5262"
}
}
Preview published to npm registry — try new features instantly! |
…er + base44 code Non-interactive atoms under `base44 builder`: new (template app, over an existing GitHub repository, or through the Wix route with a signed instance), send, status, stop, model. `base44 sandbox preview` prints the preview URL. Every command supports --json; new and send also --stream-json (one JSON line per stream event, then a result) and --verbose. Interactive `base44 code`: an Ink session where the first prompt in an empty directory creates the app, a linked directory or --app-id opens an existing one. Live turn rendering with tool results shown Claude-Code style (edits as diffs, one-line successes, whole errors, Ctrl+O to unfold, a pulsing line for the running tool), a /model picker, and cards for whatever the agent parks on the user — approvals, clarifying questions, permissions, secrets with masked entry, browser steps — answered through the same route the editor uses. Shared core: create / send / stop / state / branch resolution / conversation polling with event diffing / pending-input parsing, used by both faces. Directory rule matches `base44 create`: an empty cwd is the project, else ./<name>; --path picks explicitly. `code` and `send` refuse code-first projects and Superagents. Credentials (secret values, the Wix signed instance) never reach telemetry: request bodies are not captured for those calls and credential-like option values are redacted from the crash context. Build: the standalone binaries compile through Bun.build so the plugin that stubs Ink's dev-only devtools import applies to them too. Lockfile gains only the Ink/React entries, in registry-default form. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…kinds A parked OAuth call (connector authorization, GitHub connect) is now handled the way the editor handles it: start the flow, give the user the link (also opened in the browser when there is one), poll the connection's status, and approve the tool call only once it is ACTIVE — approving first made the tool report "no fresh active connection was stored". Failures and timeouts offer a retry. New core module: apps/connections.ts (initiate, wait, GitHub status). Also: select_payment_provider renders as a choice and answers under the tool's own key; a parked tool this CLI cannot render (waiting_on.kind input/choice from an unknown tool) shows an "answer in the editor" card instead of a misleading yes/no; a paywall-ended turn says the workspace is out of credits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nswer When the builder parks a tool call, non-interactive callers now learn about it and can answer without a browser: - `send`, `new` and `status` return status "waiting" with `pending`: the same shape the session's cards use (kind, title, questions and options, secret names, permission keys, browser step). `--stream-json` waiting events carry it too. - `send` takes the answer instead of a message: --approve / --reject / --skip / --choose <label> (repeat per question, comma for multi-select) / --other <text> / --grant <keys> / --secret NAME=env:VAR|file:PATH|- / --input <json>; --id picks when several wait. Options and keys are validated against what was asked; secret values are never accepted as plain text. - policy for scripts: --auto-approve (approval-kind pauses, all permissions) and --skip-questions (the builder decides). Never secrets, browser steps or unknown forms — those always come back as waiting. - a message sent while a call is parked is refused with the pending list instead of a bare 4xx. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…OAuth step The card offered y/n and sent an empty approval, so the tool answered "No credentials were provided". Match the editor: a name, then Base44's credentials (b) or your own client id and secret (y), secret typed hidden. Non-interactive: --connector-name [--client-id --client-secret env:VAR]. Also stop re-announcing a "needs your input" line for a call already answered in this session — the backend keeps it waiting until the resumed turn persists, and the transcript showed the pause twice. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…arrow terminals request_oauth_authorization verifies the answer by the connection_id in extra_user_input — the web threads it through, and an empty approval reads as "no fresh active connection was stored" even after a successful OAuth. The card now remembers the id the initiate call returned and approves with it. The footer (chip · dir · editor · preview) was one truncate-end row, so a narrow terminal cut the editor and preview links off. It wraps onto as many rows as it needs and the layout accounts for them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
chat/message and submit-tool-call-input stay open for the whole turn, and the edge or a proxy can cut the socket minutes after the backend took the request — surfacing as a bare "fetch failed" / ECONNRESET with no status. The session reported that as "answer failed" while the resumed turn was visibly streaming. One classifier now covers 5xx, timeouts and transport drops for both submit and answer; only a real API rejection is reported. The non-interactive send recovers the same way: on a drop it waits for the turn to settle from the conversation and returns that outcome. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
What
Build Base44 apps from the terminal. This adds five non-interactive
base44 buildercommands (create, send a turn, status, stop, pick the model),base44 sandbox preview, and an interactivebase44 codesession, all built on one shared resource layer. Everything hits existing backend endpoints — no backend changes.What it looks like
base44 codein an empty directory, before the first prompt — the rendered mark, the account, the directory, the kind of app:The same session a few minutes into the first build — tool results as they land, the agent's current thought, the tool in flight, and the footer chips:
Layout
program.tsregistersbuilderandcode; the existingsandboxgroup gainspreview.base44-client.tsadds one request header,X-Builder-Model-Selection: user-v1, so the backend honors the saved model pick (the web editor's contract; with no pick it falls back to the app default).Command reference
Every command and flag this PR adds. Globals from the existing CLI apply where marked:
--json(machine output on stdout),--app-id <id>(alsoBASE44_APP_ID; otherwise the linked directory),--branch <name>where a command is branch-aware.base44 builder new [prompt]--import <repo>build over an existing GitHub repository ·--mode <direct|fork|copy>how to import (defaultdirect) ·--repo-name <name>name for the new repo when forking/copying ·--from-branch <name>import a specific branch ·--name <name>directory and app name (invented when omitted) ·--path <dir>directory to link (default: the current directory when empty, else./<name>) ·--wix-instance <token>create through the Wix route with this signed instance (-reads it from stdin; alsoBASE44_WIX_INSTANCE) ·--wix-client-id <id>the companion OAuth app's client id ·--auto-approve/--skip-questionspolicy for the first build ·--verboseevery tool result unfolded ·--stream-jsonone JSON line per stream event, thenresult--jsonbase44 builder send [message]{"queued":true}when the agent is busy · or answer what the agent asked:--approve·--reject·--skip·--choose <label>(repeat per question, comma-separate for multi-select) ·--other <text>·--grant <keys>·--secret NAME=env:VAR|file:PATH|-(never plain text) ·--connector-name <name>[--client-id,--client-secret <source>] for a workspace connector ·--input <json>·--id <call>when several wait · policy:--auto-approve,--skip-questions·--verbose·--stream-jsonone JSON line per stream event (thinking,text,tool_start,tool_endwith fullargsandresult,waitingwith the request), thenresult--json·--app-id·--branchbase44 builder statuswaiting, with what the agent asked--json·--app-id·--branchbase44 builder stop--app-id·--branchbase44 builder model [model]Automatic/default/autoclears the pick)--jsonbase44 sandbox preview--json·--app-idbase44 code--import <repo>·--path <dir>·--wix-instance <token>[--wix-client-id <id>] ·--app-id <id>opens that app from anywhereInside
base44 code: Enter sends · Esc stops the running turn, or snaps back to live when scrolled · ↑/↓ · PgUp/PgDn scroll the transcript · Ctrl+O unfolds every tool result in the whole transcript, and folds them again · Tab reopens a question card you deferred · Ctrl+C clears the input, then exits · Ctrl+D exits ·/modelopens the picker,/model <name>switches directly.When the agent asks you something. The builder parks a tool call with
waiting_for_user_inputwhen it needs a decision — enable a connector, write or delete entity records, run a guarded shell command, answer clarifying questions, grant an app agent permissions, provide secrets, or finish an OAuth step. The session shows a card and answers throughPOST chat/submit-tool-call-input, the route the editor uses, then streams the turn the answer resumes. Approvals:y/n. Questions: an arrow picker, space for multi-select, a "something else" row for free text,sto skip them all. Permissions: checkbox rows. Workspace-connector registration: a name, then Base44's credentials or your own client id and secret. Secrets: masked entry field by field; values exist only until the answer is sent and never appear in the transcript, a log, telemetry or--stream-json. Browser steps (GitHub, OAuth, payments): finish in the editor, theny. Esc defers a card; Tab brings it back. OAuth steps (connector authorization, GitHub connect) run from the card the way the editor runs them in a popup: the CLI starts the flow, prints and opens the link, polls the connection, and approves the tool call only once it is active. A parked tool this CLI cannot render points at the editor instead of offering a misleading yes/no.Non-interactive: the next
sendis the answer. Modelled onclaude -p, where a question comes back as the result and the next message answers it. When the builder parks a call,send,newandstatusreturnstatus: "waiting"withpending— the same shape the cards render from — and--stream-jsonwaitingevents carry it too. The caller answers withsend --approve/--reject/--choose/--grant/--secret NAME=env:VAR/--skip/--input, validated against what was asked, and the resumed turn streams like any message. A message sent while a call is parked is refused with the pending list rather than a bare 4xx. Two policy flags for scripts, scoped to the kinds where a fixed policy makes sense:--auto-approve(approval-kind pauses, all requested permissions) and--skip-questions(the builder decides). Secrets, browser steps and unknown forms never have a policy: they always come back as waiting.What a tool shows. While it runs, the tool sits in place in the transcript with a pulsing dot, its title, elapsed time and its command or path beneath; the finished line replaces it. An edit renders as the change itself, removed lines then inserted lines, folded past 12; a write shows
+N lines; reads and reloads stay quiet on success; any other successful result shows one line with an… +N linesmarker; errors are never cut mid-sentence, whole lines folded past 8. The stream carries every tool's full result and parsed arguments, so the fold is a rendering choice, not a data loss —--verbose(or Ctrl+O) shows all of it, and--stream-jsonhands it over as data.builder sendandcoderefuse a code-first project (base44 create) and a Superagent;--mode,--repo-nameand--from-branchare refused without--import;--importand--pathare refused inside a linked directory.Non-interactive —
base44 builderWhere the app lands (same rule as
base44 create): an empty current directory is the project itself and names the app; otherwise./<name>/is created.--path <dir>picks the directory explicitly.builder newand a genesisbase44 codesession share this, and both end by printing the directory and thecd … && base44 codeline to pick the app up again. The block also says the app's files live remotely (base44 sandbox lsto look,base44 ejectfor a copy) — a linked directory is a handle, likebase44 link, not a checkout.Wix launch. A Wix-launched app is born through
POST /api/wix/create-app, which verifies the signed Wix instance and connects the Wix connector before the first turn — the one thingPOST /api/appscannot do.--wix-instance <token>takes that signed instance and--wix-client-id <id>the optional companion OAuth app; the prompt argument is what the agent builds. In the browser the same two values travel in the launch URL's fragment; the CLI takes them as flags, so the funnel prints them and nothing parses URLs. The token is a credential, so-reads it from stdin andBASE44_WIX_INSTANCEis honoured, and neither the request body nor the option value reaches error telemetry. Everything after creation is the shared path: link, stream,--json/--stream-json, plusclient_creation_idin the result. The route checks the instance against the backend's own Wix app secret, so the target environment must hold the credentials of the Wix app that minted it.Each command is a
Base44Command, so it gets the existing plumbing for free:--app-id <id>/ linked-dir resolution,--branch, and the global--json.Only builder-managed apps.
builder send(andcode) first read the app and refuse two kinds with a one-line message: a code-first project (base44 create,is_managed_source_code: false, where you own the source and a turn would edit a copy nobody sees) and a Superagent (which has no builder conversation).builder new— create an app and start its first build. A prompt makes a template app;--import <repo>builds over an existing GitHub repo.Without
--jsonit shows a live spinner while the sandbox provisions, then streams the agent's tool calls, printsrepo/editor/linked ./<dir>, then the preview URL. Flags:--import <repo>,--mode direct|fork|copy,--name,--repo-name,--from-branch. Inputs are validated before any API call.builder send— one agent turn; blocks until it finishes and streams it.builder status·sandbox preview·builder stopbuilder model— pick the builder model for your turns, account-wide. No argument lists the catalog and the current pick;defaultclears it.Branch-aware by design
A request with no branch targets
main— right for a template app. An app built over a repo works on its setup branch, sosend/status/stopresolve it and stamp it, or they'd silently hit the wrong line:sequenceDiagram participant CLI as base44 builder send participant BE as backend (existing endpoints) CLI->>BE: GET /api/apps/{id}/branches BE-->>CLI: [setup branch] — or [] for a template app CLI->>BE: POST chat/message?branch_id=… (none → main) BE-->>CLI: turn outcomeInteractive —
base44 codeA full-screen Ink session, composed from the same helper the atoms use — there is one create path, not two. The header mark is rendered, not hand-drawn: a supersampled, aspect-corrected disc in sub-cell block glyphs with a thin slot, octant glyphs on terminals that draw them and quadrant blocks elsewhere (
code/logo.ts, pinned by golden tests).In a linked directory it reopens that app's session;
base44 code --app-id <id>opens any app from anywhere, andbase44 linkattaches a directory to an existing app (the help text and the idle hint name both). The footer chip names the kind of app from its state —web app,game,mobile app,slides— or shows the repository for an app built over one; "import" is never used as a label. In-session: Enter sends, Esc stops the running turn (server-side), arrows/PgUp/PgDn scroll, Ctrl+C/D exit. The mode shows in the header line and a footer chip./modelopens an arrow-navigable model picker (/model <name>switches directly); the current pick shows in the footer.Dependencies added — flagged for review
For the Ink session:
ink,ink-text-input,react(+@types/react);"jsx": "react-jsx"intsconfig.json; a small bun plugin ininfra/build.tsstubbing Ink's dev-onlyreact-devtoolsimport so the bundle builds;knip.jsonscans.tsx.bun.lockupdated.Lockfile — flagged for review.
bun.lockgains only the entries for these four packages and their tree, recorded in registry-default form (""URL) like every existing entry; bun also re-nests the olderstring-width/wrap-ansifamily undercliui,yargs,@inquirer/coreandmsw. Versions and sha512 integrity are unchanged.Build.
infra/bundle.tsowns the runtime externals list and a bundler plugin that stubs Ink's dev-onlyreact-devtools-coreimport. Standalone binaries now compile throughBun.build({ compile })instead of spawningbun build --compile, so the same stub applies to them (marking the package external is not enough: the binary hoists the import and exits at startup).Tests
tests/cli/builder.spec.ts(16): template create → settled turn → preview; create over a repository; empty-directory and--pathlinking; input validation;sendbranch-scoping + queued;sendrefuses a code-first project and a Superagent;status,sandbox preview,stop;modellist, set, anddefault;coderefuses without a TTY.tests/core/stream.spec.ts(18): the poll-and-diff stream.bunbuild OK.Caveat — the agent's preview-verification tools need the editor open
Not specific to either flow. The agent's screenshot and execute-in-preview tools are answered by the running preview inside the web editor; with no editor tab open they report the preview as unavailable, for both template apps and apps built over a repository. The agent still builds fine from the terminal; only its "look at the running app" step depends on the editor. Building over a repository and picking a model are each enabled per account; the CLI explains the refusal when an account lacks one.
Next steps — not part of this PR
Listed so the line is explicit; none of this ships here.
T2 — CLI
builder newand thecodefirst prompt (honour the verdict by default,--type web|game|slidesto pin) and passing the type on create.--clonefor apps built over a repository: clone into the linked directory on the setup branch, so git is the pull/push pair. Thenbuilder commit/builder pr(refused on template apps).agents chat <name> "<msg>"andagents conversationsunder the existingagentsgroup — the runtime endpoint answers synchronously and accepts the platform token. A/agentslash incodedecided after.superagent chat | conversations— same shape, separate product; needs its owner and a confirmation story for side effects.builder open <id>or a picker incodereusinglink's app list; show recent history on attach; handle several active branches instead of falling back tomain.claude -pstyle: when stdout is not a TTY, the reply goes to stdout and progress to stderr (today settled tool lines go to stdout and the reply is not printed); exit codes reflecting the final status (a distinct one forwaiting) and--timeoutonbuilder newandbuilder send./api/auth/me(feature_flags) at the start ofbuilder new,builder sendandcode, failing with one line that names it — the same list the web client uses; one new server-side flag.ink-text-input(TODO insession.tsx).T3 — CLI
sandboxfile commands, with checkpoints — the prerequisite for anycreate --promptthat brings files onto disk.Backend
github_reauth_requirederror carrying the reconnect URL (replacing the CLI's string match).🤖 Generated with Claude Code