create-feltdb 0.2.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/README.md +70 -0
- package/bin/create-feltdb.js +3 -0
- package/dist/cli.js +157 -0
- package/dist/create.js +573 -0
- package/dist/index.js +4 -0
- package/dist/package-versions.js +4 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# create-feltdb
|
|
2
|
+
|
|
3
|
+
Interactive CLI tool to scaffold new FeltDB applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g create-feltdb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Create a new project:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx create-feltdb my-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or use the installed command:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
create-feltdb my-app
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Options
|
|
26
|
+
|
|
27
|
+
- `--yes` or `-y` - Skip interactive prompts and use defaults
|
|
28
|
+
|
|
29
|
+
## What Gets Generated
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
my-app/
|
|
33
|
+
├── feltdb/
|
|
34
|
+
│ ├── capabilities/
|
|
35
|
+
│ ├── workflows/
|
|
36
|
+
│ ├── agents/
|
|
37
|
+
│ └── schema/
|
|
38
|
+
├── src/
|
|
39
|
+
│ ├── App.tsx
|
|
40
|
+
│ ├── feltdb.ts
|
|
41
|
+
│ └── index.ts
|
|
42
|
+
├── public/
|
|
43
|
+
│ └── index.html
|
|
44
|
+
├── feltdb.config.json
|
|
45
|
+
├── package.json
|
|
46
|
+
├── tsconfig.json
|
|
47
|
+
└── README.md
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
After running `create-feltdb`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cd my-app
|
|
56
|
+
npm install
|
|
57
|
+
npm run dev
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Default Configuration
|
|
61
|
+
|
|
62
|
+
The generated project uses:
|
|
63
|
+
- **Runtime**: Browser
|
|
64
|
+
- **Storage**: OPFS (with IndexedDB fallback)
|
|
65
|
+
- **Distributed**: Enabled by default
|
|
66
|
+
- **Capabilities**: Search enabled, Vector disabled
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-feltdb
|
|
4
|
+
*
|
|
5
|
+
* Interactive CLI for creating new FeltDB applications
|
|
6
|
+
*/
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import readline from 'readline';
|
|
10
|
+
import { createProject } from './create.js';
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
function parseArgs(args) {
|
|
13
|
+
const options = {
|
|
14
|
+
runtime: 'browser',
|
|
15
|
+
framework: 'react',
|
|
16
|
+
distributed: true,
|
|
17
|
+
agents: true,
|
|
18
|
+
capabilities: 'search',
|
|
19
|
+
};
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
switch (args[i]) {
|
|
22
|
+
case '--runtime':
|
|
23
|
+
options.runtime = args[++i];
|
|
24
|
+
break;
|
|
25
|
+
case '--framework':
|
|
26
|
+
options.framework = args[++i];
|
|
27
|
+
break;
|
|
28
|
+
case '--no-distributed':
|
|
29
|
+
options.distributed = false;
|
|
30
|
+
break;
|
|
31
|
+
case '--no-agents':
|
|
32
|
+
options.agents = false;
|
|
33
|
+
break;
|
|
34
|
+
case '--capabilities':
|
|
35
|
+
options.capabilities = args[++i];
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return options;
|
|
40
|
+
}
|
|
41
|
+
async function select(message, choices, initialValue) {
|
|
42
|
+
let selected = Math.max(0, choices.findIndex(choice => choice.value === initialValue));
|
|
43
|
+
const input = process.stdin;
|
|
44
|
+
const output = process.stdout;
|
|
45
|
+
const wasRaw = input.isRaw;
|
|
46
|
+
readline.emitKeypressEvents(input);
|
|
47
|
+
input.setRawMode(true);
|
|
48
|
+
input.resume();
|
|
49
|
+
const render = (moveUp) => {
|
|
50
|
+
if (moveUp)
|
|
51
|
+
output.write(`\x1b[${choices.length}A`);
|
|
52
|
+
for (let index = 0; index < choices.length; index++) {
|
|
53
|
+
const choice = choices[index];
|
|
54
|
+
const active = index === selected;
|
|
55
|
+
const detail = choice.description ? ` \x1b[2m— ${choice.description}\x1b[22m` : '';
|
|
56
|
+
output.write(`\x1b[2K\r ${active ? '\x1b[36m❯' : ' '} ${choice.label}${active ? '\x1b[0m' : ''}${detail}\n`);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
output.write(`${message}\n`);
|
|
60
|
+
render(false);
|
|
61
|
+
return new Promise(resolve => {
|
|
62
|
+
const finish = () => {
|
|
63
|
+
input.off('keypress', onKeypress);
|
|
64
|
+
input.setRawMode(Boolean(wasRaw));
|
|
65
|
+
input.pause();
|
|
66
|
+
resolve(choices[selected].value);
|
|
67
|
+
};
|
|
68
|
+
const onKeypress = (_value, key) => {
|
|
69
|
+
if (key.ctrl && key.name === 'c') {
|
|
70
|
+
output.write('\n');
|
|
71
|
+
input.setRawMode(Boolean(wasRaw));
|
|
72
|
+
process.exit(130);
|
|
73
|
+
}
|
|
74
|
+
if (key.name === 'up')
|
|
75
|
+
selected = (selected - 1 + choices.length) % choices.length;
|
|
76
|
+
else if (key.name === 'down')
|
|
77
|
+
selected = (selected + 1) % choices.length;
|
|
78
|
+
else if (key.name === 'return' || key.name === 'enter')
|
|
79
|
+
return finish();
|
|
80
|
+
else
|
|
81
|
+
return;
|
|
82
|
+
render(true);
|
|
83
|
+
};
|
|
84
|
+
input.on('keypress', onKeypress);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function promptForOptions(defaults) {
|
|
88
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
89
|
+
return defaults;
|
|
90
|
+
console.log('Configure your application. Use ↑/↓ to move and Enter to select.\n');
|
|
91
|
+
const yesNo = [
|
|
92
|
+
{ label: 'Yes', value: true },
|
|
93
|
+
{ label: 'No', value: false },
|
|
94
|
+
];
|
|
95
|
+
return {
|
|
96
|
+
runtime: await select('Where should FeltDB run?', [
|
|
97
|
+
{ label: 'Browser', value: 'browser', description: 'local-first with durable browser storage' },
|
|
98
|
+
{ label: 'Node.js', value: 'node', description: 'application server or worker' },
|
|
99
|
+
{ label: 'Self-hosted', value: 'self-hosted', description: 'dedicated FeltDB server' },
|
|
100
|
+
], defaults.runtime),
|
|
101
|
+
framework: await select('Choose an application framework:', [
|
|
102
|
+
{ label: 'React', value: 'react' },
|
|
103
|
+
{ label: 'Vanilla TypeScript', value: 'vanilla' },
|
|
104
|
+
], defaults.framework),
|
|
105
|
+
distributed: await select('Enable distributed operation?', yesNo, defaults.distributed),
|
|
106
|
+
agents: await select('Include an autonomous agent example?', yesNo, defaults.agents),
|
|
107
|
+
capabilities: await select('Choose starter capabilities:', [
|
|
108
|
+
{ label: 'Search', value: 'search' },
|
|
109
|
+
{ label: 'Vector search', value: 'vector' },
|
|
110
|
+
{ label: 'Search + vector search', value: 'search,vector' },
|
|
111
|
+
], defaults.capabilities),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function main() {
|
|
115
|
+
const args = process.argv.slice(2);
|
|
116
|
+
const projectName = args[0] || 'feltdb-app';
|
|
117
|
+
const shouldAutoYes = args.includes('--yes') || args.includes('-y');
|
|
118
|
+
let options = parseArgs(args);
|
|
119
|
+
console.log('\n✨ Creating FeltDB Application\n');
|
|
120
|
+
if (!shouldAutoYes) {
|
|
121
|
+
options = await promptForOptions(options);
|
|
122
|
+
console.log('\nConfiguration:');
|
|
123
|
+
console.log(` Runtime: ${options.runtime}`);
|
|
124
|
+
console.log(` Framework: ${options.framework}`);
|
|
125
|
+
console.log(` Distributed: ${options.distributed ? 'yes' : 'no'}`);
|
|
126
|
+
console.log(` Agents: ${options.agents ? 'yes' : 'no'}`);
|
|
127
|
+
console.log(` Capabilities: ${options.capabilities}\n`);
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
await createProject({
|
|
131
|
+
projectName,
|
|
132
|
+
autoYes: shouldAutoYes,
|
|
133
|
+
templatesDir: path.join(__dirname, '../templates'),
|
|
134
|
+
runtime: options.runtime,
|
|
135
|
+
framework: options.framework,
|
|
136
|
+
distributed: options.distributed,
|
|
137
|
+
agents: options.agents,
|
|
138
|
+
capabilities: options.capabilities,
|
|
139
|
+
});
|
|
140
|
+
console.log('\n✅ FeltDB application created successfully!\n');
|
|
141
|
+
console.log(`Next steps:`);
|
|
142
|
+
console.log(` cd ${projectName}`);
|
|
143
|
+
console.log(` npm install`);
|
|
144
|
+
if (options.runtime === 'self-hosted') {
|
|
145
|
+
console.log(` feltdb server --data ./data`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log(` npm run dev`);
|
|
149
|
+
}
|
|
150
|
+
console.log('');
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error('❌ Failed to create project:', error);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
main();
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project creation logic
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { feltdbPackageRange } from './package-versions.js';
|
|
7
|
+
export async function createProject(options) {
|
|
8
|
+
const { projectName, templatesDir } = options;
|
|
9
|
+
const projectDir = path.resolve(process.cwd(), projectName);
|
|
10
|
+
const applicationName = path.basename(projectDir);
|
|
11
|
+
// Create project directory
|
|
12
|
+
if (!fs.existsSync(projectDir)) {
|
|
13
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
// Create feltdb directory structure
|
|
16
|
+
const feltdbDir = path.join(projectDir, 'feltdb');
|
|
17
|
+
const srcDir = path.join(projectDir, 'src');
|
|
18
|
+
const publicDir = path.join(projectDir, 'public');
|
|
19
|
+
const feltdbConfigDir = path.join(projectDir, '.feltdb');
|
|
20
|
+
fs.mkdirSync(path.join(feltdbDir, 'capabilities'), { recursive: true });
|
|
21
|
+
fs.mkdirSync(path.join(feltdbDir, 'workflows'), { recursive: true });
|
|
22
|
+
fs.mkdirSync(path.join(feltdbDir, 'agents'), { recursive: true });
|
|
23
|
+
fs.mkdirSync(path.join(feltdbDir, 'schema'), { recursive: true });
|
|
24
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
25
|
+
fs.mkdirSync(publicDir, { recursive: true });
|
|
26
|
+
fs.mkdirSync(feltdbConfigDir, { recursive: true });
|
|
27
|
+
const runtime = options.runtime || 'browser';
|
|
28
|
+
const framework = options.framework || 'react';
|
|
29
|
+
const distributed = options.distributed !== false;
|
|
30
|
+
const hasAgents = options.agents !== false;
|
|
31
|
+
const capabilities = options.capabilities || 'search';
|
|
32
|
+
// Create package.json
|
|
33
|
+
const packageJson = {
|
|
34
|
+
name: applicationName,
|
|
35
|
+
version: '0.1.0',
|
|
36
|
+
description: `A FeltDB application`,
|
|
37
|
+
main: 'src/index.ts',
|
|
38
|
+
type: 'module',
|
|
39
|
+
scripts: {
|
|
40
|
+
dev: 'feltdb dev',
|
|
41
|
+
build: 'feltdb build',
|
|
42
|
+
test: 'node --test',
|
|
43
|
+
feltdb: 'feltdb',
|
|
44
|
+
'feltdb:server': 'feltdb server',
|
|
45
|
+
'feltdb:connect': 'feltdb connect http://localhost:7700',
|
|
46
|
+
'feltdb:status': 'feltdb status',
|
|
47
|
+
'feltdb:studio': 'feltdb studio',
|
|
48
|
+
'feltdb:validate': 'feltdb validate',
|
|
49
|
+
'feltdb:diff': 'feltdb diff',
|
|
50
|
+
'feltdb:deploy': 'feltdb deploy',
|
|
51
|
+
},
|
|
52
|
+
dependencies: {
|
|
53
|
+
'@feltdb/core': feltdbPackageRange,
|
|
54
|
+
},
|
|
55
|
+
devDependencies: {
|
|
56
|
+
'@feltdb/cli': feltdbPackageRange,
|
|
57
|
+
typescript: '^5.0.0',
|
|
58
|
+
'@types/node': '^20.0.0',
|
|
59
|
+
vite: '^8.2.1',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
if (framework === 'react') {
|
|
63
|
+
packageJson.dependencies['@feltdb/react'] = feltdbPackageRange;
|
|
64
|
+
packageJson.dependencies['react'] = '^18.0.0';
|
|
65
|
+
packageJson.dependencies['react-dom'] = '^18.0.0';
|
|
66
|
+
packageJson.devDependencies['@types/react'] = '^18.0.0';
|
|
67
|
+
packageJson.devDependencies['@types/react-dom'] = '^18.0.0';
|
|
68
|
+
}
|
|
69
|
+
fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
70
|
+
// Create feltdb.config.json
|
|
71
|
+
const feltdbConfig = {
|
|
72
|
+
namespace: applicationName,
|
|
73
|
+
runtime,
|
|
74
|
+
storage: runtime === 'browser' ? 'opfs' : 'durable',
|
|
75
|
+
distributed,
|
|
76
|
+
agents: {
|
|
77
|
+
enabled: hasAgents,
|
|
78
|
+
},
|
|
79
|
+
capabilities: {},
|
|
80
|
+
};
|
|
81
|
+
if (capabilities.includes('search')) {
|
|
82
|
+
feltdbConfig.capabilities.search = true;
|
|
83
|
+
}
|
|
84
|
+
if (capabilities.includes('vector')) {
|
|
85
|
+
feltdbConfig.capabilities['vector-search'] = true;
|
|
86
|
+
}
|
|
87
|
+
fs.writeFileSync(path.join(projectDir, 'feltdb.config.json'), JSON.stringify(feltdbConfig, null, 2));
|
|
88
|
+
const appName = applicationName.replace(/[^A-Za-z0-9_]/g, '_').replace(/^[^A-Za-z_]/, 'App_');
|
|
89
|
+
const flowSpec = `app ${appName} {
|
|
90
|
+
collection Document {
|
|
91
|
+
title: text
|
|
92
|
+
content: text
|
|
93
|
+
createdAt: datetime
|
|
94
|
+
index search using fulltext(content)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
collection Report {
|
|
98
|
+
title: text
|
|
99
|
+
content: text
|
|
100
|
+
document: ref Document
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
capability Research {
|
|
104
|
+
read Document
|
|
105
|
+
write Report
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
agent Researcher {
|
|
109
|
+
capability Research
|
|
110
|
+
workflow ResearchDocument
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
workflow ResearchDocument(document: Document) {
|
|
114
|
+
step search {
|
|
115
|
+
input document.content
|
|
116
|
+
}
|
|
117
|
+
step identity {
|
|
118
|
+
input search.output
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
trigger on Document.created {
|
|
123
|
+
workflow ResearchDocument(document)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
policy Document {
|
|
127
|
+
read: authenticated
|
|
128
|
+
write: authenticated
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
fs.writeFileSync(path.join(projectDir, 'feltdb.flow'), flowSpec);
|
|
133
|
+
// Create tsconfig.json
|
|
134
|
+
const tsconfig = {
|
|
135
|
+
compilerOptions: {
|
|
136
|
+
target: 'ES2020',
|
|
137
|
+
module: 'ESNext',
|
|
138
|
+
lib: ['ES2020', 'DOM'],
|
|
139
|
+
declaration: true,
|
|
140
|
+
outDir: './dist',
|
|
141
|
+
rootDir: './src',
|
|
142
|
+
strict: true,
|
|
143
|
+
esModuleInterop: true,
|
|
144
|
+
skipLibCheck: true,
|
|
145
|
+
forceConsistentCasingInFileNames: true,
|
|
146
|
+
moduleResolution: 'node',
|
|
147
|
+
},
|
|
148
|
+
include: ['src/**/*'],
|
|
149
|
+
exclude: ['node_modules'],
|
|
150
|
+
};
|
|
151
|
+
if (framework === 'react') {
|
|
152
|
+
tsconfig.compilerOptions.jsx = 'react-jsx';
|
|
153
|
+
}
|
|
154
|
+
fs.writeFileSync(path.join(projectDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
155
|
+
// Create main application files
|
|
156
|
+
const feltdbTs = `import { createFeltDB } from '@feltdb/core';
|
|
157
|
+
|
|
158
|
+
export const db = createFeltDB({
|
|
159
|
+
namespace: '${applicationName}',
|
|
160
|
+
memory: true, // Development-only; configure \`server\` for durable customer data.
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Collections
|
|
164
|
+
export const documents = db.collection('documents');
|
|
165
|
+
export const reports = db.collection('reports');
|
|
166
|
+
`;
|
|
167
|
+
fs.writeFileSync(path.join(srcDir, 'feltdb.ts'), feltdbTs);
|
|
168
|
+
// Create a sample agent if enabled
|
|
169
|
+
if (hasAgents) {
|
|
170
|
+
const agentTs = `import { db } from '../feltdb';
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Researcher Agent
|
|
174
|
+
*
|
|
175
|
+
* This agent demonstrates distributed agent execution
|
|
176
|
+
* across peers in the FeltDB fabric.
|
|
177
|
+
*/
|
|
178
|
+
export const researcher = db.defineAgent({
|
|
179
|
+
name: 'researcher',
|
|
180
|
+
description: 'Search and analyze documents across the fabric',
|
|
181
|
+
capabilities: [
|
|
182
|
+
'document-read',
|
|
183
|
+
'vector-search',
|
|
184
|
+
'report-write'
|
|
185
|
+
],
|
|
186
|
+
async run(options: any) {
|
|
187
|
+
console.log('🤖 Researcher agent starting...');
|
|
188
|
+
console.log('Goal:', options.goal);
|
|
189
|
+
console.log('Inputs:', options.inputs);
|
|
190
|
+
|
|
191
|
+
// The agent will execute capabilities across peers
|
|
192
|
+
// Results are automatically acquired from remote locations
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
status: 'complete',
|
|
196
|
+
results: {
|
|
197
|
+
message: 'Research completed successfully'
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
`;
|
|
203
|
+
fs.writeFileSync(path.join(feltdbDir, 'agents', 'researcher.ts'), agentTs);
|
|
204
|
+
}
|
|
205
|
+
// Create capabilities index
|
|
206
|
+
const capabilitiesTs = `/**
|
|
207
|
+
* Capabilities
|
|
208
|
+
*
|
|
209
|
+
* Define capabilities that can be executed by agents
|
|
210
|
+
* across distributed peers
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
export const capabilities = {
|
|
214
|
+
'document-read': {
|
|
215
|
+
enabled: true,
|
|
216
|
+
scope: ['documents:read'],
|
|
217
|
+
},
|
|
218
|
+
'vector-search': {
|
|
219
|
+
enabled: ${capabilities.includes('vector')},
|
|
220
|
+
scope: ['documents:read', 'capabilities:execute'],
|
|
221
|
+
},
|
|
222
|
+
'report-write': {
|
|
223
|
+
enabled: true,
|
|
224
|
+
scope: ['reports:write'],
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
`;
|
|
228
|
+
fs.writeFileSync(path.join(feltdbDir, 'capabilities', 'index.ts'), capabilitiesTs);
|
|
229
|
+
// Create main application file based on framework
|
|
230
|
+
if (framework === 'react') {
|
|
231
|
+
const appTsx = `import React, { useState, useEffect } from 'react';
|
|
232
|
+
import { db, documents } from './feltdb';
|
|
233
|
+
|
|
234
|
+
export function App() {
|
|
235
|
+
const [docs, setDocs] = useState<any[]>([]);
|
|
236
|
+
const [loading, setLoading] = useState(true);
|
|
237
|
+
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
const loadDocs = async () => {
|
|
240
|
+
try {
|
|
241
|
+
const allDocs = await documents.find({});
|
|
242
|
+
setDocs(allDocs);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
console.error('Error loading documents:', err);
|
|
245
|
+
} finally {
|
|
246
|
+
setLoading(false);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
loadDocs();
|
|
251
|
+
}, []);
|
|
252
|
+
|
|
253
|
+
const handleAddDocument = async () => {
|
|
254
|
+
try {
|
|
255
|
+
await documents.insert({
|
|
256
|
+
title: 'New Document',
|
|
257
|
+
content: 'Enter content here',
|
|
258
|
+
createdAt: new Date(),
|
|
259
|
+
});
|
|
260
|
+
// Reload documents
|
|
261
|
+
const allDocs = await documents.find({});
|
|
262
|
+
setDocs(allDocs);
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error('Error adding document:', err);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div className="app">
|
|
270
|
+
<header>
|
|
271
|
+
<h1>🌊 FeltDB Research App</h1>
|
|
272
|
+
<p>Distributed document management with agents and capabilities</p>
|
|
273
|
+
</header>
|
|
274
|
+
|
|
275
|
+
<main>
|
|
276
|
+
<section className="stats">
|
|
277
|
+
<div className="stat">
|
|
278
|
+
<span className="label">Documents:</span>
|
|
279
|
+
<span className="value">{docs.length}</span>
|
|
280
|
+
</div>
|
|
281
|
+
<div className="stat">
|
|
282
|
+
<span className="label">Runtime:</span>
|
|
283
|
+
<span className="value">${runtime}</span>
|
|
284
|
+
</div>
|
|
285
|
+
<div className="stat">
|
|
286
|
+
<span className="label">Distributed:</span>
|
|
287
|
+
<span className="value">${distributed ? '✓' : '✗'}</span>
|
|
288
|
+
</div>
|
|
289
|
+
</section>
|
|
290
|
+
|
|
291
|
+
<section className="documents">
|
|
292
|
+
<h2>Documents</h2>
|
|
293
|
+
{loading ? (
|
|
294
|
+
<p>Loading...</p>
|
|
295
|
+
) : docs.length === 0 ? (
|
|
296
|
+
<p>No documents yet</p>
|
|
297
|
+
) : (
|
|
298
|
+
<ul>
|
|
299
|
+
{docs.map((doc: any) => (
|
|
300
|
+
<li key={doc.id}>
|
|
301
|
+
<h3>{doc.title}</h3>
|
|
302
|
+
<p>{doc.content}</p>
|
|
303
|
+
</li>
|
|
304
|
+
))}
|
|
305
|
+
</ul>
|
|
306
|
+
)}
|
|
307
|
+
<button onClick={handleAddDocument}>Add Document</button>
|
|
308
|
+
</section>
|
|
309
|
+
</main>
|
|
310
|
+
|
|
311
|
+
<style>{\`
|
|
312
|
+
body {
|
|
313
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;
|
|
314
|
+
margin: 0;
|
|
315
|
+
padding: 0;
|
|
316
|
+
background: #f5f5f5;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.app {
|
|
320
|
+
max-width: 1200px;
|
|
321
|
+
margin: 0 auto;
|
|
322
|
+
padding: 20px;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
header {
|
|
326
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
327
|
+
color: white;
|
|
328
|
+
padding: 30px;
|
|
329
|
+
border-radius: 8px;
|
|
330
|
+
margin-bottom: 30px;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
header h1 {
|
|
334
|
+
margin: 0 0 10px 0;
|
|
335
|
+
font-size: 28px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
header p {
|
|
339
|
+
margin: 0;
|
|
340
|
+
opacity: 0.9;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
main {
|
|
344
|
+
background: white;
|
|
345
|
+
padding: 20px;
|
|
346
|
+
border-radius: 8px;
|
|
347
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.stats {
|
|
351
|
+
display: grid;
|
|
352
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
353
|
+
gap: 20px;
|
|
354
|
+
margin-bottom: 30px;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.stat {
|
|
358
|
+
padding: 15px;
|
|
359
|
+
background: #f9f9f9;
|
|
360
|
+
border-radius: 4px;
|
|
361
|
+
border-left: 3px solid #667eea;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.stat .label {
|
|
365
|
+
display: block;
|
|
366
|
+
color: #666;
|
|
367
|
+
font-size: 12px;
|
|
368
|
+
text-transform: uppercase;
|
|
369
|
+
margin-bottom: 5px;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.stat .value {
|
|
373
|
+
display: block;
|
|
374
|
+
font-size: 24px;
|
|
375
|
+
font-weight: bold;
|
|
376
|
+
color: #333;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.documents h2 {
|
|
380
|
+
margin-top: 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.documents ul {
|
|
384
|
+
list-style: none;
|
|
385
|
+
padding: 0;
|
|
386
|
+
margin: 0 0 20px 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.documents li {
|
|
390
|
+
padding: 15px;
|
|
391
|
+
background: #f9f9f9;
|
|
392
|
+
border-radius: 4px;
|
|
393
|
+
margin-bottom: 10px;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.documents h3 {
|
|
397
|
+
margin: 0 0 10px 0;
|
|
398
|
+
color: #333;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.documents p {
|
|
402
|
+
margin: 0;
|
|
403
|
+
color: #666;
|
|
404
|
+
font-size: 14px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
button {
|
|
408
|
+
background: #667eea;
|
|
409
|
+
color: white;
|
|
410
|
+
border: none;
|
|
411
|
+
padding: 10px 20px;
|
|
412
|
+
border-radius: 4px;
|
|
413
|
+
cursor: pointer;
|
|
414
|
+
font-size: 14px;
|
|
415
|
+
font-weight: 500;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
button:hover {
|
|
419
|
+
background: #5568d3;
|
|
420
|
+
}
|
|
421
|
+
\`}</style>
|
|
422
|
+
</div>
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
`;
|
|
426
|
+
fs.writeFileSync(path.join(srcDir, 'App.tsx'), appTsx);
|
|
427
|
+
const indexTsx = `import React from 'react';
|
|
428
|
+
import ReactDOM from 'react-dom/client';
|
|
429
|
+
import { App } from './App';
|
|
430
|
+
|
|
431
|
+
const root = ReactDOM.createRoot(document.getElementById('root')!);
|
|
432
|
+
root.render(
|
|
433
|
+
<React.StrictMode>
|
|
434
|
+
<App />
|
|
435
|
+
</React.StrictMode>
|
|
436
|
+
);
|
|
437
|
+
`;
|
|
438
|
+
fs.writeFileSync(path.join(srcDir, 'index.tsx'), indexTsx);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Create vanilla JS app
|
|
442
|
+
const indexJs = `import { db, documents } from './feltdb';
|
|
443
|
+
|
|
444
|
+
async function main() {
|
|
445
|
+
console.log('🌊 FeltDB Research App');
|
|
446
|
+
console.log('Runtime: ${runtime}');
|
|
447
|
+
console.log('Distributed: ${distributed}');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
main().catch(console.error);
|
|
451
|
+
`;
|
|
452
|
+
fs.writeFileSync(path.join(srcDir, 'index.js'), indexJs);
|
|
453
|
+
}
|
|
454
|
+
// Create index.html
|
|
455
|
+
const indexHtml = `<!DOCTYPE html>
|
|
456
|
+
<html lang="en">
|
|
457
|
+
<head>
|
|
458
|
+
<meta charset="UTF-8">
|
|
459
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
460
|
+
<title>${applicationName}</title>
|
|
461
|
+
</head>
|
|
462
|
+
<body>
|
|
463
|
+
<div id="root"></div>
|
|
464
|
+
<script type="module" src="/src/index${framework === 'react' ? '.tsx' : '.js'}"></script>
|
|
465
|
+
</body>
|
|
466
|
+
</html>
|
|
467
|
+
`;
|
|
468
|
+
fs.writeFileSync(path.join(projectDir, 'index.html'), indexHtml);
|
|
469
|
+
// Create .env.example
|
|
470
|
+
const envExample = `# FeltDB Configuration
|
|
471
|
+
FELTDB_API_KEY=
|
|
472
|
+
FELTDB_URL=http://localhost:7700
|
|
473
|
+
NODE_ENV=development
|
|
474
|
+
`;
|
|
475
|
+
fs.writeFileSync(path.join(projectDir, '.env.example'), envExample);
|
|
476
|
+
// Create .gitignore
|
|
477
|
+
const gitignore = `node_modules/
|
|
478
|
+
dist/
|
|
479
|
+
build/
|
|
480
|
+
.env
|
|
481
|
+
.env.local
|
|
482
|
+
.env.*.local
|
|
483
|
+
*.log
|
|
484
|
+
.DS_Store
|
|
485
|
+
.feltdb/keys.json
|
|
486
|
+
.feltdb/connection.json
|
|
487
|
+
`;
|
|
488
|
+
fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignore);
|
|
489
|
+
// Create README
|
|
490
|
+
const readme = `# ${applicationName}
|
|
491
|
+
|
|
492
|
+
A FeltDB distributed application with agents and capabilities.
|
|
493
|
+
|
|
494
|
+
## Quick Start
|
|
495
|
+
|
|
496
|
+
\`\`\`bash
|
|
497
|
+
npm install
|
|
498
|
+
npm run dev
|
|
499
|
+
\`\`\`
|
|
500
|
+
|
|
501
|
+
## Project Structure
|
|
502
|
+
|
|
503
|
+
\`\`\`
|
|
504
|
+
${applicationName}/
|
|
505
|
+
├── feltdb/
|
|
506
|
+
│ ├── agents/ # Agent definitions
|
|
507
|
+
│ ├── capabilities/ # Capability implementations
|
|
508
|
+
│ ├── workflows/ # Workflow definitions
|
|
509
|
+
│ └── schema/ # Data schemas
|
|
510
|
+
├── src/
|
|
511
|
+
│ ├── App.${framework === 'react' ? 'tsx' : 'js'}
|
|
512
|
+
│ ├── feltdb.ts
|
|
513
|
+
│ └── index.${framework === 'react' ? 'tsx' : 'js'}
|
|
514
|
+
├── public/
|
|
515
|
+
├── index.html
|
|
516
|
+
├── .feltdb/ # Local FeltDB configuration
|
|
517
|
+
├── feltdb.config.json
|
|
518
|
+
├── .env.example
|
|
519
|
+
├── package.json
|
|
520
|
+
├── tsconfig.json
|
|
521
|
+
└── README.md
|
|
522
|
+
\`\`\`
|
|
523
|
+
|
|
524
|
+
## Configuration
|
|
525
|
+
|
|
526
|
+
Configuration is in \`feltdb.config.json\`:
|
|
527
|
+
- \`runtime\`: ${runtime} (browser|node|self-hosted)
|
|
528
|
+
- \`storage\`: ${runtime === 'browser' ? 'opfs' : 'durable'}
|
|
529
|
+
- \`distributed\`: ${distributed}
|
|
530
|
+
- \`agents.enabled\`: ${hasAgents}
|
|
531
|
+
- \`capabilities\`: ${capabilities}
|
|
532
|
+
|
|
533
|
+
## Development
|
|
534
|
+
|
|
535
|
+
### Start Development Server
|
|
536
|
+
\`\`\`bash
|
|
537
|
+
npm run dev
|
|
538
|
+
\`\`\`
|
|
539
|
+
|
|
540
|
+
### Build for Production
|
|
541
|
+
\`\`\`bash
|
|
542
|
+
npm run build
|
|
543
|
+
\`\`\`
|
|
544
|
+
|
|
545
|
+
### Connect to Remote Server
|
|
546
|
+
\`\`\`bash
|
|
547
|
+
feltdb connect http://localhost:7700
|
|
548
|
+
\`\`\`
|
|
549
|
+
|
|
550
|
+
### Create API Key
|
|
551
|
+
\`\`\`bash
|
|
552
|
+
feltdb keys create --name development --scope '*'
|
|
553
|
+
\`\`\`
|
|
554
|
+
|
|
555
|
+
## Agents
|
|
556
|
+
|
|
557
|
+
${hasAgents ? `The \`researcher\` agent demonstrates distributed execution:
|
|
558
|
+
- Resolves document references
|
|
559
|
+
- Discovers capabilities across peers
|
|
560
|
+
- Executes on compatible peers
|
|
561
|
+
- Creates workflows
|
|
562
|
+
- Publishes results` : 'No agents configured'}
|
|
563
|
+
|
|
564
|
+
## Capabilities
|
|
565
|
+
|
|
566
|
+
${capabilities}
|
|
567
|
+
|
|
568
|
+
## Learn More
|
|
569
|
+
|
|
570
|
+
Visit [FeltDB Documentation](https://github.com/rkendel1/feltdb) to learn more.
|
|
571
|
+
`;
|
|
572
|
+
fs.writeFileSync(path.join(projectDir, 'README.md'), readme);
|
|
573
|
+
}
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-feltdb",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Create a new FeltDB application with one command",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-feltdb": "bin/create-feltdb.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"bin/",
|
|
14
|
+
"templates/"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "rm -rf dist && tsc",
|
|
18
|
+
"dev": "node dist/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.0.0",
|
|
22
|
+
"@types/node": "^20.0.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=14"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/rkendel1/feltdb.git",
|
|
30
|
+
"directory": "tools/create-feltdb"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"feltdb",
|
|
34
|
+
"database",
|
|
35
|
+
"scaffold",
|
|
36
|
+
"template",
|
|
37
|
+
"cli"
|
|
38
|
+
]
|
|
39
|
+
}
|