Export and import
Every workflow becomes a scene — its data, its tools, the agent that runs it, and the history of everything it did. A scene can also be written to disk. What comes out is a folder of plain files: markdown for notes, HTML for views, TypeScript for code, JSON for data, and one small file describing the scene itself. Read the folder back in and you get the same scene. There is one importer and one exporter; a folder on disk, a URL, a share link and a blueprint all go through the same two. This page shows the folder, the two commands that move it, and what the folder can be, by example. The folder is the scene at one moment; to move the scene with its history, see Scenes to git and back.
A scene on disk is a folder of files you already know how to read
Take a scene that prepares a quarterly review. Written to disk it looks like this:
qbr/
├── scene.json # the scene's identity, and what it is about
├── notes/prep.md # a note the agent keeps
├── views/dashboard.html # a view someone authored
├── scripts/revenue.ts # a script cell: recomputes from its sources
├── data/segments.json # a data cell: a value frozen when it was written
└── scenes/drafts/ # a child scene, same layout again
Nothing was translated to get here. A note is stored as markdown, a view as HTML, a script as TypeScript, data as JSON; the export lays those files on disk under the names they already have. Import reads the same tree back. Between the two, nothing changes, which is the property everything below depends on.
scene.json carries identity and description, and nothing else
The one file that is not an asset describes the scene:
{
"name": "Quarterly review",
"slug": "qbr",
"description": "Revenue, churn and the three decisions for the board",
"icon": "chart.bar",
"tint": "2563EB",
"fields": {
"category": "finance",
"tags": ["board", "quarterly"],
"tagline": "The board pack, kept current by the agent"
}
}
fields is the same idea as the fields on an asset: an open set of typed facts about the thing, read by the product where it needs them. A scene's page, its card in a list, and the agent reading the scene all take their description from here. Assets are never listed in scene.json; the folder is the list.
Two commands move the folder in either direction
daslab scene export scn_4jzQSSRcg2i8bCbO --out ./qbr # the scene → a folder
daslab scene push ./qbr # a folder → your workspace
export writes the folder above. push reads one back in; it accepts the folder, a URL that serves it, or the same scene compiled into one .blueprint.json file for a gist or a paste (Blueprints describes that document). daslab provider push and daslab blueprint push are the same command under older names. Because the round trip is exact, you can edit a scene in your editor, push it, let the agent and your team change it in the app, and export the result again.
An import merges, commits, and remembers where it came from
Exporting records which commit the folder came from. When you push the folder back, the importer compares that commit to the scene's current head and merges per asset: an asset you changed that nobody else touched applies; an asset that moved on in the app while you were editing is reported as a conflict. By default the import proceeds with your version and tells you what it overrode.
daslab scene push ./qbr --strict # refuse while any conflict stands
daslab scene push ./qbr --replace # the folder is the whole truth: files absent from it are removed
--replace is what an integration author wants when pushing their own folder; merge is what everyone else wants. Each import lands on the scene's commit chain as one commit per scene touched, with the actor and a message, and the commit records the source: a folder, a URL, another scene's share link, or a blueprint and the inputs it was given. So the timeline reads "imported from …", "forked from …", "provisioned from …" in the same place it reads "agent wrote …", and a forked scene always knows its upstream and the commit it forked at.
A scene can come from four places
A folder on disk, yours or a checkout: daslab scene push ./folder.
A URL: a GitHub folder, or any host that serves scene.json and the files beside it. This is how an open integration gets into your workspace without cloning anything.
A share link to another scene, which is a fork. The source scene is exported, then imported into your workspace with its origin recorded. Re-importing from the same link later merges the upstream's changes onto yours.
A blueprint scene, which carries a template. Provisioning imports the template with your inputs filled in and its credential references resolved; the new scene records the blueprint, the commit, and the inputs. What a blueprint scene looks like is further down.
Credentials are references on both ends
Export never writes a secret. Credential fields (api_key, access_token, client_secret, …) are sealed out of the export and replaced by a reference, kern:NAME for the encrypted vault or env:NAME for the server environment. Import resolves the reference; a credential that lives only on your machine rides the request as --secret type:id:field=value, encrypted in transit and never written to disk. A folder with connected accounts in it is safe to commit, share, and fork by construction.
A scene is an integration when its assets have the right shape
No scene declares "I am an integration". A scene that contains tools grouped under asset types is one, and it is loaded as one. Here is the Kenko integration, which runs on daslab.run from exactly this folder:
kenko/
├── scene.json
├── lib/client.ts # the API client, written once
├── assets/
│ ├── account/
│ │ ├── asset.json # what connecting asks for: API key, center id
│ │ └── tools/list_centers.ts # tools that act on the account
│ ├── schedule/ # "Class"
│ │ ├── asset.json # name, fields, how it displays, its card
│ │ └── tools/list_schedules.ts # tools that act on classes
│ ├── contact/ # "Member"
│ └── booking/ # parent: contact
├── browse.ts # feeds the picker: classes, members, bookings
└── kb/
└── partner-api.md # the knowledge base
Each folder under assets/ is an asset type. account is the connection: its asset.json lists the credential fields the connect sheet shows, and every tool receives all of them. A scene without an assets/account/ folder is a keyless integration; Open-Meteo and Poly Haven are built that way. The other folders are the things a person browses and pins, each with the tools that act on it. Push this folder into your workspace and the integration is there on the next message: the picker shows classes and members, the agent has the twelve tools, the connect sheet asks for the key. The open integrations, Kenko among them, live in one public repository and are served from it; how they are reviewed and go live is on Scenes to git and back.
A tool is one file, and the file describes itself
// assets/schedule/tools/list_schedules.ts
import { defineTool } from "@daslabhq/asset-provider";
import { request, collection } from "../../../lib/client";
export default defineTool({
description: "List public class sessions available for partner booking, ordered by start date.",
readOnly: true,
input: {
start_date: { type: "string", description: "YYYY-MM-DD" },
end_date: { type: "string", description: "YYYY-MM-DD, on or after start_date" },
},
async run(input, ctx) {
return collection(await request(ctx.credential, "/schedules", { query: input }), "schedules");
},
});
The tool's name is its path: kenko, then the file, then the asset type, so this one is kenko_list_schedules. Its description and input schema are read out of the file when the scene loads; there is no second copy in any manifest. A tool that is a single HTTP request needs no code at all, only a list.http.json holding the templated request, with {{credential.api_key}} and {{input.query}} where the values go. A write that should pause for a human says requiresApproval: true, and the job stops there until someone approves it.
kb/ is the knowledge base
Every markdown file in kb/ is a guide. The filename is its slug, the first heading is its title. The guides show on the integration's page, the agent can read them in any scene before the integration is even connected, and each one can be pinned as an asset. Kenko's three guides, on the Partner API, on where its payment data actually lives, and on connecting a studio, are three files in that folder.
A blueprint is a scene that carries another scene
A blueprint packages a template and everything around it, and it is itself a scene, which is what makes it shareable:
qbr-starter/
├── scene.json # identity, plus: template "scenes/qbr", the inputs it asks for, secrets as references
├── scenes/qbr/… # the template, a complete scene in this same layout
├── kb/getting-started.md
└── examples/first-run/ # a recorded run you can replay
The blueprint scene and the scene it produced stay two things: the package, and what was built from it. Blueprints describes the same package written as a single JSON document.
Where to look next
Scenes to git and back moves the scene with its history and holds the public integrations repository; Blueprints is the same scene as one JSON document; Provider scenes shows an integration authored inside the app as typed rows; Layers covers who can see a scene and its share link; the CLI has every flag.
Updated 2026-08-23