@affectively/aeon-pages 1.3.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/CHANGELOG.md +112 -0
- package/README.md +625 -0
- package/examples/basic/aeon.config.ts +39 -0
- package/examples/basic/components/Cursor.tsx +86 -0
- package/examples/basic/components/OfflineIndicator.tsx +103 -0
- package/examples/basic/components/PresenceBar.tsx +77 -0
- package/examples/basic/package.json +20 -0
- package/examples/basic/pages/index.tsx +80 -0
- package/package.json +101 -0
- package/packages/analytics/README.md +309 -0
- package/packages/analytics/build.ts +35 -0
- package/packages/analytics/package.json +50 -0
- package/packages/analytics/src/click-tracker.ts +368 -0
- package/packages/analytics/src/context-bridge.ts +319 -0
- package/packages/analytics/src/data-layer.ts +302 -0
- package/packages/analytics/src/gtm-loader.ts +239 -0
- package/packages/analytics/src/index.ts +230 -0
- package/packages/analytics/src/merkle-tree.ts +489 -0
- package/packages/analytics/src/provider.tsx +300 -0
- package/packages/analytics/src/types.ts +320 -0
- package/packages/analytics/src/use-analytics.ts +296 -0
- package/packages/analytics/tsconfig.json +19 -0
- package/packages/benchmarks/src/benchmark.test.ts +691 -0
- package/packages/cli/dist/index.js +61899 -0
- package/packages/cli/package.json +43 -0
- package/packages/cli/src/commands/build.test.ts +682 -0
- package/packages/cli/src/commands/build.ts +890 -0
- package/packages/cli/src/commands/dev.ts +473 -0
- package/packages/cli/src/commands/init.ts +409 -0
- package/packages/cli/src/commands/start.ts +297 -0
- package/packages/cli/src/index.ts +105 -0
- package/packages/directives/src/use-aeon.ts +272 -0
- package/packages/mcp-server/package.json +51 -0
- package/packages/mcp-server/src/index.ts +178 -0
- package/packages/mcp-server/src/resources.ts +346 -0
- package/packages/mcp-server/src/tools/index.ts +36 -0
- package/packages/mcp-server/src/tools/navigation.ts +545 -0
- package/packages/mcp-server/tsconfig.json +21 -0
- package/packages/react/package.json +40 -0
- package/packages/react/src/Link.tsx +388 -0
- package/packages/react/src/components/InstallPrompt.tsx +286 -0
- package/packages/react/src/components/OfflineDiagnostics.tsx +677 -0
- package/packages/react/src/components/PushNotifications.tsx +453 -0
- package/packages/react/src/hooks/useAeonNavigation.ts +219 -0
- package/packages/react/src/hooks/useConflicts.ts +277 -0
- package/packages/react/src/hooks/useNetworkState.ts +209 -0
- package/packages/react/src/hooks/usePilotNavigation.ts +254 -0
- package/packages/react/src/hooks/useServiceWorker.ts +278 -0
- package/packages/react/src/hooks.ts +195 -0
- package/packages/react/src/index.ts +151 -0
- package/packages/react/src/provider.tsx +467 -0
- package/packages/react/tsconfig.json +19 -0
- package/packages/runtime/README.md +399 -0
- package/packages/runtime/build.ts +48 -0
- package/packages/runtime/package.json +71 -0
- package/packages/runtime/schema.sql +40 -0
- package/packages/runtime/src/api-routes.ts +465 -0
- package/packages/runtime/src/benchmark.ts +171 -0
- package/packages/runtime/src/cache.ts +479 -0
- package/packages/runtime/src/durable-object.ts +1341 -0
- package/packages/runtime/src/index.ts +360 -0
- package/packages/runtime/src/navigation.test.ts +421 -0
- package/packages/runtime/src/navigation.ts +422 -0
- package/packages/runtime/src/nextjs-adapter.ts +272 -0
- package/packages/runtime/src/offline/encrypted-queue.test.ts +607 -0
- package/packages/runtime/src/offline/encrypted-queue.ts +478 -0
- package/packages/runtime/src/offline/encryption.test.ts +412 -0
- package/packages/runtime/src/offline/encryption.ts +397 -0
- package/packages/runtime/src/offline/types.ts +465 -0
- package/packages/runtime/src/predictor.ts +371 -0
- package/packages/runtime/src/registry.ts +351 -0
- package/packages/runtime/src/router/context-extractor.ts +661 -0
- package/packages/runtime/src/router/esi-control-react.tsx +2053 -0
- package/packages/runtime/src/router/esi-control.ts +541 -0
- package/packages/runtime/src/router/esi-cyrano.ts +779 -0
- package/packages/runtime/src/router/esi-format-react.tsx +1744 -0
- package/packages/runtime/src/router/esi-react.tsx +1065 -0
- package/packages/runtime/src/router/esi-translate-observer.ts +476 -0
- package/packages/runtime/src/router/esi-translate-react.tsx +556 -0
- package/packages/runtime/src/router/esi-translate.ts +503 -0
- package/packages/runtime/src/router/esi.ts +666 -0
- package/packages/runtime/src/router/heuristic-adapter.test.ts +295 -0
- package/packages/runtime/src/router/heuristic-adapter.ts +557 -0
- package/packages/runtime/src/router/index.ts +298 -0
- package/packages/runtime/src/router/merkle-capability.ts +473 -0
- package/packages/runtime/src/router/speculation.ts +451 -0
- package/packages/runtime/src/router/types.ts +630 -0
- package/packages/runtime/src/router.test.ts +470 -0
- package/packages/runtime/src/router.ts +302 -0
- package/packages/runtime/src/server.ts +481 -0
- package/packages/runtime/src/service-worker-push.ts +319 -0
- package/packages/runtime/src/service-worker.ts +553 -0
- package/packages/runtime/src/skeleton-hydrate.ts +237 -0
- package/packages/runtime/src/speculation.test.ts +389 -0
- package/packages/runtime/src/speculation.ts +486 -0
- package/packages/runtime/src/storage.test.ts +1297 -0
- package/packages/runtime/src/storage.ts +1048 -0
- package/packages/runtime/src/sync/conflict-resolver.test.ts +528 -0
- package/packages/runtime/src/sync/conflict-resolver.ts +565 -0
- package/packages/runtime/src/sync/coordinator.test.ts +608 -0
- package/packages/runtime/src/sync/coordinator.ts +596 -0
- package/packages/runtime/src/tree-compiler.ts +295 -0
- package/packages/runtime/src/types.ts +728 -0
- package/packages/runtime/src/worker.ts +327 -0
- package/packages/runtime/tsconfig.json +20 -0
- package/packages/runtime/wasm/aeon_pages_runtime.d.ts +504 -0
- package/packages/runtime/wasm/aeon_pages_runtime.js +1657 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm.d.ts +196 -0
- package/packages/runtime/wasm/package.json +21 -0
- package/packages/runtime/wrangler.toml +41 -0
- package/packages/runtime-wasm/Cargo.lock +436 -0
- package/packages/runtime-wasm/Cargo.toml +29 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.d.ts +480 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.js +1568 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm.d.ts +192 -0
- package/packages/runtime-wasm/pkg/package.json +21 -0
- package/packages/runtime-wasm/src/hydrate.rs +352 -0
- package/packages/runtime-wasm/src/lib.rs +191 -0
- package/packages/runtime-wasm/src/render.rs +629 -0
- package/packages/runtime-wasm/src/router.rs +298 -0
- package/packages/runtime-wasm/src/skeleton.rs +430 -0
- package/rfcs/RFC-001-ZERO-DEPENDENCY-RENDERING.md +1446 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Aeon Pages CLI
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* - aeon init [dir] - Initialize a new Aeon Pages project
|
|
7
|
+
* - aeon dev - Start development server with hot reload
|
|
8
|
+
* - aeon build - Build for production
|
|
9
|
+
* - aeon start - Start production server
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from 'util';
|
|
13
|
+
|
|
14
|
+
const { values, positionals } = parseArgs({
|
|
15
|
+
args: process.argv.slice(2),
|
|
16
|
+
options: {
|
|
17
|
+
help: { type: 'boolean', short: 'h' },
|
|
18
|
+
version: { type: 'boolean', short: 'v' },
|
|
19
|
+
port: { type: 'string', short: 'p', default: '3000' },
|
|
20
|
+
config: { type: 'string', short: 'c' },
|
|
21
|
+
},
|
|
22
|
+
allowPositionals: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const VERSION = '0.1.0';
|
|
26
|
+
|
|
27
|
+
async function main() {
|
|
28
|
+
if (values.version) {
|
|
29
|
+
console.log(`aeon v${VERSION}`);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const command = positionals[0];
|
|
34
|
+
|
|
35
|
+
if (!command || values.help) {
|
|
36
|
+
printHelp();
|
|
37
|
+
process.exit(command ? 0 : 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
switch (command) {
|
|
41
|
+
case 'init':
|
|
42
|
+
await import('./commands/init').then((m) => m.init(positionals[1]));
|
|
43
|
+
break;
|
|
44
|
+
|
|
45
|
+
case 'dev':
|
|
46
|
+
await import('./commands/dev').then((m) =>
|
|
47
|
+
m.dev({ port: parseInt(values.port || '3000'), config: values.config }),
|
|
48
|
+
);
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'build':
|
|
52
|
+
await import('./commands/build').then((m) =>
|
|
53
|
+
m.build({ config: values.config }),
|
|
54
|
+
);
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
case 'start':
|
|
58
|
+
await import('./commands/start').then((m) =>
|
|
59
|
+
m.start({
|
|
60
|
+
port: parseInt(values.port || '3000'),
|
|
61
|
+
config: values.config,
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
default:
|
|
67
|
+
console.error(`Unknown command: ${command}`);
|
|
68
|
+
printHelp();
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function printHelp() {
|
|
74
|
+
console.log(`
|
|
75
|
+
aeon v${VERSION} - The CMS IS the website
|
|
76
|
+
|
|
77
|
+
Usage: aeon <command> [options]
|
|
78
|
+
|
|
79
|
+
Commands:
|
|
80
|
+
init [dir] Initialize a new Aeon Pages project
|
|
81
|
+
dev Start development server with hot reload
|
|
82
|
+
build Build for production (Cloudflare Workers)
|
|
83
|
+
start Start production server
|
|
84
|
+
|
|
85
|
+
Options:
|
|
86
|
+
-p, --port Server port (default: 3000)
|
|
87
|
+
-c, --config Path to aeon.config.ts
|
|
88
|
+
-h, --help Show this help message
|
|
89
|
+
-v, --version Show version
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
aeon init my-app Create new project in ./my-app
|
|
93
|
+
aeon dev Start dev server on port 3000
|
|
94
|
+
aeon dev -p 8080 Start dev server on port 8080
|
|
95
|
+
aeon build Build for Cloudflare Workers
|
|
96
|
+
aeon start Start production server
|
|
97
|
+
|
|
98
|
+
Documentation: https://github.com/affectively/aeon-pages
|
|
99
|
+
`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
main().catch((err) => {
|
|
103
|
+
console.error('Error:', err.message);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 'use aeon' Directive Processor
|
|
3
|
+
*
|
|
4
|
+
* Transforms pages with the 'use aeon' directive to enable:
|
|
5
|
+
* - Collaborative editing
|
|
6
|
+
* - Real-time presence
|
|
7
|
+
* - Schema versioning
|
|
8
|
+
* - Offline support
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* Input:
|
|
12
|
+
* ```tsx
|
|
13
|
+
* 'use aeon';
|
|
14
|
+
*
|
|
15
|
+
* export default function Page() {
|
|
16
|
+
* return <div>Hello</div>;
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Output:
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { AeonPageProvider, useAeonPage } from '@affectively/aeon-pages/react';
|
|
23
|
+
*
|
|
24
|
+
* function Page() {
|
|
25
|
+
* return <div>Hello</div>;
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* export default function AeonWrappedPage(props) {
|
|
29
|
+
* return (
|
|
30
|
+
* <AeonPageProvider route="/path/to/page">
|
|
31
|
+
* <Page {...props} />
|
|
32
|
+
* </AeonPageProvider>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
export interface TransformOptions {
|
|
39
|
+
/** File path for route derivation */
|
|
40
|
+
filePath: string;
|
|
41
|
+
/** Base pages directory */
|
|
42
|
+
pagesDir?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface TransformResult {
|
|
46
|
+
/** Transformed code */
|
|
47
|
+
code: string;
|
|
48
|
+
/** Whether the file was transformed */
|
|
49
|
+
transformed: boolean;
|
|
50
|
+
/** Derived route from file path */
|
|
51
|
+
route: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if a file contains the 'use aeon' directive
|
|
56
|
+
*/
|
|
57
|
+
export function hasAeonDirective(code: string): boolean {
|
|
58
|
+
// Match 'use aeon' or "use aeon" at the start of the file
|
|
59
|
+
const directivePattern = /^['"]use aeon['"];?\s*\n/m;
|
|
60
|
+
return directivePattern.test(code.trimStart());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Process the 'use aeon' directive and transform the code
|
|
65
|
+
*/
|
|
66
|
+
export function processAeonDirective(
|
|
67
|
+
code: string,
|
|
68
|
+
options: TransformOptions,
|
|
69
|
+
): TransformResult {
|
|
70
|
+
const { filePath, pagesDir = './pages' } = options;
|
|
71
|
+
|
|
72
|
+
// Check for directive
|
|
73
|
+
if (!hasAeonDirective(code)) {
|
|
74
|
+
return {
|
|
75
|
+
code,
|
|
76
|
+
transformed: false,
|
|
77
|
+
route: deriveRoute(filePath, pagesDir),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const route = deriveRoute(filePath, pagesDir);
|
|
82
|
+
|
|
83
|
+
// Remove the directive
|
|
84
|
+
const codeWithoutDirective = code.replace(/^['"]use aeon['"];?\s*\n/m, '');
|
|
85
|
+
|
|
86
|
+
// Find the default export
|
|
87
|
+
const defaultExportMatch = codeWithoutDirective.match(
|
|
88
|
+
/export\s+default\s+function\s+(\w+)/,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!defaultExportMatch) {
|
|
92
|
+
// Handle arrow function exports or other patterns
|
|
93
|
+
return transformArrowExport(codeWithoutDirective, route);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const componentName = defaultExportMatch[1];
|
|
97
|
+
|
|
98
|
+
// Transform the code
|
|
99
|
+
const transformedCode = `
|
|
100
|
+
// Aeon Pages - Transformed from 'use aeon' directive
|
|
101
|
+
import { AeonPageProvider, useAeonPage } from '@affectively/aeon-pages/react';
|
|
102
|
+
|
|
103
|
+
${codeWithoutDirective.replace(
|
|
104
|
+
/export\s+default\s+function\s+(\w+)/,
|
|
105
|
+
'function $1',
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
// Re-export useAeonPage for convenience
|
|
109
|
+
export { useAeonPage };
|
|
110
|
+
|
|
111
|
+
// Wrapped component with Aeon providers
|
|
112
|
+
export default function AeonWrappedPage(props: any) {
|
|
113
|
+
return (
|
|
114
|
+
<AeonPageProvider route="${route}">
|
|
115
|
+
<${componentName} {...props} />
|
|
116
|
+
</AeonPageProvider>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
`.trim();
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
code: transformedCode,
|
|
123
|
+
transformed: true,
|
|
124
|
+
route,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Transform arrow function or other export patterns
|
|
130
|
+
*/
|
|
131
|
+
function transformArrowExport(code: string, route: string): TransformResult {
|
|
132
|
+
// Match: export default () => ... or export default function() ...
|
|
133
|
+
const arrowExportMatch = code.match(
|
|
134
|
+
/export\s+default\s+((?:\([^)]*\)|[^=])\s*=>|function\s*\()/,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (!arrowExportMatch) {
|
|
138
|
+
// No recognizable default export, wrap the whole thing
|
|
139
|
+
return {
|
|
140
|
+
code: `
|
|
141
|
+
// Aeon Pages - Transformed from 'use aeon' directive
|
|
142
|
+
import { AeonPageProvider, useAeonPage } from '@affectively/aeon-pages/react';
|
|
143
|
+
|
|
144
|
+
const OriginalPage = (() => {
|
|
145
|
+
${code}
|
|
146
|
+
return undefined; // Placeholder
|
|
147
|
+
})();
|
|
148
|
+
|
|
149
|
+
export { useAeonPage };
|
|
150
|
+
|
|
151
|
+
export default function AeonWrappedPage(props: any) {
|
|
152
|
+
return (
|
|
153
|
+
<AeonPageProvider route="${route}">
|
|
154
|
+
{OriginalPage}
|
|
155
|
+
</AeonPageProvider>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
`.trim(),
|
|
159
|
+
transformed: true,
|
|
160
|
+
route,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Extract the arrow function/anonymous function
|
|
165
|
+
const transformedCode = `
|
|
166
|
+
// Aeon Pages - Transformed from 'use aeon' directive
|
|
167
|
+
import { AeonPageProvider, useAeonPage } from '@affectively/aeon-pages/react';
|
|
168
|
+
|
|
169
|
+
${code.replace(/export\s+default\s+/, 'const OriginalPage = ')}
|
|
170
|
+
|
|
171
|
+
export { useAeonPage };
|
|
172
|
+
|
|
173
|
+
export default function AeonWrappedPage(props: any) {
|
|
174
|
+
return (
|
|
175
|
+
<AeonPageProvider route="${route}">
|
|
176
|
+
<OriginalPage {...props} />
|
|
177
|
+
</AeonPageProvider>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
`.trim();
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
code: transformedCode,
|
|
184
|
+
transformed: true,
|
|
185
|
+
route,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Derive route from file path
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* - pages/index.tsx -> /
|
|
194
|
+
* - pages/about.tsx -> /about
|
|
195
|
+
* - pages/blog/[slug].tsx -> /blog/[slug]
|
|
196
|
+
* - pages/blog/[slug]/page.tsx -> /blog/[slug]
|
|
197
|
+
*/
|
|
198
|
+
export function deriveRoute(filePath: string, pagesDir: string): string {
|
|
199
|
+
// Normalize paths
|
|
200
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
201
|
+
const normalizedPagesDir = pagesDir.replace(/\\/g, '/').replace(/\/$/, '');
|
|
202
|
+
|
|
203
|
+
// Remove pages directory prefix
|
|
204
|
+
let route = normalizedPath;
|
|
205
|
+
if (route.startsWith(normalizedPagesDir)) {
|
|
206
|
+
route = route.slice(normalizedPagesDir.length);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Remove file extension
|
|
210
|
+
route = route.replace(/\.(tsx?|jsx?)$/, '');
|
|
211
|
+
|
|
212
|
+
// Remove /page suffix (Next.js app router style)
|
|
213
|
+
route = route.replace(/\/page$/, '');
|
|
214
|
+
|
|
215
|
+
// Remove /index suffix
|
|
216
|
+
route = route.replace(/\/index$/, '');
|
|
217
|
+
|
|
218
|
+
// Handle route groups (parentheses)
|
|
219
|
+
route = route.replace(/\/\([^)]+\)/g, '');
|
|
220
|
+
|
|
221
|
+
// Ensure leading slash
|
|
222
|
+
if (!route.startsWith('/')) {
|
|
223
|
+
route = '/' + route;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Handle root
|
|
227
|
+
if (route === '' || route === '/') {
|
|
228
|
+
return '/';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return route;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Bun plugin for processing 'use aeon' directives
|
|
236
|
+
*/
|
|
237
|
+
export function aeonBunPlugin(options: { pagesDir?: string } = {}) {
|
|
238
|
+
return {
|
|
239
|
+
name: 'aeon-directive',
|
|
240
|
+
setup(build: { onLoad: (opts: unknown, callback: unknown) => void }) {
|
|
241
|
+
build.onLoad(
|
|
242
|
+
{ filter: /\.(tsx?|jsx?)$/ },
|
|
243
|
+
async (args: { path: string }) => {
|
|
244
|
+
const file = Bun.file(args.path);
|
|
245
|
+
const code = await file.text();
|
|
246
|
+
|
|
247
|
+
if (!hasAeonDirective(code)) {
|
|
248
|
+
return undefined; // Let Bun handle it normally
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const result = processAeonDirective(code, {
|
|
252
|
+
filePath: args.path,
|
|
253
|
+
pagesDir: options.pagesDir,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
contents: result.code,
|
|
258
|
+
loader: args.path.endsWith('.tsx') ? 'tsx' : 'ts',
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Export for use in build tools
|
|
267
|
+
export default {
|
|
268
|
+
hasAeonDirective,
|
|
269
|
+
processAeonDirective,
|
|
270
|
+
deriveRoute,
|
|
271
|
+
aeonBunPlugin,
|
|
272
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aeon/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for aeon-flux site navigation - enables Cyrano to suggest and invoke navigation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"aeon-mcp": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"clean": "rm -rf dist"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.7.0",
|
|
22
|
+
"@types/node": "^22.0.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./tools": {
|
|
33
|
+
"types": "./dist/tools/navigation.d.ts",
|
|
34
|
+
"import": "./dist/tools/navigation.js"
|
|
35
|
+
},
|
|
36
|
+
"./resources": {
|
|
37
|
+
"types": "./dist/resources.d.ts",
|
|
38
|
+
"import": "./dist/resources.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"mcp",
|
|
43
|
+
"model-context-protocol",
|
|
44
|
+
"aeon-flux",
|
|
45
|
+
"cyrano",
|
|
46
|
+
"navigation",
|
|
47
|
+
"ai"
|
|
48
|
+
],
|
|
49
|
+
"author": "AFFECTIVELY",
|
|
50
|
+
"license": "MIT"
|
|
51
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aeon Flux MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Model Context Protocol server for aeon-flux site navigation.
|
|
5
|
+
* Enables Cyrano to suggest and invoke navigation with auto-accept mode.
|
|
6
|
+
*
|
|
7
|
+
* Tools:
|
|
8
|
+
* - navigate: Navigate to a route
|
|
9
|
+
* - suggest_route: Suggest a route to the user
|
|
10
|
+
* - get_current_route: Get the current route
|
|
11
|
+
* - get_sitemap: Get the site structure
|
|
12
|
+
* - speculate: Prefetch likely next routes
|
|
13
|
+
* - personalize: Apply personalization to current page
|
|
14
|
+
*
|
|
15
|
+
* Resources:
|
|
16
|
+
* - sitemap: Full sitemap as RAG context
|
|
17
|
+
* - current_session: Current user session data
|
|
18
|
+
* - consciousness_state: Site consciousness state
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
22
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
23
|
+
import {
|
|
24
|
+
CallToolRequestSchema,
|
|
25
|
+
ListToolsRequestSchema,
|
|
26
|
+
ListResourcesRequestSchema,
|
|
27
|
+
ReadResourceRequestSchema,
|
|
28
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
navigateTool,
|
|
32
|
+
suggestRouteTool,
|
|
33
|
+
getCurrentRouteTool,
|
|
34
|
+
getSitemapTool,
|
|
35
|
+
speculateTool,
|
|
36
|
+
personalizeTool,
|
|
37
|
+
invokeToolTool,
|
|
38
|
+
handleNavigate,
|
|
39
|
+
handleSuggestRoute,
|
|
40
|
+
handleGetCurrentRoute,
|
|
41
|
+
handleGetSitemap,
|
|
42
|
+
handleSpeculate,
|
|
43
|
+
handlePersonalize,
|
|
44
|
+
handleInvokeTool,
|
|
45
|
+
} from './tools/navigation';
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
sitemapResource,
|
|
49
|
+
sessionResource,
|
|
50
|
+
consciousnessResource,
|
|
51
|
+
handleReadSitemap,
|
|
52
|
+
handleReadSession,
|
|
53
|
+
handleReadConsciousness,
|
|
54
|
+
} from './resources';
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Server Setup
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
const server = new Server(
|
|
61
|
+
{
|
|
62
|
+
name: 'aeon-flux',
|
|
63
|
+
version: '1.0.0',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
capabilities: {
|
|
67
|
+
tools: {},
|
|
68
|
+
resources: {},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Tool Handlers
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
78
|
+
return {
|
|
79
|
+
tools: [
|
|
80
|
+
navigateTool,
|
|
81
|
+
suggestRouteTool,
|
|
82
|
+
getCurrentRouteTool,
|
|
83
|
+
getSitemapTool,
|
|
84
|
+
speculateTool,
|
|
85
|
+
personalizeTool,
|
|
86
|
+
invokeToolTool,
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
92
|
+
const { name, arguments: args } = request.params;
|
|
93
|
+
|
|
94
|
+
switch (name) {
|
|
95
|
+
case 'navigate':
|
|
96
|
+
return handleNavigate(args as { route: string; autoAccept?: boolean });
|
|
97
|
+
|
|
98
|
+
case 'suggest_route':
|
|
99
|
+
return handleSuggestRoute(
|
|
100
|
+
args as {
|
|
101
|
+
route: string;
|
|
102
|
+
reason: string;
|
|
103
|
+
autoAccept?: boolean;
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
case 'get_current_route':
|
|
108
|
+
return handleGetCurrentRoute();
|
|
109
|
+
|
|
110
|
+
case 'get_sitemap':
|
|
111
|
+
return handleGetSitemap(args as { filter?: string });
|
|
112
|
+
|
|
113
|
+
case 'speculate':
|
|
114
|
+
return handleSpeculate(args as { depth?: number });
|
|
115
|
+
|
|
116
|
+
case 'personalize':
|
|
117
|
+
return handlePersonalize(
|
|
118
|
+
args as {
|
|
119
|
+
theme?: 'light' | 'dark';
|
|
120
|
+
accent?: string;
|
|
121
|
+
density?: 'compact' | 'normal' | 'comfortable';
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
case 'invoke_tool':
|
|
126
|
+
return handleInvokeTool(
|
|
127
|
+
args as {
|
|
128
|
+
toolId: string;
|
|
129
|
+
params?: Record<string, unknown>;
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
default:
|
|
134
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Resource Handlers
|
|
140
|
+
// ============================================================================
|
|
141
|
+
|
|
142
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
143
|
+
return {
|
|
144
|
+
resources: [sitemapResource, sessionResource, consciousnessResource],
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
149
|
+
const { uri } = request.params;
|
|
150
|
+
|
|
151
|
+
switch (uri) {
|
|
152
|
+
case 'aeon://sitemap':
|
|
153
|
+
return handleReadSitemap();
|
|
154
|
+
|
|
155
|
+
case 'aeon://session':
|
|
156
|
+
return handleReadSession();
|
|
157
|
+
|
|
158
|
+
case 'aeon://consciousness':
|
|
159
|
+
return handleReadConsciousness();
|
|
160
|
+
|
|
161
|
+
default:
|
|
162
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// Main
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
async function main() {
|
|
171
|
+
const transport = new StdioServerTransport();
|
|
172
|
+
await server.connect(transport);
|
|
173
|
+
console.error('Aeon Flux MCP Server running on stdio');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
main().catch(console.error);
|
|
177
|
+
|
|
178
|
+
export { server };
|