Skip to content
Esc
↑↓navigate↵open⌘Jpreview
On this page

Code export

The code behind your app is yours: export it anytime and reuse it anywhere.

What you can do

The code behind your app is yours. It has three parts: the module is the app’s business logic, the ui is the interactive interface, and the styles are the visual look. You can export all three at any time and reuse them: move an app between agents, back it up, or hand it to a different tool. Anyone who can edit the app can export its code; someone with view-only access can’t.

Copy an app in one step. Your agent can make you a private copy of an app you can edit, with or without its stored data, without reading the code out and sending it back. Use it for a demo twin of a real app, a copy to try a risky change on, or a fresh start.

Technical

Exporting source is an agent action. Ask your agent rather than looking for it in App settings.

Copy this prompt for your agent

Export the source for one of my Charming apps so I can reuse it (back
it up or move it to a different agent), or copy it into a new app. Read
https://charm.ing/docs/capabilities/code-export.md first. List my
apps and ask me which one, or if I've only got one, use that. To make a
new app from it, copy it with copy_app and ask me whether to include
its data; otherwise export the source.

How an agent performs this job

To export source, an agent calls GET /app/<id>/source over HTTP or get_app_source({ app_id }) over MCP. Both return id, url, manifestId, displayName, a server-owned revision, capabilities, and source: { module, ui, styles }. The MCP tool also returns sourceSize, the character count of each bucket. When the source is too large for your client, read one bucket at a time with get_app_source({ app_id, bucket, offset, expected_revision }): each call returns up to 20,000 characters of whole lines in chunk.text; pass chunk.next_offset as offset until it is null, and pass the revision from the first read as expected_revision so a change between reads fails with revision_mismatch instead of mixing revisions. To turn an app into a new one, copy it instead of exporting and re-sending its source.

Copy an app

An agent calls copy_app({ app_id, manifest_id?, display_name?, include_data?, idempotency_key? }) over MCP or POST /app/<id>/copy over HTTP with the same optional fields as a JSON body. The caller gets a new private app they own, and the source never passes through the agent’s context.

  • What the copy keeps. The module, UI, styles, icon, description, and network settings, exactly as the source has them.
  • What stays behind. Shares, Public, signed-in access, Template and Listed state, tokens, secrets, Routines, Webhooks, the custom domain, and version history. The copy starts at revision 1.
  • Manifest id. Without manifest_id, the copy takes the next free id for the caller: the source’s id if they hold no app with it, otherwise <id>-2, <id>-3, and so on. An id still holding data or secrets from a deleted app counts as held. A manifest_id the caller already holds fails with manifest_id_conflict and changes nothing; a copy never replaces an app. The error names the app holding the id in existingAppId. The copy keeps the authored id as sourceManifestId, so the UI’s window.charming.api('<manifest-id>') calls keep working without an edit.
  • Data. include_data: true also copies the source’s stored data and assets. Per-visitor data on a public custom domain never copies. Only the owner of a personally owned source can copy its data; anyone else gets copy_data_forbidden. A team app’s data is not copied, even by a team admin, because a copy always lands in a personal account; copy a team app without include_data. A copy carries at most 24 MiB of stored data and assets together (copy_data_too_large), and must also fit the per-app caps (storage_full, asset_count_exceeded, asset_quota_exceeded) and the caller’s plan storage allowance (storage_limit_exceeded). The data copies in the same step that creates the copy, so a copy has all its data or does not exist, and a refused copy creates nothing. The result reports copiedData: { keys, assets }, except on an idempotency replay, which returns the earlier copy without it.
  • Secrets. Secrets never copy. When the caller can manage the source’s secrets, secretsNotCopied lists their names so the agent can set them on the copy.
  • Templates. Someone who can copy a Template but not edit it gets a Template copy (copyKind: "template"): it records the Template as its source, takes the starter prompt, and never carries data.
  • ESM apps. An app on the ESM contract copies through a build. When the build publishes at once, the result is the copy, as for any app. Otherwise it is the build status (HTTP 202) to poll with get_app_build; the copy and its data appear together when the build publishes. An ESM copy takes its name from manifest.meta.name, so display_name is refused.
  • Retries. Pass an idempotency_key (8–100 visible ASCII characters) to make a retry safe: the same key returns the copy, or the pending build, that the first call made instead of making another. If that copy was deleted or its build failed, the retry makes a new copy. The same key with a different source, include_data, or manifest_id fails with idempotency_conflict. Without a key, every call makes a new copy.

The contract

get_app_source and GET /app/<id>/source require the same edit access as update_app: the caller must be the owner or hold a collaborator (or team-member) grant on the app; get_app_source fails with not_found for anyone else. copy_app and POST /app/<id>/copy need that edit access too, or a Template the caller may copy, and a signed-in user: an app token or render token is refused. A caller who cannot open the app gets not_found; one who can open it but not copy it gets forbidden. The HTTP route accepts the original chrm_app_* edit token (unclaimed apps) or a signed-in chrm_user_* token with that access (claimed apps); a render token or claim cookie is rejected.

Limits and access

  • Who can export. The owner, a collaborator, or a team member with write access can call get_app_source; an end-user or viewer share (read/run only, no edit) can’t.
  • Who can copy. Anyone who can export can copy, without data. Copying data needs the owner of a personally owned app.
  • Copies for other people. To let people who can only view the App make their own copy, enable Template. Publish it as Listed if anyone should be able to discover and copy it without access to the source App or its data.

Was this page helpful?