Skip to content

feat(cli): remote-dev commands — sandbox + projectless connectors - #547

Merged
carmelc merged 11 commits into
mainfrom
feat/sandbox-remote-dev-commands
Jun 28, 2026
Merged

feat(cli): remote-dev commands — sandbox + projectless connectors#547
carmelc merged 11 commits into
mainfrom
feat/sandbox-remote-dev-commands

Conversation

@carmelc

@carmelc carmelc commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Note

Description

Adds a base44 sandbox command group for developing an app remotely through its server-side sandbox (list, read, write, edit, grep, run, and checkpoint), plus a projectless connectors initiate command that starts a connector's OAuth flow against any --app-id without a local project. It also introduces a global --json flag that turns stdout into a single machine-readable JSON document (with silent mode and a JSON error envelope) so the CLI can be driven by scripts and agents. To support the sandbox, the device-login flow now requests the sandbox:write scope and surfaces a re-login hint when that scope is missing.

Related Issue

None

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe):

Changes Made

  • sandbox command group — new remote-development commands: sandbox ls, read, write, edit, grep, run, and checkpoint, backed by a new core/resources/sandbox API + Zod schemas and a getSandboxClient(appId) HTTP client scoped to /sandbox-bridge/. Operates on a remote app via --app-id with no local project required.
  • connectors initiate command — initializes a connector and starts its OAuth flow projectlessly; surfaces the authorize URL and polls until authorized (interactive) or prints the link (non-interactive/--json).
  • Global --json flag — exposed as ctx.jsonMode; forces non-interactive wiring, routes status/logs to stderr, and emits a single JSON document on stdout. Base44Command provides a generic { "output": "<status>" } fallback and a JSON error envelope ({ error, code, details, hints }) so every command stays parseable.
  • Auth scope — device login now requests sandbox:write; sandbox API errors add a "run base44 login again" hint only on a genuine missing-scope 401/403.
  • readStdin — added an options.trim flag to preserve file bodies/trailing newlines verbatim (used by sandbox write/edit).
  • Docs — updated README.md (command table, Global flags, --json section) and docs/AGENTS.md (rules for --json and silent mode).
  • Test fixtures — normalized function fixtures from index.ts to entry.ts.

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated docs/ (AGENTS.md) if I made architectural changes

Additional Notes

New tests cover the sandbox commands (tests/cli/sandbox.spec.ts) and connectors initiate (tests/cli/connectors_initiate.spec.ts); existing connector/function/logs/whoami specs were updated for the --json lifecycle and the entry.ts fixture rename.


🤖 Generated by Claude | 2026-06-25 09:08 UTC | db4783d

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.0.56-pr.547.db4783d

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.0.56-pr.547.db4783d"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.0.56-pr.547.db4783d"
  }
}

Preview published to npm registry — try new features instantly!

@carmelc carmelc changed the title feat(cli): add sandbox remote-dev commands feat(cli): remote-dev commands — sandbox + projectless connectors Jun 17, 2026
Rebased onto main and aligned with the projectless app-id mechanism added
in #541: --app-id is a global program option (bound to BASE44_APP_ID), and
requireAppContext commands resolve it through the lifecycle
(ensureAppContext -> initAppContext), so commands no longer need a bespoke
resolver. Dropped the parallel cli/utils/app-id.ts and the per-command
--app-id options from the earlier iteration.

