@agent-native/core 0.4.1 → 0.4.2
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/README.md +31 -0
- package/dist/adapters/convex/adapter.d.ts +24 -0
- package/dist/adapters/convex/adapter.d.ts.map +1 -0
- package/dist/adapters/convex/adapter.js +125 -0
- package/dist/adapters/convex/adapter.js.map +1 -0
- package/dist/adapters/convex/index.d.ts +4 -0
- package/dist/adapters/convex/index.d.ts.map +1 -0
- package/dist/adapters/convex/index.js +3 -0
- package/dist/adapters/convex/index.js.map +1 -0
- package/dist/adapters/drizzle/adapter.d.ts +36 -0
- package/dist/adapters/drizzle/adapter.d.ts.map +1 -0
- package/dist/adapters/drizzle/adapter.js +210 -0
- package/dist/adapters/drizzle/adapter.js.map +1 -0
- package/dist/adapters/drizzle/index.d.ts +3 -0
- package/dist/adapters/drizzle/index.d.ts.map +1 -0
- package/dist/adapters/drizzle/index.js +3 -0
- package/dist/adapters/drizzle/index.js.map +1 -0
- package/dist/adapters/drizzle/schema.d.ts +146 -0
- package/dist/adapters/drizzle/schema.d.ts.map +1 -0
- package/dist/adapters/drizzle/schema.js +20 -0
- package/dist/adapters/drizzle/schema.js.map +1 -0
- package/dist/adapters/firestore/adapter.d.ts +3 -2
- package/dist/adapters/firestore/adapter.d.ts.map +1 -1
- package/dist/adapters/firestore/adapter.js +23 -6
- package/dist/adapters/firestore/adapter.js.map +1 -1
- package/dist/adapters/supabase/adapter.d.ts +2 -1
- package/dist/adapters/supabase/adapter.d.ts.map +1 -1
- package/dist/adapters/supabase/adapter.js +4 -1
- package/dist/adapters/supabase/adapter.js.map +1 -1
- package/dist/adapters/sync/config.d.ts +22 -2
- package/dist/adapters/sync/config.d.ts.map +1 -1
- package/dist/adapters/sync/config.js +175 -16
- package/dist/adapters/sync/config.js.map +1 -1
- package/dist/adapters/sync/create-file-sync.d.ts +32 -0
- package/dist/adapters/sync/create-file-sync.d.ts.map +1 -0
- package/dist/adapters/sync/create-file-sync.js +218 -0
- package/dist/adapters/sync/create-file-sync.js.map +1 -0
- package/dist/adapters/sync/file-sync.d.ts +40 -6
- package/dist/adapters/sync/file-sync.d.ts.map +1 -1
- package/dist/adapters/sync/file-sync.js +442 -97
- package/dist/adapters/sync/file-sync.js.map +1 -1
- package/dist/adapters/sync/index.d.ts +3 -2
- package/dist/adapters/sync/index.d.ts.map +1 -1
- package/dist/adapters/sync/index.js +3 -1
- package/dist/adapters/sync/index.js.map +1 -1
- package/dist/adapters/sync/merge.js +3 -2
- package/dist/adapters/sync/merge.js.map +1 -1
- package/dist/adapters/sync/types.d.ts +36 -2
- package/dist/adapters/sync/types.d.ts.map +1 -1
- package/dist/adapters/sync/types.js +22 -1
- package/dist/adapters/sync/types.js.map +1 -1
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +2 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/production-agent.d.ts +16 -0
- package/dist/agent/production-agent.d.ts.map +1 -0
- package/dist/agent/production-agent.js +152 -0
- package/dist/agent/production-agent.js.map +1 -0
- package/dist/agent/types.d.ts +38 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +2 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +2 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +37 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/client/PoweredByBadge.d.ts +14 -0
- package/dist/client/PoweredByBadge.d.ts.map +1 -0
- package/dist/client/PoweredByBadge.js +60 -0
- package/dist/client/PoweredByBadge.js.map +1 -0
- package/dist/client/ProductionAgentPanel.d.ts +10 -0
- package/dist/client/ProductionAgentPanel.d.ts.map +1 -0
- package/dist/client/ProductionAgentPanel.js +121 -0
- package/dist/client/ProductionAgentPanel.js.map +1 -0
- package/dist/client/Turnstile.d.ts +35 -0
- package/dist/client/Turnstile.d.ts.map +1 -0
- package/dist/client/Turnstile.js +77 -0
- package/dist/client/Turnstile.js.map +1 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/use-file-sync-status.d.ts +21 -0
- package/dist/client/use-file-sync-status.d.ts.map +1 -0
- package/dist/client/use-file-sync-status.js +65 -0
- package/dist/client/use-file-sync-status.js.map +1 -0
- package/dist/client/use-session.d.ts +16 -0
- package/dist/client/use-session.d.ts.map +1 -0
- package/dist/client/use-session.js +49 -0
- package/dist/client/use-session.js.map +1 -0
- package/dist/client/useProductionAgent.d.ts +18 -0
- package/dist/client/useProductionAgent.d.ts.map +1 -0
- package/dist/client/useProductionAgent.js +135 -0
- package/dist/client/useProductionAgent.js.map +1 -0
- package/dist/db/index.d.ts +21 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +17 -0
- package/dist/db/index.js.map +1 -0
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/router/index.d.ts +3 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +5 -0
- package/dist/router/index.js.map +1 -0
- package/dist/scripts/core-scripts.d.ts +10 -0
- package/dist/scripts/core-scripts.d.ts.map +1 -0
- package/dist/scripts/core-scripts.js +15 -0
- package/dist/scripts/core-scripts.js.map +1 -0
- package/dist/scripts/db/exec.d.ts +11 -0
- package/dist/scripts/db/exec.d.ts.map +1 -0
- package/dist/scripts/db/exec.js +86 -0
- package/dist/scripts/db/exec.js.map +1 -0
- package/dist/scripts/db/index.d.ts +2 -0
- package/dist/scripts/db/index.d.ts.map +1 -0
- package/dist/scripts/db/index.js +6 -0
- package/dist/scripts/db/index.js.map +1 -0
- package/dist/scripts/db/query.d.ts +10 -0
- package/dist/scripts/db/query.d.ts.map +1 -0
- package/dist/scripts/db/query.js +96 -0
- package/dist/scripts/db/query.js.map +1 -0
- package/dist/scripts/db/schema.d.ts +12 -0
- package/dist/scripts/db/schema.d.ts.map +1 -0
- package/dist/scripts/db/schema.js +112 -0
- package/dist/scripts/db/schema.js.map +1 -0
- package/dist/scripts/index.d.ts +4 -0
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +4 -0
- package/dist/scripts/index.js.map +1 -1
- package/dist/scripts/runner.d.ts +3 -0
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +53 -14
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/auth.d.ts +59 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +442 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/captcha.d.ts +12 -0
- package/dist/server/captcha.d.ts.map +1 -0
- package/dist/server/captcha.js +43 -0
- package/dist/server/captcha.js.map +1 -0
- package/dist/server/create-server.d.ts +13 -10
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +41 -27
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/index.d.ts +5 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +6 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/missing-key.d.ts +9 -5
- package/dist/server/missing-key.d.ts.map +1 -1
- package/dist/server/missing-key.js +12 -7
- package/dist/server/missing-key.js.map +1 -1
- package/dist/server/sse.d.ts +12 -7
- package/dist/server/sse.d.ts.map +1 -1
- package/dist/server/sse.js +79 -15
- package/dist/server/sse.js.map +1 -1
- package/dist/vite/client.d.ts +28 -5
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +36 -15
- package/dist/vite/client.js.map +1 -1
- package/dist/vite/dev-api-server.d.ts +10 -0
- package/dist/vite/dev-api-server.d.ts.map +1 -0
- package/dist/vite/dev-api-server.js +148 -0
- package/dist/vite/dev-api-server.js.map +1 -0
- package/dist/vite/index.d.ts +2 -3
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +2 -3
- package/dist/vite/index.js.map +1 -1
- package/package.json +26 -17
- package/src/templates/default/AGENTS.md +148 -22
- package/src/templates/default/_gitignore +9 -5
- package/src/templates/default/client/entry.client.tsx +4 -0
- package/src/templates/default/client/entry.server.tsx +55 -0
- package/src/templates/default/client/root.tsx +82 -0
- package/src/templates/default/client/routes/_index.tsx +19 -0
- package/src/templates/default/client/routes.ts +4 -0
- package/src/templates/default/client/vite-env.d.ts +5 -0
- package/src/templates/default/package.json +5 -7
- package/src/templates/default/react-router.config.ts +6 -0
- package/src/templates/default/server/lib/watcher.ts +21 -0
- package/src/templates/default/server/plugins/auth.ts +5 -0
- package/src/templates/default/server/plugins/file-sync.ts +39 -0
- package/src/templates/default/server/routes/[...page].get.ts +12 -0
- package/src/templates/default/server/routes/api/events.get.ts +7 -0
- package/src/templates/default/server/routes/api/file-sync/status.get.ts +13 -0
- package/src/templates/default/server/routes/api/hello.get.ts +5 -0
- package/src/templates/default/tsconfig.json +9 -1
- package/src/templates/default/vite.config.ts +4 -1
- package/tsconfig.base.json +3 -1
- package/dist/adapters/neon/adapter.d.ts +0 -28
- package/dist/adapters/neon/adapter.d.ts.map +0 -1
- package/dist/adapters/neon/adapter.js +0 -135
- package/dist/adapters/neon/adapter.js.map +0 -1
- package/dist/adapters/neon/index.d.ts +0 -3
- package/dist/adapters/neon/index.d.ts.map +0 -1
- package/dist/adapters/neon/index.js +0 -3
- package/dist/adapters/neon/index.js.map +0 -1
- package/dist/server/production.d.ts +0 -18
- package/dist/server/production.d.ts.map +0 -1
- package/dist/server/production.js +0 -37
- package/dist/server/production.js.map +0 -1
- package/dist/vite/express-plugin.d.ts +0 -14
- package/dist/vite/express-plugin.d.ts.map +0 -1
- package/dist/vite/express-plugin.js +0 -53
- package/dist/vite/express-plugin.js.map +0 -1
- package/dist/vite/server.d.ts +0 -21
- package/dist/vite/server.d.ts.map +0 -1
- package/dist/vite/server.js +0 -68
- package/dist/vite/server.js.map +0 -1
- package/src/templates/default/index.html +0 -14
- package/src/templates/default/server/index.ts +0 -22
- package/src/templates/default/server/node-build.ts +0 -4
- package/src/templates/default/vite.config.server.ts +0 -3
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { createApp, createRouter, toNodeListener } from "h3";
|
|
4
|
+
/**
|
|
5
|
+
* Map a Nitro-style route file path to { method, route }.
|
|
6
|
+
*
|
|
7
|
+
* Examples:
|
|
8
|
+
* api/emails/index.get.ts → GET /api/emails
|
|
9
|
+
* api/emails/[id].get.ts → GET /api/emails/:id
|
|
10
|
+
* api/emails/[id]/star.patch.ts→ PATCH /api/emails/:id/star
|
|
11
|
+
* api/events.get.ts → GET /api/events
|
|
12
|
+
*/
|
|
13
|
+
function parseRouteFile(relPath) {
|
|
14
|
+
// Strip .ts extension
|
|
15
|
+
const withoutExt = relPath.replace(/\.ts$/, "");
|
|
16
|
+
// Extract HTTP method from the last segment (e.g. "status.get" → method="get")
|
|
17
|
+
const dotIdx = withoutExt.lastIndexOf(".");
|
|
18
|
+
if (dotIdx === -1)
|
|
19
|
+
return null;
|
|
20
|
+
const method = withoutExt.slice(dotIdx + 1).toLowerCase();
|
|
21
|
+
const validMethods = ["get", "post", "put", "patch", "delete", "options"];
|
|
22
|
+
if (!validMethods.includes(method))
|
|
23
|
+
return null;
|
|
24
|
+
let routePath = withoutExt.slice(0, dotIdx);
|
|
25
|
+
// Replace [param] with :param
|
|
26
|
+
routePath = routePath.replace(/\[([^\]]+)\]/g, ":$1");
|
|
27
|
+
// Remove trailing /index
|
|
28
|
+
routePath = routePath.replace(/\/index$/, "");
|
|
29
|
+
// Ensure leading slash
|
|
30
|
+
if (!routePath.startsWith("/"))
|
|
31
|
+
routePath = "/" + routePath;
|
|
32
|
+
return { method, route: routePath };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Recursively discover all .ts files under a directory.
|
|
36
|
+
*/
|
|
37
|
+
function discoverFiles(dir, prefix = "") {
|
|
38
|
+
if (!fs.existsSync(dir))
|
|
39
|
+
return [];
|
|
40
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
41
|
+
const files = [];
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
files.push(...discoverFiles(path.join(dir, entry.name), rel));
|
|
46
|
+
}
|
|
47
|
+
else if (entry.name.endsWith(".ts")) {
|
|
48
|
+
files.push(rel);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return files;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Load all API routes and server plugins, return an H3 listener.
|
|
55
|
+
*/
|
|
56
|
+
async function buildApiListener(server, cwd) {
|
|
57
|
+
const apiDir = path.join(cwd, "server/routes/api");
|
|
58
|
+
const pluginsDir = path.join(cwd, "server/plugins");
|
|
59
|
+
const app = createApp();
|
|
60
|
+
const router = createRouter();
|
|
61
|
+
app.use(router);
|
|
62
|
+
// Discover and register API route files
|
|
63
|
+
const routeFiles = discoverFiles(apiDir, "api");
|
|
64
|
+
let registered = 0;
|
|
65
|
+
for (const relFile of routeFiles) {
|
|
66
|
+
const parsed = parseRouteFile(relFile);
|
|
67
|
+
if (!parsed)
|
|
68
|
+
continue;
|
|
69
|
+
const absPath = path.join(cwd, "server/routes", relFile);
|
|
70
|
+
try {
|
|
71
|
+
const mod = await server.ssrLoadModule(`/${path.relative(cwd, absPath)}`);
|
|
72
|
+
const handler = mod.default;
|
|
73
|
+
if (typeof handler !== "function")
|
|
74
|
+
continue;
|
|
75
|
+
const routerMethod = router[parsed.method];
|
|
76
|
+
if (typeof routerMethod === "function") {
|
|
77
|
+
routerMethod.call(router, parsed.route, handler);
|
|
78
|
+
registered++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
console.warn(`[dev-api-server] Failed to load route ${relFile}:`, e.message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Run server plugins (auth, file-sync, etc.)
|
|
86
|
+
if (fs.existsSync(pluginsDir)) {
|
|
87
|
+
const pluginFiles = fs
|
|
88
|
+
.readdirSync(pluginsDir)
|
|
89
|
+
.filter((f) => f.endsWith(".ts"))
|
|
90
|
+
.sort();
|
|
91
|
+
for (const file of pluginFiles) {
|
|
92
|
+
try {
|
|
93
|
+
const absPath = path.join(pluginsDir, file);
|
|
94
|
+
const mod = await server.ssrLoadModule(`/${path.relative(cwd, absPath)}`);
|
|
95
|
+
const plugin = mod.default;
|
|
96
|
+
if (typeof plugin === "function") {
|
|
97
|
+
await plugin({ h3App: app });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
console.warn(`[dev-api-server] Failed to load plugin ${file}:`, e.message);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
console.log(`[dev-api-server] ${registered} API routes registered`);
|
|
106
|
+
return toNodeListener(app);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Vite plugin that serves H3 API routes from server/routes/api/ during dev.
|
|
110
|
+
*
|
|
111
|
+
* This replaces the disabled Nitro Vite plugin for dev-mode API route serving.
|
|
112
|
+
* It scans the file-based routes, imports them, and mounts them on an H3 app
|
|
113
|
+
* that handles /api/* requests before React Router's SSR handler.
|
|
114
|
+
*/
|
|
115
|
+
export function devApiServer() {
|
|
116
|
+
return {
|
|
117
|
+
name: "agent-native-dev-api-server",
|
|
118
|
+
apply: "serve",
|
|
119
|
+
configureServer(server) {
|
|
120
|
+
const cwd = server.config.root || process.cwd();
|
|
121
|
+
const apiDir = path.join(cwd, "server/routes/api");
|
|
122
|
+
// Skip if no API routes directory exists
|
|
123
|
+
if (!fs.existsSync(apiDir))
|
|
124
|
+
return;
|
|
125
|
+
// Lazily initialize the H3 listener on first /api/ request.
|
|
126
|
+
// This avoids blocking server startup and ensures ssrLoadModule is ready.
|
|
127
|
+
let listenerPromise = null;
|
|
128
|
+
// Add middleware DIRECTLY (not via return) so it runs BEFORE
|
|
129
|
+
// Vite's internal middleware and React Router's SSR handler.
|
|
130
|
+
server.middlewares.use((req, res, next) => {
|
|
131
|
+
if (!req.url?.startsWith("/api/")) {
|
|
132
|
+
return next();
|
|
133
|
+
}
|
|
134
|
+
if (!listenerPromise) {
|
|
135
|
+
listenerPromise = buildApiListener(server, cwd);
|
|
136
|
+
}
|
|
137
|
+
listenerPromise
|
|
138
|
+
.then((listener) => listener(req, res))
|
|
139
|
+
.catch((err) => {
|
|
140
|
+
console.error("[dev-api-server] Error handling request:", err);
|
|
141
|
+
res.statusCode = 500;
|
|
142
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=dev-api-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-api-server.js","sourceRoot":"","sources":["../../src/vite/dev-api-server.ts"],"names":[],"mappings":"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"}
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { defineConfig, type ClientConfigOptions } from "./client.js";
|
|
2
|
-
export {
|
|
3
|
-
export { expressPlugin, type ExpressPluginOptions } from "./express-plugin.js";
|
|
1
|
+
export { defineConfig, type ClientConfigOptions, type NitroOptions, } from "./client.js";
|
|
2
|
+
export { devApiServer } from "./dev-api-server.js";
|
|
4
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/vite/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/vite/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { defineConfig } from "./client.js";
|
|
2
|
-
export {
|
|
3
|
-
export { expressPlugin } from "./express-plugin.js";
|
|
1
|
+
export { defineConfig, } from "./client.js";
|
|
2
|
+
export { devApiServer } from "./dev-api-server.js";
|
|
4
3
|
//# sourceMappingURL=index.js.map
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAGb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Framework for agent-native application development — where AI agents and UI share state via files",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,14 +19,17 @@
|
|
|
19
19
|
},
|
|
20
20
|
"./vite": "./dist/vite/index.js",
|
|
21
21
|
"./server": "./dist/server/index.js",
|
|
22
|
+
"./db": "./dist/db/index.js",
|
|
22
23
|
"./client": "./dist/client/index.js",
|
|
23
24
|
"./shared": "./dist/shared/index.js",
|
|
24
25
|
"./scripts": "./dist/scripts/index.js",
|
|
25
26
|
"./adapters/sync": "./dist/adapters/sync/index.js",
|
|
27
|
+
"./adapters/drizzle": "./dist/adapters/drizzle/index.js",
|
|
26
28
|
"./adapters/firestore": "./dist/adapters/firestore/index.js",
|
|
27
29
|
"./adapters/supabase": "./dist/adapters/supabase/index.js",
|
|
28
|
-
"./adapters/
|
|
30
|
+
"./adapters/convex": "./dist/adapters/convex/index.js",
|
|
29
31
|
"./adapters/cli": "./dist/adapters/cli/index.js",
|
|
32
|
+
"./router": "./dist/router/index.js",
|
|
30
33
|
"./a2a": "./dist/a2a/index.js",
|
|
31
34
|
"./tailwind": "./dist/tailwind.preset.js",
|
|
32
35
|
"./tsconfig.base.json": "./tsconfig.base.json"
|
|
@@ -46,23 +49,31 @@
|
|
|
46
49
|
"release": "npm version patch && npm publish --access public"
|
|
47
50
|
},
|
|
48
51
|
"dependencies": {
|
|
52
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
53
|
+
"@react-router/dev": "^7.13.1",
|
|
54
|
+
"@react-router/fs-routes": "^7.13.1",
|
|
55
|
+
"better-sqlite3": "^12.8.0",
|
|
49
56
|
"chokidar": "^4.0.0",
|
|
50
57
|
"clsx": "^2.1.1",
|
|
51
58
|
"dotenv": "^17.2.1",
|
|
59
|
+
"drizzle-orm": "^0.38.0",
|
|
60
|
+
"h3": "^1.14.0",
|
|
52
61
|
"minimatch": "^10.0.0",
|
|
62
|
+
"nitro": "^3.0.0",
|
|
63
|
+
"p-limit": "^7.3.0",
|
|
64
|
+
"react-router": "^7.13.1",
|
|
53
65
|
"tailwind-merge": "^2.6.0"
|
|
54
66
|
},
|
|
55
67
|
"peerDependencies": {
|
|
56
68
|
"@supabase/supabase-js": ">=2",
|
|
57
|
-
"@neondatabase/serverless": ">=0.9",
|
|
58
69
|
"@tanstack/react-query": ">=5",
|
|
59
70
|
"@vitejs/plugin-react-swc": ">=4",
|
|
60
71
|
"autoprefixer": ">=10",
|
|
61
|
-
"
|
|
62
|
-
"express": ">=5",
|
|
72
|
+
"convex": ">=1",
|
|
63
73
|
"postcss": ">=8",
|
|
64
74
|
"react": ">=18",
|
|
65
75
|
"react-dom": ">=18",
|
|
76
|
+
"react-router": ">=7",
|
|
66
77
|
"tailwindcss": ">=3",
|
|
67
78
|
"tailwindcss-animate": ">=1",
|
|
68
79
|
"typescript": ">=5",
|
|
@@ -84,13 +95,10 @@
|
|
|
84
95
|
"postcss": {
|
|
85
96
|
"optional": true
|
|
86
97
|
},
|
|
87
|
-
"
|
|
88
|
-
"optional": true
|
|
89
|
-
},
|
|
90
|
-
"express": {
|
|
98
|
+
"@vitejs/plugin-react-swc": {
|
|
91
99
|
"optional": true
|
|
92
100
|
},
|
|
93
|
-
"
|
|
101
|
+
"react-router": {
|
|
94
102
|
"optional": true
|
|
95
103
|
},
|
|
96
104
|
"vite": {
|
|
@@ -99,25 +107,26 @@
|
|
|
99
107
|
"@supabase/supabase-js": {
|
|
100
108
|
"optional": true
|
|
101
109
|
},
|
|
102
|
-
"
|
|
110
|
+
"convex": {
|
|
103
111
|
"optional": true
|
|
104
112
|
}
|
|
105
113
|
},
|
|
106
114
|
"devDependencies": {
|
|
115
|
+
"@react-router/dev": "^7.13.1",
|
|
116
|
+
"@react-router/fs-routes": "^7.13.1",
|
|
117
|
+
"@supabase/supabase-js": "^2.49.0",
|
|
107
118
|
"@tanstack/react-query": "^5.84.2",
|
|
108
|
-
"@types/
|
|
109
|
-
"@types/express": "^5.0.
|
|
119
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
120
|
+
"@types/express": "^5.0.6",
|
|
110
121
|
"@types/node": "^24.2.1",
|
|
111
122
|
"@types/react": "^18.3.23",
|
|
112
123
|
"@vitejs/plugin-react-swc": "^4.0.0",
|
|
113
124
|
"autoprefixer": "^10.4.21",
|
|
114
|
-
"
|
|
115
|
-
"express": "^5.1.0",
|
|
116
|
-
"@neondatabase/serverless": "^1.0.0",
|
|
117
|
-
"@supabase/supabase-js": "^2.49.0",
|
|
125
|
+
"express": "^5.2.1",
|
|
118
126
|
"firebase-admin": "^13.0.0",
|
|
119
127
|
"postcss": "^8.5.6",
|
|
120
128
|
"react": "^18.3.1",
|
|
129
|
+
"react-router": "^7.13.1",
|
|
121
130
|
"tailwindcss": "^3.4.17",
|
|
122
131
|
"tailwindcss-animate": "^1.0.7",
|
|
123
132
|
"typescript": "^5.9.2",
|
|
@@ -12,28 +12,46 @@ This is an **@agent-native/core** application — the AI agent and UI share stat
|
|
|
12
12
|
4. **Bidirectional SSE events** — The file watcher keeps the UI in sync when the agent modifies files.
|
|
13
13
|
5. **Agent can update code** — The agent can modify this app's source code directly.
|
|
14
14
|
|
|
15
|
-
###
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
client/ # React frontend (Vite SPA)
|
|
19
|
-
App.tsx # Entry point
|
|
20
|
-
components/ # UI components
|
|
21
|
-
hooks/ # React hooks
|
|
22
|
-
lib/ # Utilities (cn, etc)
|
|
15
|
+
### Authentication
|
|
23
16
|
|
|
24
|
-
server/
|
|
25
|
-
index.ts # createAppServer() — routes + middleware
|
|
26
|
-
node-build.ts # Production entry point
|
|
17
|
+
Auth is automatic and environment-driven. The `server/plugins/auth.ts` plugin calls `autoMountAuth(app)` at startup.
|
|
27
18
|
|
|
28
|
-
|
|
19
|
+
- **Dev mode**: Auth is bypassed. `getSession()` returns `{ email: "local@localhost" }`. Zero friction.
|
|
20
|
+
- **Production** (`ACCESS_TOKEN` set): Auth middleware auto-mounts. Login page for unauthenticated visitors.
|
|
21
|
+
- **Production** (no token, no `AUTH_DISABLED=true`): Server refuses to start.
|
|
29
22
|
|
|
30
|
-
|
|
31
|
-
run.ts # Script dispatcher
|
|
32
|
-
*.ts # Individual scripts (pnpm script <name>)
|
|
23
|
+
Use `getSession(event)` server-side and `useSession()` client-side. See [docs/auth.md](docs/auth.md).
|
|
33
24
|
|
|
34
|
-
|
|
25
|
+
### Directory Structure
|
|
35
26
|
|
|
36
|
-
|
|
27
|
+
```
|
|
28
|
+
client/ # React frontend
|
|
29
|
+
root.tsx # HTML shell + global providers
|
|
30
|
+
entry.client.tsx # Client hydration entry
|
|
31
|
+
routes.ts # Route config — flatRoutes()
|
|
32
|
+
routes/ # File-based page routes (auto-discovered)
|
|
33
|
+
_index.tsx # / (home page)
|
|
34
|
+
components/ # UI components
|
|
35
|
+
hooks/ # React hooks
|
|
36
|
+
lib/ # Utilities (cn, etc)
|
|
37
|
+
|
|
38
|
+
server/ # Nitro API server
|
|
39
|
+
routes/
|
|
40
|
+
api/ # File-based API routes (auto-discovered)
|
|
41
|
+
[...page].get.ts # SSR catch-all (delegates to React Router)
|
|
42
|
+
plugins/ # Server plugins (startup logic)
|
|
43
|
+
lib/ # Shared server modules
|
|
44
|
+
|
|
45
|
+
shared/ # Isomorphic code (imported by both client & server)
|
|
46
|
+
|
|
47
|
+
scripts/ # Agent-callable scripts
|
|
48
|
+
run.ts # Script dispatcher
|
|
49
|
+
*.ts # Individual scripts (pnpm script <name>)
|
|
50
|
+
|
|
51
|
+
data/ # App data files (watched by SSE)
|
|
52
|
+
|
|
53
|
+
react-router.config.ts # React Router framework config
|
|
54
|
+
.agents/skills/ # Agent skills — detailed guidance for each rule
|
|
37
55
|
```
|
|
38
56
|
|
|
39
57
|
## Learnings & Preferences
|
|
@@ -64,10 +82,79 @@ Skills in `.agents/skills/` provide detailed guidance for each architectural rul
|
|
|
64
82
|
|
|
65
83
|
The **`frontend-design`** skill (sourced from [Anthropic's skills library](https://github.com/anthropics/skills/blob/main/skills/frontend-design/SKILL.md)) enforces distinctive, production-grade aesthetics — committing to a clear visual direction and avoiding generic patterns like purple gradients, overused fonts, and cookie-cutter layouts.
|
|
66
84
|
|
|
85
|
+
### Framework Basics
|
|
86
|
+
|
|
87
|
+
**SSR-first framework, CSR-by-default content:** This app uses React Router v7 framework mode with `ssr: true`. But virtually every route renders only an SSR shell (loading spinner + meta tags). All real data fetching and rendering happens on the client via React Query hooks. Server-side data fetching is the exception — only used for public pages that need SEO/og tags.
|
|
88
|
+
|
|
89
|
+
**Adding a page:**
|
|
90
|
+
Create a file in `client/routes/`. The filename determines the URL path:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
client/routes/_index.tsx → /
|
|
94
|
+
client/routes/settings.tsx → /settings
|
|
95
|
+
client/routes/inbox.tsx → /inbox
|
|
96
|
+
client/routes/inbox.$threadId.tsx → /inbox/:threadId
|
|
97
|
+
client/routes/$id.tsx → /:id (dynamic param)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Each route file exports a default component, optional `meta()`, and optional `HydrateFallback()`:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import MyPage from "@/pages/MyPage";
|
|
104
|
+
|
|
105
|
+
export function meta() {
|
|
106
|
+
return [{ title: "My Page" }];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function HydrateFallback() {
|
|
110
|
+
return (
|
|
111
|
+
<div className="flex items-center justify-center h-screen">
|
|
112
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-foreground" />
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default function MyPageRoute() {
|
|
118
|
+
return <MyPage />;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Do NOT fetch data server-side** in route loaders unless the page genuinely needs SEO content or og tags based on dynamic data. The standard pattern is: SSR renders a loading spinner, client hydrates, React Query hooks fetch from `/api/*`.
|
|
123
|
+
|
|
67
124
|
### Key Patterns
|
|
68
125
|
|
|
69
126
|
**Adding an API route:**
|
|
70
|
-
|
|
127
|
+
Create a file in `server/routes/api/`. The filename determines the URL path and HTTP method:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
server/routes/api/items/index.get.ts → GET /api/items
|
|
131
|
+
server/routes/api/items/[id].get.ts → GET /api/items/:id
|
|
132
|
+
server/routes/api/items/[id].patch.ts → PATCH /api/items/:id
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Each file exports a default `defineEventHandler`.
|
|
136
|
+
|
|
137
|
+
**Adding a server plugin:**
|
|
138
|
+
Startup logic (file watcher, file sync, auth) lives in `server/plugins/`. Use `defineNitroPlugin` from core:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { defineNitroPlugin } from "@agent-native/core";
|
|
142
|
+
|
|
143
|
+
export default defineNitroPlugin(async (nitroApp) => {
|
|
144
|
+
// Runs once at server startup
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Key imports from `@agent-native/core`:**
|
|
149
|
+
|
|
150
|
+
| Import | Purpose |
|
|
151
|
+
| -------------------------------------------- | ------------------------------------------------- |
|
|
152
|
+
| `defineNitroPlugin` | Define a server plugin (re-exported from Nitro) |
|
|
153
|
+
| `createFileWatcher` | Watch data directory for changes |
|
|
154
|
+
| `createSSEHandler` | Create SSE endpoint for real-time updates |
|
|
155
|
+
| `defineEventHandler`, `readBody`, `getQuery` | H3 route handler utilities (re-exported) |
|
|
156
|
+
| `sendToAgentChat` | Send messages to agent from UI (client-side) |
|
|
157
|
+
| `agentChat` | Send messages to agent from scripts (server-side) |
|
|
71
158
|
|
|
72
159
|
**Adding a script:**
|
|
73
160
|
Create `scripts/my-script.ts` exporting `default async function(args: string[])`.
|
|
@@ -91,11 +178,50 @@ import { agentChat } from "@agent-native/core";
|
|
|
91
178
|
agentChat.submit("Generate something");
|
|
92
179
|
```
|
|
93
180
|
|
|
181
|
+
### File Sync (Multi-User Collaboration)
|
|
182
|
+
|
|
183
|
+
File sync is **opt-in** — enabled when `FILE_SYNC_ENABLED=true` is set in `.env`.
|
|
184
|
+
|
|
185
|
+
**Environment variables:**
|
|
186
|
+
|
|
187
|
+
| Variable | Required | Description |
|
|
188
|
+
| -------------------------------- | ------------- | ---------------------------------------------------- |
|
|
189
|
+
| `FILE_SYNC_ENABLED` | No | Set to `"true"` to enable sync |
|
|
190
|
+
| `FILE_SYNC_BACKEND` | When enabled | `"firestore"`, `"supabase"`, or `"convex"` |
|
|
191
|
+
| `SUPABASE_URL` | For Supabase | Project URL |
|
|
192
|
+
| `SUPABASE_PUBLISHABLE_KEY` | For Supabase | Publishable key (or legacy `SUPABASE_ANON_KEY`) |
|
|
193
|
+
| `GOOGLE_APPLICATION_CREDENTIALS` | For Firestore | Path to service account JSON |
|
|
194
|
+
| `CONVEX_URL` | For Convex | Deployment URL from `npx convex dev` (must be HTTPS) |
|
|
195
|
+
|
|
196
|
+
**How sync works:**
|
|
197
|
+
|
|
198
|
+
- `createFileSync()` factory in `server/plugins/file-sync.ts` reads env vars and initializes sync
|
|
199
|
+
- Files matching `sync-config.json` patterns are synced to/from the remote database
|
|
200
|
+
- Sync events flow through SSE (`source: "sync"`) alongside file change events
|
|
201
|
+
- Conflicts produce `.conflict` sidecar files and notify the agent
|
|
202
|
+
|
|
203
|
+
**Checking sync status:**
|
|
204
|
+
|
|
205
|
+
- Read `data/.sync-status.json` for current sync state (connected, conflicts, retry queue)
|
|
206
|
+
- Read `data/.sync-failures.json` for permanently failed sync operations
|
|
207
|
+
|
|
208
|
+
**Handling conflicts:**
|
|
209
|
+
|
|
210
|
+
- When `application-state/sync-conflict.json` appears, a sync conflict needs resolution
|
|
211
|
+
- Read the `.conflict` file alongside the original to understand both versions
|
|
212
|
+
- Edit the original file to resolve, then delete the `.conflict` file
|
|
213
|
+
|
|
214
|
+
**Scratch files (not synced):**
|
|
215
|
+
|
|
216
|
+
- Prefix temporary files with `_tmp-` (e.g., `data/_tmp-scratch.json`) to exclude from sync
|
|
217
|
+
|
|
94
218
|
### Tech Stack
|
|
95
219
|
|
|
96
|
-
- **Framework:** @agent-native/core
|
|
220
|
+
- **Framework:** @agent-native/core + React Router v7 (framework mode)
|
|
97
221
|
- **Frontend:** React 18, Vite, TailwindCSS, shadcn/ui
|
|
98
|
-
- **
|
|
222
|
+
- **Routing:** File-based via `flatRoutes()` — SSR shell + client rendering
|
|
223
|
+
- **Backend:** Nitro (via @agent-native/core) — file-based API routing, server plugins, deploy-anywhere presets
|
|
99
224
|
- **State:** File-based (SSE for real-time updates)
|
|
100
|
-
- **Build:** `pnpm build` (client
|
|
101
|
-
- **Dev:** `pnpm dev` (Vite dev server with
|
|
225
|
+
- **Build:** `pnpm build` (React Router build — client + SSR + Nitro server)
|
|
226
|
+
- **Dev:** `pnpm dev` (Vite dev server with both React Router + Nitro plugins)
|
|
227
|
+
- **Start:** `node .output/server/index.mjs` (production)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Router generated types
|
|
2
|
+
.react-router/
|
|
3
|
+
|
|
4
|
+
# Logs
|
|
2
5
|
logs
|
|
3
6
|
*.log
|
|
4
7
|
npm-debug.log*
|
|
@@ -25,9 +28,10 @@ dist-ssr
|
|
|
25
28
|
.env.*
|
|
26
29
|
!.env.example
|
|
27
30
|
|
|
28
|
-
# Data
|
|
29
|
-
data/uploads/
|
|
30
|
-
data/settings.json
|
|
31
|
+
# Data
|
|
32
|
+
data/uploads/
|
|
33
|
+
data/settings.json
|
|
34
|
+
data/.sessions.json
|
|
31
35
|
|
|
32
36
|
# Learnings (personal preferences and memory — use learnings.defaults.md for tracked defaults)
|
|
33
|
-
learnings.md
|
|
37
|
+
learnings.md
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AppLoadContext, EntryContext } from "react-router";
|
|
2
|
+
import { ServerRouter } from "react-router";
|
|
3
|
+
import ReactDOMServer from "react-dom/server.browser";
|
|
4
|
+
const { renderToReadableStream } = ReactDOMServer;
|
|
5
|
+
import { isbot } from "isbot";
|
|
6
|
+
|
|
7
|
+
export const streamTimeout = 5_000;
|
|
8
|
+
|
|
9
|
+
export default async function handleRequest(
|
|
10
|
+
request: Request,
|
|
11
|
+
responseStatusCode: number,
|
|
12
|
+
responseHeaders: Headers,
|
|
13
|
+
routerContext: EntryContext,
|
|
14
|
+
_loadContext: AppLoadContext,
|
|
15
|
+
) {
|
|
16
|
+
if (request.method.toUpperCase() === "HEAD") {
|
|
17
|
+
return new Response(null, {
|
|
18
|
+
status: responseStatusCode,
|
|
19
|
+
headers: responseHeaders,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const userAgent = request.headers.get("user-agent");
|
|
24
|
+
const waitForAll = (userAgent && isbot(userAgent)) || routerContext.isSpaMode;
|
|
25
|
+
|
|
26
|
+
const abortController = new AbortController();
|
|
27
|
+
const timeoutId = setTimeout(() => abortController.abort(), streamTimeout);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const body = await renderToReadableStream(
|
|
31
|
+
<ServerRouter context={routerContext} url={request.url} />,
|
|
32
|
+
{
|
|
33
|
+
signal: abortController.signal,
|
|
34
|
+
onError(error: unknown) {
|
|
35
|
+
if (!abortController.signal.aborted) {
|
|
36
|
+
responseStatusCode = 500;
|
|
37
|
+
console.error(error);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (waitForAll) {
|
|
44
|
+
await body.allReady;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
responseHeaders.set("Content-Type", "text/html");
|
|
48
|
+
return new Response(body, {
|
|
49
|
+
headers: responseHeaders,
|
|
50
|
+
status: responseStatusCode,
|
|
51
|
+
});
|
|
52
|
+
} finally {
|
|
53
|
+
clearTimeout(timeoutId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Links,
|
|
3
|
+
Meta,
|
|
4
|
+
Outlet,
|
|
5
|
+
Scripts,
|
|
6
|
+
ScrollRestoration,
|
|
7
|
+
isRouteErrorResponse,
|
|
8
|
+
} from "react-router";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import {
|
|
11
|
+
QueryClient,
|
|
12
|
+
QueryClientProvider,
|
|
13
|
+
useQueryClient,
|
|
14
|
+
} from "@tanstack/react-query";
|
|
15
|
+
import { useFileWatcher } from "@agent-native/core";
|
|
16
|
+
import "./global.css";
|
|
17
|
+
|
|
18
|
+
export function Layout({ children }: { children: React.ReactNode }) {
|
|
19
|
+
return (
|
|
20
|
+
<html lang="en" className="dark">
|
|
21
|
+
<head>
|
|
22
|
+
<meta charSet="utf-8" />
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
24
|
+
<Meta />
|
|
25
|
+
<Links />
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
{children}
|
|
29
|
+
<ScrollRestoration />
|
|
30
|
+
<Scripts />
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function FileWatcher({ children }: { children: React.ReactNode }) {
|
|
37
|
+
const qc = useQueryClient();
|
|
38
|
+
useFileWatcher({ queryClient: qc, queryKeys: ["files"] });
|
|
39
|
+
return <>{children}</>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default function Root() {
|
|
43
|
+
const [queryClient] = useState(() => new QueryClient());
|
|
44
|
+
return (
|
|
45
|
+
<QueryClientProvider client={queryClient}>
|
|
46
|
+
<FileWatcher>
|
|
47
|
+
<Outlet />
|
|
48
|
+
</FileWatcher>
|
|
49
|
+
</QueryClientProvider>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function ErrorBoundary({ error }: { error: unknown }) {
|
|
54
|
+
let message = "Oops!";
|
|
55
|
+
let details = "An unexpected error occurred.";
|
|
56
|
+
let stack: string | undefined;
|
|
57
|
+
|
|
58
|
+
if (isRouteErrorResponse(error)) {
|
|
59
|
+
message = error.status === 404 ? "404" : "Error";
|
|
60
|
+
details =
|
|
61
|
+
error.status === 404
|
|
62
|
+
? "The requested page could not be found."
|
|
63
|
+
: error.statusText || details;
|
|
64
|
+
} else if (import.meta.env.DEV && error instanceof Error) {
|
|
65
|
+
details = error.message;
|
|
66
|
+
stack = error.stack;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<main className="flex items-center justify-center min-h-screen p-4">
|
|
71
|
+
<div className="text-center">
|
|
72
|
+
<h1 className="text-4xl font-bold mb-2">{message}</h1>
|
|
73
|
+
<p className="text-muted-foreground">{details}</p>
|
|
74
|
+
{stack && (
|
|
75
|
+
<pre className="mt-4 text-left text-xs overflow-auto max-w-lg mx-auto p-4 bg-muted rounded">
|
|
76
|
+
<code>{stack}</code>
|
|
77
|
+
</pre>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</main>
|
|
81
|
+
);
|
|
82
|
+
}
|