Skip to content

Import marketplace - #40

Merged
zfoong merged 11 commits into
mainfrom
import-marketplace
Oct 29, 2026
Merged

zfoong merged 11 commits into
mainfrom
import-marketplace

Conversation

@zfoong

@zfoong zfoong commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Allow user to import agent app from the marketplace. However, might need revamp we if stop relying on the github repo as the only source.

It will not work now because the living-ui-marketplace (agent-app-marketplace) repo is still using the older framework files.

zfoong added 11 commits October 29, 2026 11:12
One pipeline for every way an Agent App arrives: a package (the app's
framework files, never credentials, runtime state or data), a
marketplace catalogue delivered the way CraftBot already reads it (raw
catalogue.json, then the app's folder from the branch archive), and
zip, folder, GitHub and git sources. Install acquires into staging,
inspects, sanitizes, re-vendors from a locally installed toolkit,
customizes, places atomically, registers with a new identity and
provenance, installs dependencies, gates and serves. Marketplace apps
are catalogued (no walk-verify); every other source is untrusted.
inspect, export and --events complete the CLI contract; schemas for the
catalogue, the inspect report and manifest provenance.
The starter now targets PocketBase 0.39.7, the version CraftBot and the
marketplace apps run, instead of 0.26.x.

- The adapter needed no API rewrite: nothing it uses in the JSVM API
  changed between 0.26.6 and 0.39.7 (type definitions compared, flags
  and superuser/migrate commands checked on the real binary).
- Behaviour changes in that range are handled and documented:
  - API rules now run before record hooks (0.27), so an app's create
    rule must not depend on the owner field the guard fills;
  - raw $app.db() writes go to a single write connection (0.28),
    audited for deadlocks inside transactions.
- `node pb-serve.mjs --fetch` downloads the pinned release for this
  OS/arch, checks its SHA-256 against hashes embedded in the launcher
  (taken from the release's checksums.txt), extracts only the binary
  through a .partial file, confirms its version and renames it into
  place. It does nothing when 0.39.7 is already there, and refuses an
  OS/arch the release doesn't ship. pipeline.install runs it, so an
  installed app needs no manual download.
- A database created on 0.26.6 keeps its accounts, sessions, ownership
  and tasks under 0.39.7.

Rules self-test passes; the PocketBase e2e passes 104/104 (a new check
that install fetches nothing when the binary is present); the
multi-user suite passes 65/65; the starter suite is unchanged at 20/32.
Any HTTP app now runs unchanged behind the framework's sidecar adapter,
and agents operate it over A2App with the full 0.2 auth (accounts,
delegated credentials, the ceiling, device sign-in, task credentials),
inherited from adapter-core.

- @a2app/reverse-proxy is now runnable (`a2app-sidecar [<dir>]`). It
  reads sidecar.json, starts the upstream from an argv (never a shell)
  with PORT and every A2APP_* variable removed from its environment,
  waits for its health path, then serves A2App on the app's port under
  the network policy, with .agent-token as the admin credential,
  authMode from the manifest, the account page and a durable state
  store. Declared operations are forwarded (path params escaped, query
  or JSON body, nothing of the caller forwarded, per-call timeout,
  5 MiB response cap, clean error envelopes). The app's exit takes the
  sidecar down; stopping the sidecar stops the app.
- `a2app-sidecar --check` is the gate: every declared operation has a
  mapping and every mapping a declaration, path params declared and
  required, no GET with a body, a sound upstream declaration.
- blueprint-sidecar: framework files, sidecar.json, the account page,
  and how an agent adopts an app (source read-only in upstream/, start
  and health declared, the API mapped into modules and operations).
  It is operate-only: the app's own UI stays at its own address.
- The package is published like adapter-core; the toolkit is in the
  workspace and the conformance Toolkit class (87/87).

Tests: reverse-proxy 45 checks, sidecar 65 checks (stable across
repeated Windows runs after ending by draining the event loop instead
of process.exit, which aborted Node while the app's handle closed),
and an end-to-end adoption of a small foreign app (52 checks).
The CLI side of spec/v0_2/packaging.md: one pipeline for every way an
Agent App arrives, so every harness gets the same result.

- `agent-app catalogue [--search] [--source]`: the marketplace's apps,
  each marked compatible, incompatible (with why) or unknown. The
  default marketplace falls back from agent-app-collection to
  living-ui-marketplace, as CraftBot does, and says so.
- `agent-app inspect <id> | --from <source>`: what a source is
  (agent-app, living-ui or foreign), whether it installs, what its
  toolkit will run, what would be stripped, and the route (install,
  adopt, convert) when it doesn't.
- `agent-app install <id> | --from <zip|folder|github:…|git+…>`:
  acquire → inspect → sanitize → revendor → customize → place →
  register → dependencies → gate → serve → open, with --events for
  progress. Marketplace apps arrive the way CraftBot delivers them
  (catalogue.json, then the app's folder from the branch archive) and
  are trusted as catalogued; every other source is untrusted and owes a
  walk-verify. A failure leaves nothing registered or half-placed.
- `agent-app <dir> export` writes a package that installs elsewhere;
  `agent-app <dir> import` is the same pipeline in place.

Safety the old import and CraftBot's installer lacked:
- credentials (at any depth), runtime state, the data directory and
  build output are never carried over;
- system files come only from a toolkit installed here, never from one
  the package names or plants, and stray files in system folders are
  dropped;
- extraction refuses zip-slip, absolute and device paths, links,
  encrypted entries and oversize archives;
- downloads are capped while streaming and their sha256 recorded as
  provenance in the manifest;
- placeholder values are escaped per file type, or refused before any
  file is written;
- git runs with no prompts and no hooks.

Zip is read and written with Node's zlib (deflate/stored, data
descriptors, zip64, UTF-8 names) streaming entry by entry, so no
dependency is added. The describe cache is now kept per credential,
under a hash of it, so a walk never shows one agent another's reach.

Tests (no network; a loopback fake of GitHub): zip 28, package rules
14, install 24, describe cache; every existing CLI test still passes.
The gate's describe-budget and multi-user-access checks need the app
up, so during install they came back unchecked (and printed connection
refused). install now runs them after serve instead: a result whose
gate is {ok:true} has really been checked. An app that fails them —
say a multi-user app with sign-in off — is stopped and reported, and
stays registered so it can be fixed, as a gate failure does.

stop's work is now stopServed(), which returns its outcome without
printing, so install can stop an app and still answer with one JSON
document.

Proven end to end from a local marketplace: a react-node app (65/65
multi-user, 32/32 starter) and a PocketBase app whose install fetched
and verified PocketBase 0.39.7 itself (65/65, 20/32 as on any
PocketBase app).
…ketBase 0.39.7

Records the install pipeline and its commands, adopting with
blueprint-sidecar, the PocketBase 0.39.7 upgrade and how existing apps
take it, the hardened import, the per-credential describe cache, and
corrects the PocketBase starter-suite count (20/32). The conformance
Toolkit row for pocketbase-react no longer claims the binary is added
by hand.
Every surface calls the same CLI (catalogue, inspect, install, export),
so an app installed from OpenClaw, Hermes, dsh, CraftBot, Claude Code or
Pi arrives exactly as one installed from a terminal; nothing re-runs
the pipeline itself.

- integration-starter (the shared engine): five tools —
  agent_app_catalogue, _inspect, _install (its description explains
  trust and what `next` owes), _export, _list_apps; runInstall, which
  reports each --events step; install jobs and private, capped uploads
  for manager UIs; one PackageApi holding the manager routes. Its verb
  router now covers every framework verb and is tested against the
  built CLI's own commands; all eight toolkits are listed; install,
  import and adopt kick off the importer skill.
- Claude Code (MCP): the 22 tools; any non-zero exit is an error that
  keeps the CLI's own words.
- OpenClaw, Hermes (dashboard and webui) and dsh: Marketplace and Import
  panels beside the build form.
  - Marketplace: search, tags, compatibility, a form for customizable
    fields, and install with step-by-step progress, polled from a job
    so it survives reloads and has no client timeout.
  - Import: a zip, folder or git source is inspected before install,
    and Living UI or foreign code is handed to an agent to convert or
    adopt.
- CraftBot: marketplace_list, marketplace_install, inspect and import
  under CraftBot's own action names, so its agents keep working when
  CraftBot moves to this framework; find and run_operation declare the
  fields they read; the install hint names agent-app-framework.
- Hermes gains the same tools, routes and jobs in its Python engine;
  dsh's tools are run by the engine, so each reaches the binary that
  owns it.
- The importer skill is rewritten around the real commands: marketplace
  (catalogue, confirm, install; no walk-verify), packages (inspect,
  show, ask, install, walk-verify by a separate agent), Living UI and
  foreign code (convert, or adopt with blueprint-sidecar), export. The
  /agent-app prompts and the Claude Code plugin route these to it.

Tests: engine and packages (the real install path through the tools),
MCP, OpenClaw, dsh, Pi, Hermes (48 tests, system Python and the FastAPI
venv), CraftBot (5 tests, which fail on the old schema bugs); the three
manager pages checked in headless Chrome.
Converting a Living UI app or foreign code starts from its source kept
read-only in reference/source/, and the importer skill had to tell the
agent to fetch a marketplace folder by hand. `agent-app inspect … --keep
<dir>` now keeps the inspected source there, from any source, exactly
as a package would carry it: no credentials, secrets, runtime state,
data, .git/, node_modules/ or build output, and links never followed.
The report gains `kept`; the skill's convert steps use it.

Test: a Living UI marketplace app kept without its published
.superuser, and a non-empty --keep refused (install 26/26).
…efused

Found walking the whole user journey with the real CLIs:

- owner, invite and credential list/mint/revoke printed their result as
  plain lines on stdout, against the CLI's contract that stdout carries
  one JSON result. They now answer { ok, role, url, expiresAt },
  { ok, credentials }, { ok, token, credential } (the token's only
  appearance) and { ok, revoked }; the human lines stay on stderr.
- whoami turned every refusal into "whoami unavailable (adapter
  predates IAM, or credential rejected)", and data reported a revoked
  credential as "No entity". Both now lead with the app's own code
  (credential_revoked, token_expired, principal_inactive,
  missing_required, …) and pass on its how-to-recover hint, so an agent
  can take the step the operator skill names for that code.

The journey (setup, build, people, operate, connect, credentials, the
bridge, evolve, export and install, teardown) passes 53/53 end to end.
- bridge: a refused heartbeat is read, not assumed. A run that completed its
  own task is left to exit instead of being killed as claim_lost, and the log
  names who each run acts as.
- Bundled blueprints keep their .gitignore (packed as _gitignore). pb-serve
  --fetch keeps a binary only when its SHA-256 is the pinned release's, never
  by running it.
- PocketBase's describe root names the app from the manifest, not "Acme".
- A remote agent with no credential is pointed at login, not .agent-token.
  --scopes splits on commas or spaces (PowerShell joins a,b into "a b").
- scaffold needs --blueprint; agent-app blueprints lists them; dev --install.
- data create/update take field=value; tasks complete --summary; usage lines
  for tasks, data, dev, serve, login and remove say what they take.
- remove keeps the app's own backups. The harness build tool takes authMode.
- The Claude Code MCP server no longer overrides the built-in bridge profile.
- Blueprint guides: how the View runs an operation and finds who is signed in.
- The CLI reports the version in its own package.json.
All five go out together at 0.2.0 (protocol 0.2), so `npm i -g
agent-app-framework` no longer pulls the 0.1 alphas. The react-node and sidecar
blueprints depend on ^0.2.0, which the published packages satisfy.
@zfoong
zfoong merged commit da3d557 into main Oct 29, 2026
3 checks passed
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