create-kdna-web-app 0.1.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,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 KDNA Authors
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,13 @@
1
+ create-kdna-web-app
2
+ Copyright 2026 KDNA Authors
3
+
4
+ This product includes software developed at
5
+ https://github.com/aikdna/create-kdna-web-app
6
+
7
+ create-kdna-web-app is a project scaffolding CLI for KDNA-integrated
8
+ web applications. Generated projects include the KDNA packages each
9
+ template imports at runtime, pre-configured with a working validate →
10
+ plan-load → load → consume flow.
11
+
12
+ See LICENSE for the full Apache 2.0 license text.
13
+ See README.md for usage and template options.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # create-kdna-web-app
2
+
3
+ **Scaffold a KDNA-integrated web application in one command.**
4
+
5
+ ```bash
6
+ npx create-kdna-web-app my-app
7
+ cd my-app
8
+ npm run dev
9
+ ```
10
+
11
+ The Next.js templates include `@aikdna/kdna-web-server` and
12
+ `@aikdna/kdna-react` pre-configured with a validate -> inspect ->
13
+ plan-load -> load demo. The Express template includes the server adapter
14
+ and a minimal static HTML demo.
15
+
16
+ [![npm](https://img.shields.io/npm/v/create-kdna-web-app)](https://www.npmjs.com/package/create-kdna-web-app)
17
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
18
+
19
+ ---
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ npx create-kdna-web-app <project-name> [options]
25
+ ```
26
+
27
+ ### Options
28
+
29
+ | Flag | Default | Description |
30
+ |------|---------|-------------|
31
+ | `--template` | `nextjs` | Project template — see [templates](#templates) |
32
+ | `--package-manager` | auto-detected | `npm`, `pnpm`, or `yarn` |
33
+ | `--no-install` | — | Scaffold files without running install |
34
+
35
+ ### Examples
36
+
37
+ ```bash
38
+ # Next.js App Router (default)
39
+ npx create-kdna-web-app my-app
40
+
41
+ # Express
42
+ npx create-kdna-web-app my-app --template express
43
+
44
+ # Next.js Pages Router
45
+ npx create-kdna-web-app my-app --template nextjs-pages
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Templates
51
+
52
+ ### `nextjs` (default)
53
+
54
+ - Next.js 16+ with App Router
55
+ - `app/api/kdna/[...route]/route.js` — all KDNA endpoints mounted
56
+ - `app/page.jsx` — full demo: file drop, inspect, load, display
57
+ - `.env.local.example` for local configuration
58
+
59
+ ### `nextjs-pages`
60
+
61
+ - Next.js with Pages Router
62
+ - `pages/api/kdna/[...route].js` — all KDNA endpoints
63
+ - `pages/index.jsx` — same demo as the App Router template
64
+
65
+ ### `express`
66
+
67
+ - Express with ESM
68
+ - `src/server.js` — KDNA router mounted at `/api/kdna`
69
+ - `public/index.html` — minimal HTML demo page
70
+ - `.env.example` for local configuration
71
+
72
+ ---
73
+
74
+ ## Template dependencies
75
+
76
+ | Template | KDNA packages |
77
+ |----------|---------------|
78
+ | `nextjs` | `@aikdna/kdna-core`, `@aikdna/kdna-web-server`, `@aikdna/kdna-react` |
79
+ | `nextjs-pages` | `@aikdna/kdna-core`, `@aikdna/kdna-web-server`, `@aikdna/kdna-react` |
80
+ | `express` | `@aikdna/kdna-core`, `@aikdna/kdna-web-server` |
81
+
82
+ ## Pre-configured flow
83
+
84
+ The Next.js templates provide the full React flow:
85
+
86
+ 1. User drops a `.kdna` file onto the `<KDNAFileDropzone>`.
87
+ 2. The file is uploaded and inspected — metadata appears immediately.
88
+ 3. `<KDNALoadPlanGate>` checks whether a password or license is needed.
89
+ 4. If the asset is open, it loads automatically.
90
+ 5. If the asset requires a password, `<KDNAPasswordUnlockDialog>` appears.
91
+ 6. The loaded content is displayed.
92
+
93
+ The Express template mounts the same `/api/kdna` server endpoints and includes
94
+ a minimal HTML page that uploads, inspects, and loads an open `.kdna` asset.
95
+
96
+ ### Environment variables
97
+
98
+ ```bash
99
+ # Optional
100
+ KDNA_STORAGE_DIR=/tmp/kdna
101
+
102
+ # Optional — only needed for licensed-mode assets
103
+ KDNA_ACTIVATION_URL=https://your-activation-server.example.com
104
+ ```
105
+
106
+ ---
107
+
108
+ ## After scaffolding
109
+
110
+ ```bash
111
+ cd my-app
112
+ cp .env.local.example .env.local # Next.js templates
113
+ # or: cp .env.example .env # Express template, if you load env files locally
114
+ npm test # smoke-test KDNA package imports
115
+ npm run dev
116
+ # Open http://localhost:3000 and drop a .kdna file
117
+ ```
118
+
119
+ To get a `.kdna` file for testing:
120
+
121
+ ```bash
122
+ npm install -g @aikdna/kdna-cli
123
+ curl -LO https://github.com/aikdna/kdna-assets/releases/download/agent-project-context-v0.1.2/agent-project-context-v0.1.2.kdna
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Related packages
129
+
130
+ | Package | Role |
131
+ |---------|------|
132
+ | [`@aikdna/kdna-core`](https://github.com/aikdna/kdna) | KDNA format and runtime |
133
+ | [`@aikdna/kdna-web-server`](https://github.com/aikdna/kdna-web-server) | Server-side adapter |
134
+ | [`@aikdna/kdna-react`](https://github.com/aikdna/kdna-react) | React components and hooks |
135
+
136
+ ---
137
+
138
+ ## License
139
+
140
+ Apache 2.0 — see [LICENSE](./LICENSE).
package/SECURITY.md ADDED
@@ -0,0 +1,38 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ Please **do not** report security vulnerabilities through public GitHub issues.
6
+
7
+ Instead, use one of these private channels:
8
+
9
+ - **GitHub Private Vulnerability Reporting**: Go to the [Security Advisories](https://github.com/aikdna/create-kdna-web-app/security/advisories/new) page
10
+ - **Email**: security@aikdna.com
11
+
12
+ We aim to respond within 72 hours and provide a timeline for resolution within 1 week.
13
+ Please do not disclose the vulnerability publicly until we have had a chance to address it.
14
+
15
+ ## Supported Versions
16
+
17
+ `create-kdna-web-app` is a pre-release scaffolder support surface. Until the
18
+ first stable package release, security support tracks the latest mainline
19
+ pre-release and the canonical KDNA protocol/runtime surfaces.
20
+
21
+ | Component | Supported Versions |
22
+ |-----------|-------------------|
23
+ | KDNA Protocol | Latest tagged release |
24
+ | kdna-cli | Latest minor release |
25
+ | create-kdna-web-app | Latest mainline pre-release |
26
+
27
+ Older pre-release versions may receive critical security patches on a
28
+ case-by-case basis.
29
+
30
+ ## Security Model
31
+
32
+ `create-kdna-web-app` scaffolds KDNA-integrated web applications. Generated
33
+ templates must consume the public KDNA package contracts rather than define
34
+ protocol validity, access modes, LoadPlan states, or crypto policy.
35
+
36
+ For the KDNA Protocol security architecture, see
37
+ [GOVERNANCE.md](https://github.com/aikdna/kdna/blob/main/docs/GOVERNANCE.md)
38
+ in the main protocol repository.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { main } = require('../src/scaffold');
5
+
6
+ main(process.argv.slice(2)).catch((error) => {
7
+ console.error(error.message);
8
+ process.exit(error.exitCode || 1);
9
+ });
@@ -0,0 +1,67 @@
1
+ # Getting started
2
+
3
+ This page walks through the first five minutes after running
4
+ `npx create-kdna-web-app`.
5
+
6
+ ---
7
+
8
+ ## Create a project
9
+
10
+ ```bash
11
+ npx create-kdna-web-app my-app
12
+ cd my-app
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Configure environment variables
18
+
19
+ ```bash
20
+ cp .env.local.example .env.local
21
+ ```
22
+
23
+ Open `.env.local`. The only required variable is `KDNA_STORAGE_DIR`:
24
+
25
+ ```bash
26
+ KDNA_STORAGE_DIR=/tmp/kdna
27
+ ```
28
+
29
+ `KDNA_ACTIVATION_URL` is optional. Leave it empty unless you are working
30
+ with licensed-mode assets and have a self-hosted activation server.
31
+
32
+ ---
33
+
34
+ ## Start the development server
35
+
36
+ ```bash
37
+ npm run dev
38
+ ```
39
+
40
+ Open [http://localhost:3000](http://localhost:3000).
41
+
42
+ ---
43
+
44
+ ## Try it with a real .kdna file
45
+
46
+ Install the KDNA CLI and download an example asset:
47
+
48
+ ```bash
49
+ npm install -g @aikdna/kdna-cli
50
+
51
+ curl -LO https://github.com/aikdna/kdna-assets/releases/download/agent-project-context-v0.1.2/agent-project-context-v0.1.2.kdna
52
+ ```
53
+
54
+ Drop the file onto the page. You should see:
55
+
56
+ 1. The manifest metadata (domain, version, title).
57
+ 2. A "Load" button or automatic loading for open assets.
58
+ 3. The formatted payload content.
59
+
60
+ ---
61
+
62
+ ## Next steps
63
+
64
+ - [Templates](./templates.md) — switch to Express or Pages Router
65
+ - [KDNA Core](https://github.com/aikdna/kdna) — understand the file format
66
+ - [kdna-web-server docs](https://github.com/aikdna/kdna-web-server) — add auth middleware, configure storage
67
+ - [kdna-react docs](https://github.com/aikdna/kdna-react) — customise the components
@@ -0,0 +1,30 @@
1
+ # Template checklist
2
+
3
+ Use this checklist when adding or changing a `create-kdna-web-app`
4
+ template.
5
+
6
+ ## Required files
7
+
8
+ - `package.json` with bounded dependency ranges, never `latest`.
9
+ - KDNA server route or server entrypoint.
10
+ - Browser demo page that exercises inspect, plan-load, and load.
11
+ - Environment example file for storage and activation settings.
12
+ - `scripts/smoke.mjs` that imports the KDNA package entrypoints used by
13
+ the generated project.
14
+
15
+ ## Runtime boundaries
16
+
17
+ - KDNA assets stay server-side after upload.
18
+ - Decryption and license verification happen through
19
+ `@aikdna/kdna-web-server` or a compatible server API.
20
+ - Raw license keys go to activation endpoints, not `/load`.
21
+ - Remote-server configuration is not included until the server adapter
22
+ implements and documents that mode.
23
+
24
+ ## Verification
25
+
26
+ - `npm test` passes in this repository.
27
+ - A generated project includes the expected route, page, env example, and
28
+ smoke script.
29
+ - The generated template package uses bounded dependency ranges.
30
+ - Public docs and package metadata mention only templates that exist.
@@ -0,0 +1,89 @@
1
+ # Templates
2
+
3
+ `create-kdna-web-app` ships three templates. Choose with `--template`.
4
+
5
+ ---
6
+
7
+ ## `nextjs` (default)
8
+
9
+ **When to use:** You are starting a new Next.js project or want the
10
+ most up-to-date template.
11
+
12
+ **Framework:** Next.js 14+ (App Router)
13
+
14
+ **What it includes:**
15
+
16
+ | File | Purpose |
17
+ |------|---------|
18
+ | `app/api/kdna/[...route]/route.js` | All KDNA endpoints (catch-all App Router route) |
19
+ | `app/page.jsx` | Demo page: file drop, inspect, load, display |
20
+ | `.env.local.example` | Environment variable template |
21
+
22
+ **Start:**
23
+
24
+ ```bash
25
+ npx create-kdna-web-app my-app --template nextjs
26
+ cd my-app && npm run dev
27
+ ```
28
+
29
+ ---
30
+
31
+ ## `nextjs-pages`
32
+
33
+ **When to use:** You are integrating into an existing Next.js Pages
34
+ Router project, or prefer the Pages Router.
35
+
36
+ **Framework:** Next.js (Pages Router)
37
+
38
+ **What it includes:**
39
+
40
+ | File | Purpose |
41
+ |------|---------|
42
+ | `pages/api/kdna/[...route].js` | All KDNA endpoints |
43
+ | `pages/index.jsx` | Demo page |
44
+ | `.env.local.example` | Environment variable template |
45
+
46
+ **Start:**
47
+
48
+ ```bash
49
+ npx create-kdna-web-app my-app --template nextjs-pages
50
+ cd my-app && npm run dev
51
+ ```
52
+
53
+ ---
54
+
55
+ ## `express`
56
+
57
+ **When to use:** You need a standalone Node.js server, are not using
58
+ Next.js, or are integrating into an existing Express application.
59
+
60
+ **Framework:** Express (ESM)
61
+
62
+ **What it includes:**
63
+
64
+ | File | Purpose |
65
+ |------|---------|
66
+ | `src/server.js` | Express server with KDNA router at `/api/kdna` |
67
+ | `public/index.html` | Minimal HTML demo page |
68
+ | `.env.example` | Environment variable template |
69
+
70
+ **Start:**
71
+
72
+ ```bash
73
+ npx create-kdna-web-app my-app --template express
74
+ cd my-app && npm start
75
+ # Open http://localhost:3000
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Choosing a template
81
+
82
+ | | `nextjs` | `nextjs-pages` | `express` |
83
+ |-|----------|----------------|-----------|
84
+ | KDNA API route included | Yes | Yes | Yes |
85
+ | Edge runtime (Vercel) | No* | No* | No |
86
+ | Browser demo included | React page | React page | Static HTML |
87
+ | React dependency included | Yes | Yes | No |
88
+
89
+ \* Use `@aikdna/kdna-web-server/cloudflare` for edge deployments.
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "create-kdna-web-app",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a KDNA-integrated web application with Next.js App Router, Next.js Pages Router, or Express templates.",
5
+ "type": "commonjs",
6
+ "bin": {
7
+ "create-kdna-web-app": "bin/create-kdna-web-app.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "docs/",
12
+ "src/",
13
+ "templates/",
14
+ "LICENSE",
15
+ "NOTICE",
16
+ "README.md",
17
+ "SECURITY.md"
18
+ ],
19
+ "scripts": {
20
+ "ci": "npm test && npm pack --dry-run --json",
21
+ "test": "node --test tests/**/*.test.js"
22
+ },
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "dependencies": {},
27
+ "license": "Apache-2.0",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/aikdna/create-kdna-web-app.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/aikdna/create-kdna-web-app/issues"
34
+ },
35
+ "homepage": "https://github.com/aikdna/create-kdna-web-app",
36
+ "keywords": [
37
+ "kdna",
38
+ "create-kdna-web-app",
39
+ "scaffold",
40
+ "nextjs",
41
+ "express",
42
+ "judgment-asset",
43
+ "starter"
44
+ ]
45
+ }
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { spawnSync } = require('node:child_process');
6
+
7
+ const VALID_TEMPLATES = new Set(['nextjs', 'nextjs-pages', 'express']);
8
+ const VALID_PACKAGE_MANAGERS = new Set(['npm', 'pnpm', 'yarn']);
9
+
10
+ function usage() {
11
+ return [
12
+ 'Usage: create-kdna-web-app <project-name> [options]',
13
+ '',
14
+ 'Options:',
15
+ ' --template <nextjs|nextjs-pages|express>',
16
+ ' --package-manager <npm|pnpm|yarn>',
17
+ ' --no-install',
18
+ ' --help',
19
+ ].join('\n');
20
+ }
21
+
22
+ function parseArgs(args) {
23
+ const options = {
24
+ projectName: null,
25
+ template: 'nextjs',
26
+ packageManager: null,
27
+ install: true,
28
+ };
29
+
30
+ for (let index = 0; index < args.length; index += 1) {
31
+ const arg = args[index];
32
+ if (arg === '--help' || arg === '-h') {
33
+ options.help = true;
34
+ } else if (arg === '--template') {
35
+ options.template = args[++index];
36
+ } else if (arg.startsWith('--template=')) {
37
+ options.template = arg.slice('--template='.length);
38
+ } else if (arg === '--package-manager') {
39
+ options.packageManager = args[++index];
40
+ } else if (arg.startsWith('--package-manager=')) {
41
+ options.packageManager = arg.slice('--package-manager='.length);
42
+ } else if (arg === '--no-install') {
43
+ options.install = false;
44
+ } else if (!options.projectName) {
45
+ options.projectName = arg;
46
+ } else {
47
+ const error = new Error(`Unknown argument: ${arg}\n\n${usage()}`);
48
+ error.exitCode = 1;
49
+ throw error;
50
+ }
51
+ }
52
+
53
+ return options;
54
+ }
55
+
56
+ function validateOptions(options) {
57
+ if (options.help) return;
58
+ if (!options.projectName) {
59
+ const error = new Error(`Project name is required.\n\n${usage()}`);
60
+ error.exitCode = 1;
61
+ throw error;
62
+ }
63
+ if (!VALID_TEMPLATES.has(options.template)) {
64
+ const error = new Error(`Unknown template: ${options.template}`);
65
+ error.exitCode = 1;
66
+ throw error;
67
+ }
68
+ if (options.packageManager && !VALID_PACKAGE_MANAGERS.has(options.packageManager)) {
69
+ const error = new Error(`Unknown package manager: ${options.packageManager}`);
70
+ error.exitCode = 1;
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ function detectPackageManager() {
76
+ const userAgent = process.env.npm_config_user_agent || '';
77
+ if (userAgent.startsWith('pnpm')) return 'pnpm';
78
+ if (userAgent.startsWith('yarn')) return 'yarn';
79
+ return 'npm';
80
+ }
81
+
82
+ function packageInstallCommand(packageManager) {
83
+ if (packageManager === 'pnpm') return ['pnpm', ['install']];
84
+ if (packageManager === 'yarn') return ['yarn', []];
85
+ return ['npm', ['install']];
86
+ }
87
+
88
+ function templateRoot(template) {
89
+ return path.join(__dirname, '..', 'templates', template);
90
+ }
91
+
92
+ function copyTemplate(srcDir, destDir, variables) {
93
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
94
+ const src = path.join(srcDir, entry.name);
95
+ const name = entry.name === '_gitignore' ? '.gitignore' : entry.name;
96
+ const dest = path.join(destDir, name);
97
+
98
+ if (entry.isDirectory()) {
99
+ fs.mkdirSync(dest, { recursive: true });
100
+ copyTemplate(src, dest, variables);
101
+ } else {
102
+ let content = fs.readFileSync(src, 'utf8');
103
+ for (const [key, value] of Object.entries(variables)) {
104
+ content = content.replaceAll(`{{${key}}}`, value);
105
+ }
106
+ fs.writeFileSync(dest, content);
107
+ }
108
+ }
109
+ }
110
+
111
+ function assertSafeProjectDir(projectDir) {
112
+ if (fs.existsSync(projectDir) && fs.readdirSync(projectDir).length > 0) {
113
+ const error = new Error(`Target directory is not empty: ${projectDir}`);
114
+ error.exitCode = 1;
115
+ throw error;
116
+ }
117
+ }
118
+
119
+ function scaffold(options) {
120
+ validateOptions(options);
121
+ if (options.help) {
122
+ return { help: usage() };
123
+ }
124
+
125
+ const packageManager = options.packageManager || detectPackageManager();
126
+ const projectDir = path.resolve(options.projectName);
127
+ const projectName = path.basename(projectDir);
128
+ assertSafeProjectDir(projectDir);
129
+ fs.mkdirSync(projectDir, { recursive: true });
130
+
131
+ copyTemplate(templateRoot(options.template), projectDir, {
132
+ projectName,
133
+ packageManager,
134
+ });
135
+
136
+ if (options.install) {
137
+ const [command, args] = packageInstallCommand(packageManager);
138
+ const result = spawnSync(command, args, { cwd: projectDir, stdio: 'inherit' });
139
+ if (result.status !== 0) {
140
+ const error = new Error(`${command} ${args.join(' ')} failed.`);
141
+ error.exitCode = result.status || 1;
142
+ throw error;
143
+ }
144
+ }
145
+
146
+ return { projectDir, projectName, template: options.template, packageManager, installed: options.install };
147
+ }
148
+
149
+ async function main(args) {
150
+ const options = parseArgs(args);
151
+ const result = scaffold(options);
152
+ if (result.help) {
153
+ console.log(result.help);
154
+ return result;
155
+ }
156
+ console.log(`Created ${result.projectName} with the ${result.template} template.`);
157
+ if (!result.installed) {
158
+ console.log(`Skipped install. Run: cd ${path.relative(process.cwd(), result.projectDir)} && ${result.packageManager} install`);
159
+ }
160
+ return result;
161
+ }
162
+
163
+ module.exports = {
164
+ VALID_TEMPLATES,
165
+ parseArgs,
166
+ scaffold,
167
+ main,
168
+ };
@@ -0,0 +1,2 @@
1
+ KDNA_STORAGE_DIR=/tmp/kdna-web
2
+ KDNA_ACTIVATION_URL=
@@ -0,0 +1,2 @@
1
+ node_modules
2
+ .env
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "node src/server.js",
7
+ "start": "node src/server.js",
8
+ "test": "node scripts/smoke.mjs"
9
+ },
10
+ "dependencies": {
11
+ "@aikdna/kdna-core": "^0.15.10",
12
+ "@aikdna/kdna-web-server": "^0.1.0",
13
+ "express": "^5.2.1"
14
+ }
15
+ }
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>KDNA Web App</title>
7
+ </head>
8
+ <body>
9
+ <main>
10
+ <input id="file" type="file" accept=".kdna">
11
+ <pre id="output">Choose a .kdna file</pre>
12
+ </main>
13
+ <script>
14
+ const file = document.querySelector('#file')
15
+ const output = document.querySelector('#output')
16
+ file.addEventListener('change', async () => {
17
+ const form = new FormData()
18
+ form.set('file', file.files[0])
19
+ const inspect = await fetch('/api/kdna/inspect', { method: 'POST', body: form }).then((r) => r.json())
20
+ const loaded = await fetch('/api/kdna/load', {
21
+ method: 'POST',
22
+ headers: { 'content-type': 'application/json' },
23
+ body: JSON.stringify({ fileId: inspect.fileId, profile: 'compact' }),
24
+ }).then((r) => r.json())
25
+ output.textContent = loaded.content || JSON.stringify(loaded, null, 2)
26
+ })
27
+ </script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,7 @@
1
+ import { createKDNARouter } from '@aikdna/kdna-web-server/express'
2
+
3
+ if (typeof createKDNARouter !== 'function') {
4
+ throw new Error('@aikdna/kdna-web-server/express did not export createKDNARouter')
5
+ }
6
+
7
+ console.log('KDNA Express template smoke passed')
@@ -0,0 +1,15 @@
1
+ import express from 'express'
2
+ import { createKDNARouter } from '@aikdna/kdna-web-server/express'
3
+
4
+ const app = express()
5
+ const port = Number(process.env.PORT || 3000)
6
+
7
+ app.use('/api/kdna', createKDNARouter({
8
+ storageDir: process.env.KDNA_STORAGE_DIR ?? '/tmp/kdna-web',
9
+ activationServerUrl: process.env.KDNA_ACTIVATION_URL,
10
+ }))
11
+ app.use(express.static('public'))
12
+
13
+ app.listen(port, () => {
14
+ console.log(`KDNA web app running on http://localhost:${port}`)
15
+ })
@@ -0,0 +1,2 @@
1
+ KDNA_STORAGE_DIR=/tmp/kdna-web
2
+ KDNA_ACTIVATION_URL=
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ .next
3
+ .env.local
@@ -0,0 +1,8 @@
1
+ import { createNextHandlers } from '@aikdna/kdna-web-server/nextjs'
2
+
3
+ const { GET, POST } = createNextHandlers({
4
+ storageDir: process.env.KDNA_STORAGE_DIR ?? '/tmp/kdna-web',
5
+ activationServerUrl: process.env.KDNA_ACTIVATION_URL,
6
+ })
7
+
8
+ export { GET, POST }
@@ -0,0 +1,42 @@
1
+ 'use client'
2
+
3
+ import {
4
+ KDNAAssetInspector,
5
+ KDNAFileDropzone,
6
+ KDNALoadPlanGate,
7
+ KDNAPasswordUnlockDialog,
8
+ } from '@aikdna/kdna-react'
9
+ import { useState } from 'react'
10
+
11
+ export default function Page() {
12
+ const [unlock, setUnlock] = useState(null)
13
+
14
+ return (
15
+ <main>
16
+ <KDNAFileDropzone endpoint="/api/kdna" onError={console.error}>
17
+ {({ fileId, inspect, loading }) => (
18
+ <section>
19
+ <p>{loading ? 'Uploading...' : 'Drop or choose a .kdna file'}</p>
20
+ {inspect ? <KDNAAssetInspector inspect={inspect} /> : null}
21
+ {fileId ? (
22
+ <KDNALoadPlanGate fileId={fileId} endpoint="/api/kdna">
23
+ {({ status, content }) => {
24
+ if (status === 'locked') {
25
+ return (
26
+ <KDNAPasswordUnlockDialog
27
+ fileId={fileId}
28
+ endpoint="/api/kdna"
29
+ onUnlock={(result) => setUnlock(result.content)}
30
+ />
31
+ )
32
+ }
33
+ return <pre>{unlock || content || status}</pre>
34
+ }}
35
+ </KDNALoadPlanGate>
36
+ ) : null}
37
+ </section>
38
+ )}
39
+ </KDNAFileDropzone>
40
+ </main>
41
+ )
42
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "test": "node scripts/smoke.mjs"
10
+ },
11
+ "dependencies": {
12
+ "@aikdna/kdna-core": "^0.15.10",
13
+ "@aikdna/kdna-react": "^0.1.0",
14
+ "@aikdna/kdna-web-server": "^0.1.0",
15
+ "next": "^16.2.10",
16
+ "react": "^19.2.7",
17
+ "react-dom": "^19.2.7"
18
+ }
19
+ }
@@ -0,0 +1,21 @@
1
+ import { createNextHandlers } from '@aikdna/kdna-web-server/nextjs'
2
+ import {
3
+ KDNAAssetInspector,
4
+ KDNAFileDropzone,
5
+ KDNALoadPlanGate,
6
+ KDNAPasswordUnlockDialog,
7
+ } from '@aikdna/kdna-react'
8
+
9
+ for (const [name, value] of Object.entries({
10
+ createNextHandlers,
11
+ KDNAAssetInspector,
12
+ KDNAFileDropzone,
13
+ KDNALoadPlanGate,
14
+ KDNAPasswordUnlockDialog,
15
+ })) {
16
+ if (typeof value !== 'function') {
17
+ throw new Error(`KDNA Next.js template dependency missing export: ${name}`)
18
+ }
19
+ }
20
+
21
+ console.log('KDNA Next.js template smoke passed')
@@ -0,0 +1,2 @@
1
+ KDNA_STORAGE_DIR=/tmp/kdna-web
2
+ KDNA_ACTIVATION_URL=
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ .next
3
+ .env.local
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "test": "node scripts/smoke.mjs"
10
+ },
11
+ "dependencies": {
12
+ "@aikdna/kdna-core": "^0.15.10",
13
+ "@aikdna/kdna-react": "^0.1.0",
14
+ "@aikdna/kdna-web-server": "^0.1.0",
15
+ "next": "^16.2.10",
16
+ "react": "^19.2.7",
17
+ "react-dom": "^19.2.7"
18
+ }
19
+ }
@@ -0,0 +1,17 @@
1
+ import { createKDNARouter } from '@aikdna/kdna-web-server/express'
2
+
3
+ export const config = {
4
+ api: {
5
+ bodyParser: false,
6
+ },
7
+ }
8
+
9
+ const handler = createKDNARouter({
10
+ basePath: '/api/kdna',
11
+ storageDir: process.env.KDNA_STORAGE_DIR ?? '/tmp/kdna-web',
12
+ activationServerUrl: process.env.KDNA_ACTIVATION_URL,
13
+ })
14
+
15
+ export default function kdna(req, res) {
16
+ return handler(req, res)
17
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ KDNAAssetInspector,
3
+ KDNAFileDropzone,
4
+ KDNALoadPlanGate,
5
+ KDNAPasswordUnlockDialog,
6
+ } from '@aikdna/kdna-react'
7
+ import { useState } from 'react'
8
+
9
+ export default function Page() {
10
+ const [unlock, setUnlock] = useState(null)
11
+
12
+ return (
13
+ <main>
14
+ <KDNAFileDropzone endpoint="/api/kdna" onError={console.error}>
15
+ {({ fileId, inspect, loading }) => (
16
+ <section>
17
+ <p>{loading ? 'Uploading...' : 'Drop or choose a .kdna file'}</p>
18
+ {inspect ? <KDNAAssetInspector inspect={inspect} /> : null}
19
+ {fileId ? (
20
+ <KDNALoadPlanGate fileId={fileId} endpoint="/api/kdna">
21
+ {({ status, content }) => {
22
+ if (status === 'locked') {
23
+ return (
24
+ <KDNAPasswordUnlockDialog
25
+ fileId={fileId}
26
+ endpoint="/api/kdna"
27
+ onUnlock={(result) => setUnlock(result.content)}
28
+ />
29
+ )
30
+ }
31
+ return <pre>{unlock || content || status}</pre>
32
+ }}
33
+ </KDNALoadPlanGate>
34
+ ) : null}
35
+ </section>
36
+ )}
37
+ </KDNAFileDropzone>
38
+ </main>
39
+ )
40
+ }
@@ -0,0 +1,21 @@
1
+ import { createKDNARouter } from '@aikdna/kdna-web-server/express'
2
+ import {
3
+ KDNAAssetInspector,
4
+ KDNAFileDropzone,
5
+ KDNALoadPlanGate,
6
+ KDNAPasswordUnlockDialog,
7
+ } from '@aikdna/kdna-react'
8
+
9
+ for (const [name, value] of Object.entries({
10
+ createKDNARouter,
11
+ KDNAAssetInspector,
12
+ KDNAFileDropzone,
13
+ KDNALoadPlanGate,
14
+ KDNAPasswordUnlockDialog,
15
+ })) {
16
+ if (typeof value !== 'function') {
17
+ throw new Error(`KDNA Next.js Pages template dependency missing export: ${name}`)
18
+ }
19
+ }
20
+
21
+ console.log('KDNA Next.js Pages template smoke passed')