Sandbox (new `base44 sandbox <tool>`, wraps apper #11608 sandbox-bridge):
- getSandboxClient(appId) + core/resources/sandbox {schema,api}
- list-directory/read-file/write-file/edit-file/grep/run-command/release,
  JSON-first stdout; write/edit payloads via flag or stdin (verbatim)
- commands use the default app-context lifecycle and read getAppContext().id

Connectors:
- new `connectors initiate --integration-type <type> [--scopes <s...>]`:
  initializes the connector and starts its OAuth flow; prints the URL
  (agent/non-interactive friendly), opens browser + polls when interactive
- pull/push work projectless: when the app context has no projectRoot
  (explicit --app-id/BASE44_APP_ID) they use ./connectors (--dir override);
  otherwise the linked project's connectors dir
- list-available already works projectless via the lifecycle (unchanged)

Tests: sandbox.spec.ts (9), connectors_initiate.spec.ts (6), projectless
cases for connectors pull/list-available. README updated. typecheck/lint/
knip clean. Pre-existing dev/exec spec failures are environment-dependent
(deno/network) and reproduce on clean main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@carmelc
carmelc force-pushed the feat/sandbox-remote-dev-commands branch from accfe99 to 18a591d Compare June 17, 2026 14:47
Align the backend-function test fixtures and specs with the canonical
entry.ts convention (matching base44/skills#121): rename the config-based
fixtures' index.ts entry files to entry.ts, update their function.jsonc
"entry" fields, and update all specs that referenced index.ts as a function
entry/path (project, function-config, function-schema, functions deploy/
list/pull, logs).

The CLI's own docs (docs/resources.md) and source already use entry.ts; this
removes the stale index.ts examples from fixtures. Config-based discovery
still supports an arbitrary entry filename — when we document/support that
explicitly we'll add a dedicated index.ts example + test back.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
carmelc and others added 9 commits June 18, 2026 10:15
The sandbox-bridge mutating tools (write/edit/run-command) require the
sandbox:write OAuth scope, which is deliberately kept out of the backend's
DEFAULT_SCOPE so shell access is never granted silently. Request it
explicitly in the device-code flow alongside apps:read/apps:write so
`base44 sandbox ...` works after login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A token issued before sandbox support (or without the sandbox:write scope)
gets a 403 from the sandbox-bridge endpoints. Augment forbidden responses
from sandbox commands with a hint to run 'base44 login' again to grant
sandbox access, while preserving the server's original error/hints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous blanket 403 hint was wrong: the HTTP sandbox-bridge endpoints
the CLI calls authorize via the platform JWT + app-admin and do NOT enforce
sandbox:write (that check is MCP-only). Their 403s — feature-flag-off,
blocked app, view-only, wrong app type — are generic and not fixed by
re-login.

Detect the scope rejection specifically by matching the response wording
("sandbox:write" / "granting sandbox access", as emitted by the scope check)
instead of any 403, so the re-login hint only appears when it would actually
help. Added a test asserting a generic 403 (view-only) does NOT get the hint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `--json` as a global program option (like --app-id) instead of a
per-command flag. The lifecycle centralizes the behavior:

- index.ts forces non-interactive wiring when --json is present, so the
  clack spinner and logs go to stderr.
- CLIContext gains `jsonMode`; Base44Command skips the clack framing and
  routes the status line to stderr, keeping stdout a pure JSON document.

Any command that sets RunCommandResult.stdout now yields clean JSON under
--json. Wired structured JSON output into the connectors commands
(list-available, initiate, pull, push); the sandbox commands already emit
JSON and now produce it pure. Pre-existing `logs --json` benefits too.

Tests: pure-JSON stdout assertions for sandbox + connectors list-available
+ initiate. typecheck/lint/knip clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make --json a universal contract in Base44Command so every command emits
valid JSON on stdout, even ones that don't build a machine payload:

- success: a command that sets result.stdout gets it verbatim (native JSON,
  status to stderr); otherwise the human status line is wrapped as
  { "output": "<text>" } (ANSI stripped).
- failure: any command emits a JSON error envelope on stdout —
  { "error", "code"?, "details"?, "hints"? } from the CLIError fields.

So `base44 whoami --json` -> {"output":"Logged in as: ..."} and a failing
`base44 sandbox list-directory --json` (no app id) -> {"error":"No Base44
app ID found.","code":"...","hints":[...]} instead of empty stdout.

Native/structured output (sandbox, connectors) is kept — it's richer than
the wrapped-status fallback (e.g. initiate still exposes redirectUrl /
connectionId as fields).

Tests: whoami --json (fallback wrap) and sandbox --json error envelope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- README: add a "Global flags" section covering --app-id and --json, with
  the contract (pure JSON stdout, silent mode, JSON error envelope,
  universal) and sandbox/jq examples.
- AGENTS.md/CLAUDE.md: note that --json forces silent mode in the
  @clack/prompts rule, and add a rule so new commands keep stdout
  machine-parseable (return RunCommandResult.stdout, status via
  outroMessage/log, emit JSON when ctx.jsonMode).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sandbox namespace already supplies the file/shell context, so the
redundant nouns on each subcommand can be dropped to match the CLI's
one-word-where-possible naming convention:

  list-directory -> ls
  read-file      -> read
  write-file     -> write
  edit-file      -> edit
  run-command    -> run

grep and release are already concise and unchanged. The scope itself
stays `sandbox` to keep the CLI aligned with the platform vocabulary
(the sandbox:write OAuth scope, backend, and error hints).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ew3Z6LjEgToj2bZyQHMQb
The sandbox release command is no longer needed. Removes the command,
its API client function, response schema, test, and README entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5MJy3X1QEiXo2NFTD5Hpx
Adds `base44 sandbox checkpoint` to create a restore-point checkpoint of
an app's remote sandbox via the create_checkpoint sandbox-bridge tool.
Takes an optional --name (message/title; defaults to an auto-generated
title) and returns the checkpoint id, name, and git commit hash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H5MJy3X1QEiXo2NFTD5Hpx
@carmelc
carmelc force-pushed the feat/sandbox-remote-dev-commands branch from 06f1f26 to d08c0ad Compare June 25, 2026 09:07
carmelc added a commit to base44/skills that referenced this pull request Jun 25, 2026
Add the `base44 sandbox checkpoint` CLI command (base44/cli#547) and the
`create_checkpoint` MCP tool (base44-dev/apper#11608) to the base44-remote-dev
and base44-sandbox skills, including the sandbox:write scope, the CLI/MCP name
mapping, and the COMMIT_FLUSH_PENDING error code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@carmelc
carmelc merged commit e0dd34d into main Jun 28, 2026
14 checks passed
@carmelc
carmelc deleted the feat/sandbox-remote-dev-commands branch June 28, 2026 11:24
ImriKochWix added a commit that referenced this pull request Jun 28, 2026
#547 introduced a global --json (ctx.jsonMode) with silent mode, but the
logs command still declared its own local --json and read options.json.
The global captured --json, leaving options.json undefined, so `logs
--json` emitted text instead of JSON (the test fails on main too). Drop
the local option and read ctx.jsonMode, matching the sandbox/connectors
commands and CLAUDE.md rule 10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ImriKochWix added a commit that referenced this pull request Jun 29, 2026
* feat(logs): add --env preview|prod flag

Routes the logs request to preview (draft) or prod (published) via the
backend's ?env= param. Defaults to preview; --env prod shows a
published-app hint when empty. --env all is handled client-side by the
caller (two requests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(logs): honor the global --json flag instead of a local option

#547 introduced a global --json (ctx.jsonMode) with silent mode, but the
logs command still declared its own local --json and read options.json.
The global captured --json, leaving options.json undefined, so `logs
--json` emitted text instead of JSON (the test fails on main too). Drop
the local option and read ctx.jsonMode, matching the sandbox/connectors
commands and CLAUDE.md rule 10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(logs): use LogEnvSchema zod enum for the env type

Replace the repeated "preview" | "prod" literal union with a single
LogEnvSchema (z.enum) + LogEnv type in the function schema, mirroring
LogLevelSchema. The --env choices derive from LogEnvSchema.options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
carmelc added a commit to base44/skills that referenced this pull request Jun 30, 2026
* feat(base44-sandbox): add cloud-sandbox remote-dev skill flavor

Add a focused, separately-installable "sandbox flavor" for developing a
Base44 app inside Base44's cloud sandbox (PR base44-dev/apper#11608), where
the implementation is remote and writing a backend-function file is what
ships it — no local checkout and no Base44 CLI.

- skills/base44-sandbox: new hand-authored skill. Backend functions and
  frontend code are supported; entities, connectors, and agents are marked
  not-supported-yet; no automations. Concise inline guidance with a pointer
  to base44-cli's functions-create reference (no duplicated copies).
- skills/base44-remote-dev: vendored connection skill (MCP / HTTP bridge,
  tools, edit→preview→verify loop) from the upstream PR.
- New base44-sandbox plugin across channels: second entry in the Claude and
  Codex marketplaces + plugins/base44-sandbox/ bundle (its own .claude/.codex/
  .cursor manifests + skills/assets symlinks). Bundles base44-remote-dev,
  base44-sandbox, base44-sdk, base44-troubleshooter; excludes deploy-oriented
  base44-cli. npx skills --skill documented in the README.
- validate-template.mjs now discovers and validates all .cursor-plugin
  manifests (repo root + plugins/*/).
- sync-cli-skill flags base44-sandbox for manual review when function
  authoring conventions change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(base44-sandbox): support connectors via MCP tools and projectless CLI

Connectors are now usable in remote-dev (apper#11608 MCP connector tools +
cli#547 projectless connectors), so flip them from "not supported" to a
documented connect flow:

- list_connectors (apps:read) / initiate_connector_connection (apps:write)
  over MCP, and base44 connectors list-available/initiate/pull over the CLI
  with --app-id (no local project / config.jsonc).
- Document the declarative/replace scope semantics (read current scopes first,
  pass the complete set) and the human-consent authorization-URL step.
- Note the connector CLI is the one allowed CLI use in remote-dev; it targets
  a remote app by id with no deploy step (not a contradiction of the no-CLI
  rule, which is about local-project/deploy commands).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(base44-remote-dev): document the connector MCP tools

Upstream's base44-remote-dev SKILL.md doesn't yet list the connector tools
shipped in apper#11608, so add a "Connectors (OAuth integrations)" section
covering list_connectors (apps:read) and initiate_connector_connection
(apps:write) — declarative/replace scope semantics and the human-consent
redirect-URL step — plus the projectless base44 connectors CLI equivalent.

Marked as a local addition (HTML comment) to reconcile when upstream
documents these tools.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(base44-remote-dev): drop local-addition note from connectors section

The connectors section will align with upstream by merge time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): use entry.ts as the backend-function entry file

The function entry file (and the function.jsonc "entry" value) is entry.ts,
not index.ts. Update all function examples across base44-sandbox, base44-sdk,
and base44-cli docs (functions-create, automations, project-structure trees).

Invocation already uses base44.functions.invoke('name', data) everywhere; no
change needed there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(base44-sandbox): connector token usage, complete README, reference-first

- Add "Using a connected connector in code": fetch the OAuth token via
  base44.asServiceRole.connectors.getConnection(type) inside a backend
  function, with a link to base44-sdk's connectors reference.
- Add a "Reference order & the complete README" section: prefer this skill
  and its siblings over web search, and point to the public app-specific
  onboarding README endpoint.
- Surface the reference-first note near the top too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(base44-sdk): clarify functions.invoke() returns the raw axios response

invoke() resolves to the raw axios response, so the function's JSON is on
.data (not the top-level object), and it throws on non-2xx with the error
body at err.response.data. Update the invoke method doc and every frontend
example to read res.data, and add the warning in SKILL.md + QUICK_REFERENCE.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(base44-sandbox): note invoke() returns raw axios response (.data)

Add a small frontend-calling clarification so agents read the function's
JSON off res.data and handle the non-2xx throw; link to base44-sdk for detail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(base44-sandbox): support entities and agents (filesystem-only)

Entities and agents are now authorable in the sandbox — writing the .jsonc
file auto-syncs, no entities/agents push or deploy. Flip both from
"not supported" to supported, broaden the mental model to "writing a resource
file is the deploy", and add Entities and Agents authoring sections (file
naming, schema, links to base44-cli references with their push/deploy
sections flagged as not applicable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(base44-sandbox): add base44-cli reference to the Agents section

Match the functions/entities pattern: link the Agents section to the
base44-cli Agent Schema and make clear to ignore agents push/pull/deploy
commands (sandbox auto-syncs the file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(sandbox): drop HTTP bridge surface, document base44 sandbox CLI names

Remove the HTTP REST sandbox-bridge option (POST /api/apps/<id>/sandbox-bridge/<tool>)
from base44-remote-dev and base44-sandbox; local agents use MCP or the
`base44 sandbox` CLI instead.

Clarify CLI naming throughout: add the MCP-tool -> CLI-command mapping
(list_directory->sandbox ls, read_file->sandbox read, write_file->sandbox write,
edit_file->sandbox edit, run_command->sandbox run; grep/release unchanged) and
annotate inline tool references with their CLI command names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01842h6eP9hLWHnoRrERmbLo

* docs(sandbox): drop the sandbox release command/tool

The release command is no longer supported, so remove it from the
base44 sandbox CLI mapping table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01842h6eP9hLWHnoRrERmbLo

* docs(sandbox): document checkpoint command and create_checkpoint tool

Add the `base44 sandbox checkpoint` CLI command (base44/cli#547) and the
`create_checkpoint` MCP tool (base44-dev/apper#11608) to the base44-remote-dev
and base44-sandbox skills, including the sandbox:write scope, the CLI/MCP name
mapping, and the COMMIT_FLUSH_PENDING error code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(sandbox): functions need only entry.ts, no function.jsonc

In the sandbox the function is inferred from the directory, so authors only
create entry.ts under base44/functions/<name>/; the function.jsonc config is
not required and is ignored in this mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(sandbox): note function.jsonc guidance in cli ref doesn't apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants