@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.
Files changed (221) hide show
  1. package/README.md +31 -0
  2. package/dist/adapters/convex/adapter.d.ts +24 -0
  3. package/dist/adapters/convex/adapter.d.ts.map +1 -0
  4. package/dist/adapters/convex/adapter.js +125 -0
  5. package/dist/adapters/convex/adapter.js.map +1 -0
  6. package/dist/adapters/convex/index.d.ts +4 -0
  7. package/dist/adapters/convex/index.d.ts.map +1 -0
  8. package/dist/adapters/convex/index.js +3 -0
  9. package/dist/adapters/convex/index.js.map +1 -0
  10. package/dist/adapters/drizzle/adapter.d.ts +36 -0
  11. package/dist/adapters/drizzle/adapter.d.ts.map +1 -0
  12. package/dist/adapters/drizzle/adapter.js +210 -0
  13. package/dist/adapters/drizzle/adapter.js.map +1 -0
  14. package/dist/adapters/drizzle/index.d.ts +3 -0
  15. package/dist/adapters/drizzle/index.d.ts.map +1 -0
  16. package/dist/adapters/drizzle/index.js +3 -0
  17. package/dist/adapters/drizzle/index.js.map +1 -0
  18. package/dist/adapters/drizzle/schema.d.ts +146 -0
  19. package/dist/adapters/drizzle/schema.d.ts.map +1 -0
  20. package/dist/adapters/drizzle/schema.js +20 -0
  21. package/dist/adapters/drizzle/schema.js.map +1 -0
  22. package/dist/adapters/firestore/adapter.d.ts +3 -2
  23. package/dist/adapters/firestore/adapter.d.ts.map +1 -1
  24. package/dist/adapters/firestore/adapter.js +23 -6
  25. package/dist/adapters/firestore/adapter.js.map +1 -1
  26. package/dist/adapters/supabase/adapter.d.ts +2 -1
  27. package/dist/adapters/supabase/adapter.d.ts.map +1 -1
  28. package/dist/adapters/supabase/adapter.js +4 -1
  29. package/dist/adapters/supabase/adapter.js.map +1 -1
  30. package/dist/adapters/sync/config.d.ts +22 -2
  31. package/dist/adapters/sync/config.d.ts.map +1 -1
  32. package/dist/adapters/sync/config.js +175 -16
  33. package/dist/adapters/sync/config.js.map +1 -1
  34. package/dist/adapters/sync/create-file-sync.d.ts +32 -0
  35. package/dist/adapters/sync/create-file-sync.d.ts.map +1 -0
  36. package/dist/adapters/sync/create-file-sync.js +218 -0
  37. package/dist/adapters/sync/create-file-sync.js.map +1 -0
  38. package/dist/adapters/sync/file-sync.d.ts +40 -6
  39. package/dist/adapters/sync/file-sync.d.ts.map +1 -1
  40. package/dist/adapters/sync/file-sync.js +442 -97
  41. package/dist/adapters/sync/file-sync.js.map +1 -1
  42. package/dist/adapters/sync/index.d.ts +3 -2
  43. package/dist/adapters/sync/index.d.ts.map +1 -1
  44. package/dist/adapters/sync/index.js +3 -1
  45. package/dist/adapters/sync/index.js.map +1 -1
  46. package/dist/adapters/sync/merge.js +3 -2
  47. package/dist/adapters/sync/merge.js.map +1 -1
  48. package/dist/adapters/sync/types.d.ts +36 -2
  49. package/dist/adapters/sync/types.d.ts.map +1 -1
  50. package/dist/adapters/sync/types.js +22 -1
  51. package/dist/adapters/sync/types.js.map +1 -1
  52. package/dist/agent/index.d.ts +3 -0
  53. package/dist/agent/index.d.ts.map +1 -0
  54. package/dist/agent/index.js +2 -0
  55. package/dist/agent/index.js.map +1 -0
  56. package/dist/agent/production-agent.d.ts +16 -0
  57. package/dist/agent/production-agent.d.ts.map +1 -0
  58. package/dist/agent/production-agent.js +152 -0
  59. package/dist/agent/production-agent.js.map +1 -0
  60. package/dist/agent/types.d.ts +38 -0
  61. package/dist/agent/types.d.ts.map +1 -0
  62. package/dist/agent/types.js +2 -0
  63. package/dist/agent/types.js.map +1 -0
  64. package/dist/cli/create.d.ts.map +1 -1
  65. package/dist/cli/create.js +2 -0
  66. package/dist/cli/create.js.map +1 -1
  67. package/dist/cli/index.js +37 -11
  68. package/dist/cli/index.js.map +1 -1
  69. package/dist/client/PoweredByBadge.d.ts +14 -0
  70. package/dist/client/PoweredByBadge.d.ts.map +1 -0
  71. package/dist/client/PoweredByBadge.js +60 -0
  72. package/dist/client/PoweredByBadge.js.map +1 -0
  73. package/dist/client/ProductionAgentPanel.d.ts +10 -0
  74. package/dist/client/ProductionAgentPanel.d.ts.map +1 -0
  75. package/dist/client/ProductionAgentPanel.js +121 -0
  76. package/dist/client/ProductionAgentPanel.js.map +1 -0
  77. package/dist/client/Turnstile.d.ts +35 -0
  78. package/dist/client/Turnstile.d.ts.map +1 -0
  79. package/dist/client/Turnstile.js +77 -0
  80. package/dist/client/Turnstile.js.map +1 -0
  81. package/dist/client/index.d.ts +6 -0
  82. package/dist/client/index.d.ts.map +1 -1
  83. package/dist/client/index.js +6 -0
  84. package/dist/client/index.js.map +1 -1
  85. package/dist/client/use-file-sync-status.d.ts +21 -0
  86. package/dist/client/use-file-sync-status.d.ts.map +1 -0
  87. package/dist/client/use-file-sync-status.js +65 -0
  88. package/dist/client/use-file-sync-status.js.map +1 -0
  89. package/dist/client/use-session.d.ts +16 -0
  90. package/dist/client/use-session.d.ts.map +1 -0
  91. package/dist/client/use-session.js +49 -0
  92. package/dist/client/use-session.js.map +1 -0
  93. package/dist/client/useProductionAgent.d.ts +18 -0
  94. package/dist/client/useProductionAgent.d.ts.map +1 -0
  95. package/dist/client/useProductionAgent.js +135 -0
  96. package/dist/client/useProductionAgent.js.map +1 -0
  97. package/dist/db/index.d.ts +21 -0
  98. package/dist/db/index.d.ts.map +1 -0
  99. package/dist/db/index.js +17 -0
  100. package/dist/db/index.js.map +1 -0
  101. package/dist/index.browser.d.ts +1 -1
  102. package/dist/index.browser.d.ts.map +1 -1
  103. package/dist/index.browser.js +1 -1
  104. package/dist/index.browser.js.map +1 -1
  105. package/dist/index.d.ts +3 -2
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +4 -2
  108. package/dist/index.js.map +1 -1
  109. package/dist/router/index.d.ts +3 -0
  110. package/dist/router/index.d.ts.map +1 -0
  111. package/dist/router/index.js +5 -0
  112. package/dist/router/index.js.map +1 -0
  113. package/dist/scripts/core-scripts.d.ts +10 -0
  114. package/dist/scripts/core-scripts.d.ts.map +1 -0
  115. package/dist/scripts/core-scripts.js +15 -0
  116. package/dist/scripts/core-scripts.js.map +1 -0
  117. package/dist/scripts/db/exec.d.ts +11 -0
  118. package/dist/scripts/db/exec.d.ts.map +1 -0
  119. package/dist/scripts/db/exec.js +86 -0
  120. package/dist/scripts/db/exec.js.map +1 -0
  121. package/dist/scripts/db/index.d.ts +2 -0
  122. package/dist/scripts/db/index.d.ts.map +1 -0
  123. package/dist/scripts/db/index.js +6 -0
  124. package/dist/scripts/db/index.js.map +1 -0
  125. package/dist/scripts/db/query.d.ts +10 -0
  126. package/dist/scripts/db/query.d.ts.map +1 -0
  127. package/dist/scripts/db/query.js +96 -0
  128. package/dist/scripts/db/query.js.map +1 -0
  129. package/dist/scripts/db/schema.d.ts +12 -0
  130. package/dist/scripts/db/schema.d.ts.map +1 -0
  131. package/dist/scripts/db/schema.js +112 -0
  132. package/dist/scripts/db/schema.js.map +1 -0
  133. package/dist/scripts/index.d.ts +4 -0
  134. package/dist/scripts/index.d.ts.map +1 -1
  135. package/dist/scripts/index.js +4 -0
  136. package/dist/scripts/index.js.map +1 -1
  137. package/dist/scripts/runner.d.ts +3 -0
  138. package/dist/scripts/runner.d.ts.map +1 -1
  139. package/dist/scripts/runner.js +53 -14
  140. package/dist/scripts/runner.js.map +1 -1
  141. package/dist/server/auth.d.ts +59 -0
  142. package/dist/server/auth.d.ts.map +1 -0
  143. package/dist/server/auth.js +442 -0
  144. package/dist/server/auth.js.map +1 -0
  145. package/dist/server/captcha.d.ts +12 -0
  146. package/dist/server/captcha.d.ts.map +1 -0
  147. package/dist/server/captcha.js +43 -0
  148. package/dist/server/captcha.js.map +1 -0
  149. package/dist/server/create-server.d.ts +13 -10
  150. package/dist/server/create-server.d.ts.map +1 -1
  151. package/dist/server/create-server.js +41 -27
  152. package/dist/server/create-server.js.map +1 -1
  153. package/dist/server/index.d.ts +5 -1
  154. package/dist/server/index.d.ts.map +1 -1
  155. package/dist/server/index.js +6 -1
  156. package/dist/server/index.js.map +1 -1
  157. package/dist/server/missing-key.d.ts +9 -5
  158. package/dist/server/missing-key.d.ts.map +1 -1
  159. package/dist/server/missing-key.js +12 -7
  160. package/dist/server/missing-key.js.map +1 -1
  161. package/dist/server/sse.d.ts +12 -7
  162. package/dist/server/sse.d.ts.map +1 -1
  163. package/dist/server/sse.js +79 -15
  164. package/dist/server/sse.js.map +1 -1
  165. package/dist/vite/client.d.ts +28 -5
  166. package/dist/vite/client.d.ts.map +1 -1
  167. package/dist/vite/client.js +36 -15
  168. package/dist/vite/client.js.map +1 -1
  169. package/dist/vite/dev-api-server.d.ts +10 -0
  170. package/dist/vite/dev-api-server.d.ts.map +1 -0
  171. package/dist/vite/dev-api-server.js +148 -0
  172. package/dist/vite/dev-api-server.js.map +1 -0
  173. package/dist/vite/index.d.ts +2 -3
  174. package/dist/vite/index.d.ts.map +1 -1
  175. package/dist/vite/index.js +2 -3
  176. package/dist/vite/index.js.map +1 -1
  177. package/package.json +26 -17
  178. package/src/templates/default/AGENTS.md +148 -22
  179. package/src/templates/default/_gitignore +9 -5
  180. package/src/templates/default/client/entry.client.tsx +4 -0
  181. package/src/templates/default/client/entry.server.tsx +55 -0
  182. package/src/templates/default/client/root.tsx +82 -0
  183. package/src/templates/default/client/routes/_index.tsx +19 -0
  184. package/src/templates/default/client/routes.ts +4 -0
  185. package/src/templates/default/client/vite-env.d.ts +5 -0
  186. package/src/templates/default/package.json +5 -7
  187. package/src/templates/default/react-router.config.ts +6 -0
  188. package/src/templates/default/server/lib/watcher.ts +21 -0
  189. package/src/templates/default/server/plugins/auth.ts +5 -0
  190. package/src/templates/default/server/plugins/file-sync.ts +39 -0
  191. package/src/templates/default/server/routes/[...page].get.ts +12 -0
  192. package/src/templates/default/server/routes/api/events.get.ts +7 -0
  193. package/src/templates/default/server/routes/api/file-sync/status.get.ts +13 -0
  194. package/src/templates/default/server/routes/api/hello.get.ts +5 -0
  195. package/src/templates/default/tsconfig.json +9 -1
  196. package/src/templates/default/vite.config.ts +4 -1
  197. package/tsconfig.base.json +3 -1
  198. package/dist/adapters/neon/adapter.d.ts +0 -28
  199. package/dist/adapters/neon/adapter.d.ts.map +0 -1
  200. package/dist/adapters/neon/adapter.js +0 -135
  201. package/dist/adapters/neon/adapter.js.map +0 -1
  202. package/dist/adapters/neon/index.d.ts +0 -3
  203. package/dist/adapters/neon/index.d.ts.map +0 -1
  204. package/dist/adapters/neon/index.js +0 -3
  205. package/dist/adapters/neon/index.js.map +0 -1
  206. package/dist/server/production.d.ts +0 -18
  207. package/dist/server/production.d.ts.map +0 -1
  208. package/dist/server/production.js +0 -37
  209. package/dist/server/production.js.map +0 -1
  210. package/dist/vite/express-plugin.d.ts +0 -14
  211. package/dist/vite/express-plugin.d.ts.map +0 -1
  212. package/dist/vite/express-plugin.js +0 -53
  213. package/dist/vite/express-plugin.js.map +0 -1
  214. package/dist/vite/server.d.ts +0 -21
  215. package/dist/vite/server.d.ts.map +0 -1
  216. package/dist/vite/server.js +0 -68
  217. package/dist/vite/server.js.map +0 -1
  218. package/src/templates/default/index.html +0 -14
  219. package/src/templates/default/server/index.ts +0 -22
  220. package/src/templates/default/server/node-build.ts +0 -4
  221. 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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"}
@@ -1,4 +1,3 @@
1
- export { defineConfig, type ClientConfigOptions } from "./client.js";
2
- export { defineServerConfig, type ServerConfigOptions } from "./server.js";
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
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"}
@@ -1,4 +1,3 @@
1
- export { defineConfig } from "./client.js";
2
- export { defineServerConfig } from "./server.js";
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA4B,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAA4B,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC"}
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.1",
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/neon": "./dist/adapters/neon/index.js",
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
- "cors": ">=2",
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
- "cors": {
88
- "optional": true
89
- },
90
- "express": {
98
+ "@vitejs/plugin-react-swc": {
91
99
  "optional": true
92
100
  },
93
- "@vitejs/plugin-react-swc": {
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
- "@neondatabase/serverless": {
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/cors": "^2.8.19",
109
- "@types/express": "^5.0.3",
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
- "cors": "^2.8.5",
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
- ### Directory Structure
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/ # Express backend
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
- shared/ # Isomorphic code (imported by both client & server)
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
- scripts/ # Agent-callable scripts
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
- data/ # App data files (watched by SSE)
25
+ ### Directory Structure
35
26
 
36
- .agents/skills/ # Agent skills — detailed guidance for each rule
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
- Edit `server/index.ts`, add your route to `createAppServer()`.
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
- - **Backend:** Express 5
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 SPA + server bundle)
101
- - **Dev:** `pnpm dev` (Vite dev server with Express middleware)
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
- # Logs
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,4 @@
1
+ import { hydrateRoot } from "react-dom/client";
2
+ import { HydratedRouter } from "react-router/dom";
3
+
4
+ hydrateRoot(document, <HydratedRouter />);
@@ -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
+ }