create-mdan 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MDAN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # create-mdan
2
+
3
+ `create-mdan` scaffolds a minimal MDAN app.
4
+
5
+ Use it when you want the fastest path from a blank directory to a working MDAN app.
6
+
7
+ It supports both Node and Bun starters.
8
+
9
+ ## Usage
10
+
11
+ Node starter:
12
+
13
+ ```bash
14
+ npm create mdan@latest agent-app
15
+ cd agent-app
16
+ npm install
17
+ npm start
18
+ ```
19
+
20
+ Bun starter:
21
+
22
+ ```bash
23
+ bunx create-mdan agent-app
24
+ cd agent-app
25
+ bun install
26
+ bun start
27
+ ```
28
+
29
+ You can override the generated runtime explicitly:
30
+
31
+ ```bash
32
+ npm create mdan@latest agent-app -- --runtime bun
33
+ bunx create-mdan agent-app --runtime node
34
+ ```
35
+
36
+ Defaults:
37
+
38
+ - `npm create` and `npx create-mdan` default to the Node starter
39
+ - `bunx create-mdan` defaults to the Bun starter
40
+
41
+ The generated app uses this small shape:
42
+
43
+ - `app/index.md`
44
+ - `app/server.ts`
45
+ - `app/client.ts`
46
+ - `index.mjs`
47
+
48
+ ## Docs
49
+
50
+ - [Docs](https://docs.mdan.ai/)
51
+ - [Getting Started](https://docs.mdan.ai/getting-started)
52
+ - [Understanding MDAN](https://docs.mdan.ai/understanding-mdan)
53
+ - [Examples](https://docs.mdan.ai/examples)
54
+ - [SDK Overview](https://docs.mdan.ai/sdk)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli-bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-bin.d.ts","sourceRoot":"","sources":["../src/cli-bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "./cli.js";
3
+ main(process.argv.slice(2)).catch((error) => {
4
+ console.error(error instanceof Error ? error.message : String(error));
5
+ process.exitCode = 1;
6
+ });
7
+ //# sourceMappingURL=cli-bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-bin.js","sourceRoot":"","sources":["../src/cli-bin.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1C,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { type StarterRuntime } from "./index.js";
2
+ export interface ParsedCliArgs {
3
+ targetArg: string | undefined;
4
+ runtime: StarterRuntime;
5
+ showHelp: boolean;
6
+ }
7
+ export declare function detectDefaultRuntime(userAgent?: string): StarterRuntime;
8
+ export declare function parseCliArgs(argv: string[], userAgent?: string): ParsedCliArgs;
9
+ export declare function formatUsage(): string;
10
+ export declare function formatNextSteps(projectDir: string, runtime: StarterRuntime, targetArg?: string): string;
11
+ declare function main(argv: string[]): Promise<void>;
12
+ export { main };
13
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgD,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/F,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAMD,wBAAgB,oBAAoB,CAAC,SAAS,SAA0C,GAAG,cAAc,CAExG;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAA0C,GAAG,aAAa,CAkD/G;AAED,wBAAgB,WAAW,IAAI,MAAM,CAQpC;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,SAAa,GAAG,MAAM,CAa3G;AAMD,iBAAe,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBjD;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,94 @@
1
+ import { resolve } from "node:path";
2
+ import packageJson from "../package.json" with { type: "json" };
3
+ import { scaffoldStarterProject, toCompatibleSdkRange } from "./index.js";
4
+ function isStarterRuntime(value) {
5
+ return value === "node" || value === "bun";
6
+ }
7
+ export function detectDefaultRuntime(userAgent = process.env.npm_config_user_agent ?? "") {
8
+ return /\bbun\/\d/i.test(userAgent) ? "bun" : "node";
9
+ }
10
+ export function parseCliArgs(argv, userAgent = process.env.npm_config_user_agent ?? "") {
11
+ let targetArg;
12
+ let runtime = detectDefaultRuntime(userAgent);
13
+ let showHelp = false;
14
+ for (let index = 0; index < argv.length; index += 1) {
15
+ const arg = argv[index];
16
+ if (!arg) {
17
+ continue;
18
+ }
19
+ if (arg === "--help" || arg === "-h") {
20
+ showHelp = true;
21
+ continue;
22
+ }
23
+ if (arg === "--") {
24
+ continue;
25
+ }
26
+ if (arg === "--runtime") {
27
+ const next = argv[index + 1];
28
+ if (!next || !isStarterRuntime(next)) {
29
+ throw new Error('Expected "--runtime" to be followed by "node" or "bun".');
30
+ }
31
+ runtime = next;
32
+ index += 1;
33
+ continue;
34
+ }
35
+ if (arg.startsWith("--runtime=")) {
36
+ const value = arg.slice("--runtime=".length);
37
+ if (!isStarterRuntime(value)) {
38
+ throw new Error(`Unsupported runtime "${value}". Expected "node" or "bun".`);
39
+ }
40
+ runtime = value;
41
+ continue;
42
+ }
43
+ if (arg.startsWith("-")) {
44
+ throw new Error(`Unknown option "${arg}".`);
45
+ }
46
+ if (targetArg) {
47
+ throw new Error(`Unexpected extra argument "${arg}".`);
48
+ }
49
+ targetArg = arg;
50
+ }
51
+ return { targetArg, runtime, showHelp };
52
+ }
53
+ export function formatUsage() {
54
+ return [
55
+ "Usage:",
56
+ " npm create mdan@latest <project-name>",
57
+ " npm create mdan@latest <project-name> -- --runtime bun",
58
+ " bunx create-mdan <project-name>",
59
+ " bunx create-mdan <project-name> --runtime node"
60
+ ].join("\n");
61
+ }
62
+ export function formatNextSteps(projectDir, runtime, targetArg = projectDir) {
63
+ const installCommand = runtime === "bun" ? "bun install" : "npm install";
64
+ const startCommand = runtime === "bun" ? "bun start" : "npm start";
65
+ return [
66
+ `Created MDAN ${runtime} starter in ${projectDir}`,
67
+ "",
68
+ "Next steps:",
69
+ ` cd ${targetArg}`,
70
+ "",
71
+ ` ${installCommand}`,
72
+ ` ${startCommand}`
73
+ ].join("\n");
74
+ }
75
+ function printUsage() {
76
+ console.log(formatUsage());
77
+ }
78
+ async function main(argv) {
79
+ const { targetArg, runtime, showHelp } = parseCliArgs(argv);
80
+ if (!targetArg || showHelp) {
81
+ printUsage();
82
+ return;
83
+ }
84
+ const targetDir = resolve(process.cwd(), targetArg);
85
+ const projectDir = await scaffoldStarterProject({
86
+ targetDir,
87
+ sdkVersion: toCompatibleSdkRange(packageJson.version),
88
+ runtime,
89
+ ...(targetArg === "." ? {} : { projectName: targetArg })
90
+ });
91
+ console.log(formatNextSteps(projectDir, runtime, targetArg));
92
+ }
93
+ export { main };
94
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type StarterRuntime = "node" | "bun";
2
+ export interface ScaffoldStarterProjectOptions {
3
+ targetDir: string;
4
+ projectName?: string;
5
+ sdkVersion: string;
6
+ runtime?: StarterRuntime;
7
+ }
8
+ export declare function toCompatibleSdkRange(packageVersion: string): string;
9
+ export declare function scaffoldStarterProject(options: ScaffoldStarterProjectOptions, moduleUrl?: string): Promise<string>;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,CAAC;AAE5C,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AASD,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAQnE;AAsDD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,EACtC,SAAS,SAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAuBjB"}
package/dist/index.js ADDED
@@ -0,0 +1,86 @@
1
+ import { cp, mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import { basename, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const TEMPLATE_PLACEHOLDERS = {
5
+ __PROJECT_NAME__: "",
6
+ __SDK_VERSION__: ""
7
+ };
8
+ const DEFAULT_RUNTIME = "node";
9
+ export function toCompatibleSdkRange(packageVersion) {
10
+ const match = packageVersion.match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
11
+ if (!match) {
12
+ throw new Error(`Unsupported package version "${packageVersion}".`);
13
+ }
14
+ const [, major, minor] = match;
15
+ return `^${major}.${minor}.0`;
16
+ }
17
+ function templateRootsFromModule(moduleUrl, runtime) {
18
+ return [
19
+ resolve(fileURLToPath(new URL("../template/shared", moduleUrl))),
20
+ resolve(fileURLToPath(new URL(`../template/${runtime}`, moduleUrl)))
21
+ ];
22
+ }
23
+ async function ensureEmptyTarget(targetDir) {
24
+ try {
25
+ const targetStat = await stat(targetDir);
26
+ if (!targetStat.isDirectory()) {
27
+ throw new Error(`Target path "${targetDir}" already exists and is not a directory.`);
28
+ }
29
+ const entries = await readdir(targetDir);
30
+ if (entries.length > 0) {
31
+ throw new Error(`Target directory "${targetDir}" must be empty.`);
32
+ }
33
+ }
34
+ catch (error) {
35
+ const code = error.code;
36
+ if (code === "ENOENT") {
37
+ await mkdir(targetDir, { recursive: true });
38
+ return;
39
+ }
40
+ throw error;
41
+ }
42
+ }
43
+ async function replaceInFile(filePath, replacements) {
44
+ const original = await readFile(filePath, "utf8");
45
+ let next = original;
46
+ for (const [pattern, value] of Object.entries(replacements)) {
47
+ next = next.replaceAll(pattern, value);
48
+ }
49
+ if (next !== original) {
50
+ await writeFile(filePath, next, "utf8");
51
+ }
52
+ }
53
+ async function walkFiles(root) {
54
+ const entries = await readdir(root, { withFileTypes: true });
55
+ const files = [];
56
+ for (const entry of entries) {
57
+ const filePath = join(root, entry.name);
58
+ if (entry.isDirectory()) {
59
+ files.push(...(await walkFiles(filePath)));
60
+ continue;
61
+ }
62
+ files.push(filePath);
63
+ }
64
+ return files;
65
+ }
66
+ export async function scaffoldStarterProject(options, moduleUrl = import.meta.url) {
67
+ const runtime = options.runtime ?? DEFAULT_RUNTIME;
68
+ const templateRoots = templateRootsFromModule(moduleUrl, runtime);
69
+ const targetDir = resolve(options.targetDir);
70
+ const projectName = options.projectName?.trim() || basename(targetDir);
71
+ if (!projectName) {
72
+ throw new Error("Project name cannot be empty.");
73
+ }
74
+ await ensureEmptyTarget(targetDir);
75
+ for (const templateRoot of templateRoots) {
76
+ await cp(templateRoot, targetDir, { recursive: true });
77
+ }
78
+ const replacements = {
79
+ __PROJECT_NAME__: projectName,
80
+ __SDK_VERSION__: options.sdkVersion
81
+ };
82
+ const files = await walkFiles(targetDir);
83
+ await Promise.all(files.map((filePath) => replaceInFile(filePath, replacements)));
84
+ return targetDir;
85
+ }
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "create-mdan",
3
+ "version": "0.5.0",
4
+ "private": false,
5
+ "description": "Create a minimal MDAN app with npm create or bunx.",
6
+ "license": "MIT",
7
+ "homepage": "https://docs.mdan.ai",
8
+ "bugs": {
9
+ "url": "https://github.com/mdan-ai/mdan/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/mdan-ai/mdan.git"
14
+ },
15
+ "keywords": [
16
+ "mdan",
17
+ "create",
18
+ "scaffold",
19
+ "markdown",
20
+ "ai",
21
+ "agent",
22
+ "agentic",
23
+ "workflow",
24
+ "skills-app",
25
+ "agent-app",
26
+ "cli",
27
+ "bun"
28
+ ],
29
+ "type": "module",
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE",
39
+ "template/shared",
40
+ "template/node",
41
+ "template/bun"
42
+ ],
43
+ "bin": {
44
+ "create-mdan": "./dist/cli-bin.js"
45
+ },
46
+ "scripts": {
47
+ "prepack": "node ../scripts/sync-package-license.mjs",
48
+ "postpack": "node ../scripts/sync-package-license.mjs --cleanup"
49
+ },
50
+ "exports": {
51
+ ".": {
52
+ "types": "./dist/index.d.ts",
53
+ "default": "./dist/index.js"
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,31 @@
1
+ # __PROJECT_NAME__
2
+
3
+ This is a minimal MDAN starter for the Bun host.
4
+
5
+ ## 30-Second Tour
6
+
7
+ - `app/index.md`
8
+ Defines the page source and interaction.
9
+ - `app/server.ts`
10
+ Owns state and action handlers.
11
+ - `app/client.ts`
12
+ Mounts the browser runtime.
13
+ - `index.mjs`
14
+ Starts the Bun host.
15
+
16
+ ## Run It
17
+
18
+ ```bash
19
+ bun install
20
+ bun start
21
+ ```
22
+
23
+ This starter is Bun-native, so you can use it without installing Node.
24
+
25
+ Then open [http://127.0.0.1:3000/](http://127.0.0.1:3000/).
26
+
27
+ ## First Things To Change
28
+
29
+ 1. Edit `app/index.md`
30
+ 2. Edit `app/server.ts`
31
+ 3. Keep `app/client.ts` as-is, or replace it when you want to own the UI
@@ -0,0 +1,63 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { createHost } from "@mdanai/sdk/server/bun";
6
+
7
+ import { createAppServer } from "./dist/server.js";
8
+
9
+ const port = Number(process.env.PORT || 3000);
10
+ const hostname = process.env.HOST || "127.0.0.1";
11
+ const projectRoot = fileURLToPath(new URL("./", import.meta.url));
12
+ const sourcePath = join(projectRoot, "app", "index.md");
13
+ const assetVersion = Date.now().toString(36);
14
+
15
+ function withVersion(path) {
16
+ return `${path}?v=${assetVersion}`;
17
+ }
18
+
19
+ const importMap = {
20
+ imports: {
21
+ "@mdanai/sdk/core": withVersion("/node_modules/@mdanai/sdk/dist/core/index.js"),
22
+ "@mdanai/sdk/web": withVersion("/node_modules/@mdanai/sdk/dist/web/index.js"),
23
+ "@mdanai/sdk/elements": withVersion("/node_modules/@mdanai/sdk/dist/elements/index.js"),
24
+ "lit": withVersion("/node_modules/lit/index.js"),
25
+ "lit-html": withVersion("/node_modules/lit-html/lit-html.js"),
26
+ "lit-html/is-server.js": withVersion("/node_modules/lit-html/is-server.js"),
27
+ "lit-element/lit-element.js": withVersion("/node_modules/lit-element/lit-element.js"),
28
+ "@lit/reactive-element": withVersion("/node_modules/@lit/reactive-element/reactive-element.js")
29
+ }
30
+ };
31
+
32
+ function injectEnhancement(html) {
33
+ const enhancement = `
34
+ <script type="importmap">${JSON.stringify(importMap)}</script>
35
+ <script type="module">
36
+ import { mountApp } from "${withVersion("/app/client.js")}";
37
+ mountApp(document, window.fetch.bind(window));
38
+ </script>`;
39
+
40
+ return html.replace("</body>", `${enhancement}\n </body>`);
41
+ }
42
+
43
+ const source = await readFile(sourcePath, "utf8");
44
+ const mdan = createAppServer({ source });
45
+
46
+ Bun.serve({
47
+ hostname,
48
+ port,
49
+ fetch: createHost(mdan, {
50
+ transformHtml: injectEnhancement,
51
+ staticFiles: {
52
+ "/app/client.js": join(projectRoot, "dist", "client.js")
53
+ },
54
+ staticMounts: [
55
+ {
56
+ urlPrefix: "/node_modules/",
57
+ directory: join(projectRoot, "node_modules")
58
+ }
59
+ ]
60
+ })
61
+ });
62
+
63
+ console.log(`MDAN starter running at http://${hostname}:${port}/`);
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc -p tsconfig.json",
8
+ "start": "tsc -p tsconfig.json && bun run index.mjs"
9
+ },
10
+ "dependencies": {
11
+ "@mdanai/sdk": "__SDK_VERSION__"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^24.6.0",
15
+ "typescript": "^5.9.3"
16
+ }
17
+ }
@@ -0,0 +1,29 @@
1
+ # __PROJECT_NAME__
2
+
3
+ This is a minimal MDAN starter for the Node host.
4
+
5
+ ## 30-Second Tour
6
+
7
+ - `app/index.md`
8
+ Defines the page source and interaction.
9
+ - `app/server.ts`
10
+ Owns state and action handlers.
11
+ - `app/client.ts`
12
+ Mounts the browser runtime.
13
+ - `index.mjs`
14
+ Starts the Node host.
15
+
16
+ ## Run It
17
+
18
+ ```bash
19
+ npm install
20
+ npm start
21
+ ```
22
+
23
+ Then open [http://127.0.0.1:3000/](http://127.0.0.1:3000/).
24
+
25
+ ## First Things To Change
26
+
27
+ 1. Edit `app/index.md`
28
+ 2. Edit `app/server.ts`
29
+ 3. Keep `app/client.ts` as-is, or replace it when you want to own the UI
@@ -0,0 +1,62 @@
1
+ import http from "node:http";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { createHost } from "@mdanai/sdk/server/node";
7
+
8
+ import { createAppServer } from "./dist/server.js";
9
+
10
+ const port = Number(process.env.PORT || 3000);
11
+ const projectRoot = fileURLToPath(new URL("./", import.meta.url));
12
+ const sourcePath = join(projectRoot, "app", "index.md");
13
+ const assetVersion = Date.now().toString(36);
14
+
15
+ function withVersion(path) {
16
+ return `${path}?v=${assetVersion}`;
17
+ }
18
+
19
+ const importMap = {
20
+ imports: {
21
+ "@mdanai/sdk/core": withVersion("/node_modules/@mdanai/sdk/dist/core/index.js"),
22
+ "@mdanai/sdk/web": withVersion("/node_modules/@mdanai/sdk/dist/web/index.js"),
23
+ "@mdanai/sdk/elements": withVersion("/node_modules/@mdanai/sdk/dist/elements/index.js"),
24
+ "lit": withVersion("/node_modules/lit/index.js"),
25
+ "lit-html": withVersion("/node_modules/lit-html/lit-html.js"),
26
+ "lit-html/is-server.js": withVersion("/node_modules/lit-html/is-server.js"),
27
+ "lit-element/lit-element.js": withVersion("/node_modules/lit-element/lit-element.js"),
28
+ "@lit/reactive-element": withVersion("/node_modules/@lit/reactive-element/reactive-element.js")
29
+ }
30
+ };
31
+
32
+ function injectEnhancement(html) {
33
+ const enhancement = `
34
+ <script type="importmap">${JSON.stringify(importMap)}</script>
35
+ <script type="module">
36
+ import { mountApp } from "${withVersion("/app/client.js")}";
37
+ mountApp(document, window.fetch.bind(window));
38
+ </script>`;
39
+
40
+ return html.replace("</body>", `${enhancement}\n </body>`);
41
+ }
42
+
43
+ const source = await readFile(sourcePath, "utf8");
44
+ const mdan = createAppServer({ source });
45
+ const server = http.createServer(
46
+ createHost(mdan, {
47
+ transformHtml: injectEnhancement,
48
+ staticFiles: {
49
+ "/app/client.js": join(projectRoot, "dist", "client.js")
50
+ },
51
+ staticMounts: [
52
+ {
53
+ urlPrefix: "/node_modules/",
54
+ directory: join(projectRoot, "node_modules")
55
+ }
56
+ ]
57
+ })
58
+ );
59
+
60
+ server.listen(port, "127.0.0.1", () => {
61
+ console.log(`MDAN starter running at http://127.0.0.1:${port}/`);
62
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc -p tsconfig.json",
8
+ "start": "tsc -p tsconfig.json && node index.mjs"
9
+ },
10
+ "dependencies": {
11
+ "@mdanai/sdk": "__SDK_VERSION__"
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^24.6.0",
15
+ "typescript": "^5.9.3"
16
+ }
17
+ }
@@ -0,0 +1,7 @@
1
+ import { mountMdanElements } from "@mdanai/sdk/elements";
2
+ import { createHeadlessHost } from "@mdanai/sdk/web";
3
+
4
+ export function mountApp(root: HTMLElement, fetchImpl: typeof fetch): void {
5
+ const host = createHeadlessHost({ root, fetchImpl });
6
+ mountMdanElements({ root, host }).mount();
7
+ }
@@ -0,0 +1,17 @@
1
+ ---
2
+ title: "Agent App"
3
+ ---
4
+
5
+ # Agent App
6
+
7
+ Use this starter as the smallest end-to-end MDAN app.
8
+
9
+ <!-- mdan:block main -->
10
+
11
+ ```mdan
12
+ BLOCK main {
13
+ INPUT text required -> message
14
+ GET "/list" -> refresh label:"Refresh"
15
+ POST "/post" (message) -> submit label:"Submit"
16
+ }
17
+ ```
@@ -0,0 +1,51 @@
1
+ import { composePage } from "@mdanai/sdk/core";
2
+ import { createHostedApp } from "@mdanai/sdk/server";
3
+
4
+ export interface CreateAppServerOptions {
5
+ source: string;
6
+ initialMessages?: string[];
7
+ }
8
+
9
+ export function createAppServer(options: CreateAppServerOptions) {
10
+ const messages = [...(options.initialMessages ?? ["Welcome to MDAN"])];
11
+
12
+ function renderMainBlock(): string {
13
+ const count = `${messages.length} live ${messages.length === 1 ? "message" : "messages"}`;
14
+ return `## ${count}\n\n${messages.map((message) => `- ${message}`).join("\n")}`;
15
+ }
16
+
17
+ function renderPage() {
18
+ return composePage(options.source, {
19
+ blocks: {
20
+ main: renderMainBlock()
21
+ }
22
+ });
23
+ }
24
+
25
+ return createHostedApp({
26
+ pages: {
27
+ "/": renderPage
28
+ },
29
+ actions: [
30
+ {
31
+ target: "/list",
32
+ methods: ["GET"],
33
+ routePath: "/",
34
+ blockName: "main",
35
+ handler: ({ block }) => block()
36
+ },
37
+ {
38
+ target: "/post",
39
+ methods: ["POST"],
40
+ routePath: "/",
41
+ blockName: "main",
42
+ handler: ({ inputs, block }) => {
43
+ if (inputs.message) {
44
+ messages.push(inputs.message);
45
+ }
46
+ return block();
47
+ }
48
+ }
49
+ ]
50
+ });
51
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "strict": true,
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "noUncheckedIndexedAccess": true,
12
+ "exactOptionalPropertyTypes": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "resolveJsonModule": true,
16
+ "lib": ["ES2022", "DOM"],
17
+ "types": ["node"],
18
+ "rootDir": "./app",
19
+ "outDir": "./dist"
20
+ },
21
+ "include": ["app/**/*.ts"]
22
+ }