create-feltdb 0.4.0 → 0.4.1
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 +21 -15
- package/dist/cli.js +33 -9
- package/dist/create.js +103 -35
- package/dist/package-versions.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,29 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Interactive CLI tool to scaffold new FeltDB applications.
|
|
4
4
|
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g create-feltdb
|
|
9
|
-
```
|
|
10
|
-
|
|
11
5
|
## Usage
|
|
12
6
|
|
|
13
|
-
|
|
7
|
+
One command scaffolds, installs, and starts the application, Studio, and local
|
|
8
|
+
WebLLM agent (no global installation required):
|
|
14
9
|
|
|
15
10
|
```bash
|
|
16
|
-
npx create-feltdb my-app
|
|
11
|
+
npx --yes create-feltdb@latest my-app
|
|
17
12
|
```
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
Use the interactive menu, or accept its defaults non-interactively:
|
|
20
15
|
|
|
21
16
|
```bash
|
|
22
|
-
create-feltdb my-app
|
|
17
|
+
npx --yes create-feltdb@latest my-app --yes
|
|
23
18
|
```
|
|
24
19
|
|
|
25
20
|
### Options
|
|
26
21
|
|
|
27
|
-
- `--
|
|
22
|
+
- `--runtime <browser|node|self-hosted>`
|
|
23
|
+
- `--framework <react|vanilla>`
|
|
24
|
+
- `--no-distributed`
|
|
25
|
+
- `--no-agents`
|
|
26
|
+
- `--capabilities <search|vector|search,vector>`
|
|
27
|
+
- `--no-install` — generate without installing dependencies
|
|
28
|
+
- `--no-start` — do not start services after generation
|
|
29
|
+
- `--yes` or `-y` — skip the interactive menu and use defaults
|
|
30
|
+
- `--help` or `-h`
|
|
31
|
+
- `--version`
|
|
28
32
|
|
|
29
33
|
## What Gets Generated
|
|
30
34
|
|
|
@@ -47,16 +51,16 @@ my-app/
|
|
|
47
51
|
└── README.md
|
|
48
52
|
```
|
|
49
53
|
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
After running `create-feltdb`:
|
|
54
|
+
## Run It Again
|
|
53
55
|
|
|
54
56
|
```bash
|
|
55
57
|
cd my-app
|
|
56
|
-
npm install
|
|
57
58
|
npm run dev
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
This starts the app and Studio together. Self-hosted projects also start the
|
|
62
|
+
version-matched FeltDB server container and therefore require Docker.
|
|
63
|
+
|
|
60
64
|
## Default Configuration
|
|
61
65
|
|
|
62
66
|
The generated project uses:
|
|
@@ -64,6 +68,8 @@ The generated project uses:
|
|
|
64
68
|
- **Storage**: OPFS (with IndexedDB fallback)
|
|
65
69
|
- **Distributed**: Enabled by default
|
|
66
70
|
- **Capabilities**: Search enabled, Vector disabled
|
|
71
|
+
- **AI**: Private WebLLM agent enabled; model loads lazily in the browser
|
|
72
|
+
- **Studio**: Installed and served alongside the application
|
|
67
73
|
|
|
68
74
|
## License
|
|
69
75
|
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import readline from 'readline';
|
|
10
|
+
import { spawn } from 'child_process';
|
|
10
11
|
import { createProject } from './create.js';
|
|
11
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
function parseArgs(args) {
|
|
@@ -38,6 +39,15 @@ function parseArgs(args) {
|
|
|
38
39
|
}
|
|
39
40
|
return options;
|
|
40
41
|
}
|
|
42
|
+
function run(command, args, cwd) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const child = spawn(command, args, { cwd, stdio: 'inherit' });
|
|
45
|
+
child.once('error', reject);
|
|
46
|
+
child.once('exit', code => code === 0
|
|
47
|
+
? resolve()
|
|
48
|
+
: reject(new Error(`${command} exited with status ${code ?? 'unknown'}`)));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
41
51
|
async function select(message, choices, initialValue) {
|
|
42
52
|
let selected = Math.max(0, choices.findIndex(choice => choice.value === initialValue));
|
|
43
53
|
const input = process.stdin;
|
|
@@ -114,11 +124,11 @@ async function promptForOptions(defaults) {
|
|
|
114
124
|
async function main() {
|
|
115
125
|
const args = process.argv.slice(2);
|
|
116
126
|
if (args.includes('--help') || args.includes('-h')) {
|
|
117
|
-
console.log(`create-feltdb 0.4.
|
|
127
|
+
console.log(`create-feltdb 0.4.1\n\nUsage: create-feltdb [project-name] [options]\n\nOptions:\n --runtime <browser|node|self-hosted>\n --framework <react|vanilla>\n --no-distributed\n --no-agents\n --capabilities <list>\n --no-install\n --no-start\n -y, --yes\n -h, --help\n --version`);
|
|
118
128
|
return;
|
|
119
129
|
}
|
|
120
130
|
if (args.includes('--version')) {
|
|
121
|
-
console.log('0.4.
|
|
131
|
+
console.log('0.4.1');
|
|
122
132
|
return;
|
|
123
133
|
}
|
|
124
134
|
// Find project name (first non-flag argument)
|
|
@@ -135,6 +145,8 @@ async function main() {
|
|
|
135
145
|
}
|
|
136
146
|
}
|
|
137
147
|
const shouldAutoYes = args.includes('--yes') || args.includes('-y');
|
|
148
|
+
const shouldInstall = !args.includes('--no-install');
|
|
149
|
+
const shouldStart = !args.includes('--no-start');
|
|
138
150
|
let options = parseArgs(args);
|
|
139
151
|
console.log('\n✨ Creating FeltDB Application\n');
|
|
140
152
|
if (!shouldAutoYes) {
|
|
@@ -158,16 +170,28 @@ async function main() {
|
|
|
158
170
|
capabilities: options.capabilities,
|
|
159
171
|
});
|
|
160
172
|
console.log('\n✅ FeltDB application created successfully!\n');
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
173
|
+
const projectDir = path.resolve(process.cwd(), projectName);
|
|
174
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
175
|
+
if (shouldInstall) {
|
|
176
|
+
console.log('📦 Installing application, Studio, and local AI dependencies...\n');
|
|
177
|
+
await run(npm, ['install'], projectDir);
|
|
178
|
+
}
|
|
179
|
+
if (shouldStart) {
|
|
180
|
+
if (!shouldInstall) {
|
|
181
|
+
console.log(`Start after installing dependencies:\n cd ${projectName}\n npm install\n npm run dev\n`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log('\n🚀 Starting the application and FeltDB Studio...\n');
|
|
185
|
+
await run(npm, ['run', 'dev'], projectDir);
|
|
186
|
+
}
|
|
166
187
|
}
|
|
167
188
|
else {
|
|
168
|
-
console.log(
|
|
189
|
+
console.log('Project is ready. Start everything with:');
|
|
190
|
+
console.log(` cd ${projectName}`);
|
|
191
|
+
if (!shouldInstall)
|
|
192
|
+
console.log(' npm install');
|
|
193
|
+
console.log(' npm run dev');
|
|
169
194
|
}
|
|
170
|
-
console.log('');
|
|
171
195
|
}
|
|
172
196
|
catch (error) {
|
|
173
197
|
console.error('❌ Failed to create project:', error);
|
package/dist/create.js
CHANGED
|
@@ -54,6 +54,7 @@ export async function createProject(options) {
|
|
|
54
54
|
},
|
|
55
55
|
devDependencies: {
|
|
56
56
|
'@feltdb/cli': feltdbPackageRange,
|
|
57
|
+
'@feltdb/studio': feltdbPackageRange,
|
|
57
58
|
typescript: '^5.0.0',
|
|
58
59
|
'@types/node': '^20.0.0',
|
|
59
60
|
vite: '^8.2.1',
|
|
@@ -66,6 +67,9 @@ export async function createProject(options) {
|
|
|
66
67
|
packageJson.devDependencies['@types/react'] = '^18.0.0';
|
|
67
68
|
packageJson.devDependencies['@types/react-dom'] = '^18.0.0';
|
|
68
69
|
}
|
|
70
|
+
if (hasAgents) {
|
|
71
|
+
packageJson.dependencies['@feltdb/webllm'] = feltdbPackageRange;
|
|
72
|
+
}
|
|
69
73
|
fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
70
74
|
// Create feltdb.config.json
|
|
71
75
|
const feltdbConfig = {
|
|
@@ -153,52 +157,62 @@ export async function createProject(options) {
|
|
|
153
157
|
}
|
|
154
158
|
fs.writeFileSync(path.join(projectDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
155
159
|
// Create main application files
|
|
160
|
+
const runtimeOptions = runtime === 'browser'
|
|
161
|
+
? "{ namespace: '" + applicationName + "', browser: true }"
|
|
162
|
+
: runtime === 'self-hosted'
|
|
163
|
+
? "{ namespace: '" + applicationName + "', server: { url: import.meta.env.VITE_FELTDB_URL || 'http://localhost:7700', token: import.meta.env.VITE_FELTDB_API_KEY || '' } }"
|
|
164
|
+
: "{ namespace: '" + applicationName + "', memory: true }";
|
|
156
165
|
const feltdbTs = `import { createFeltDB } from '@feltdb/core';
|
|
157
166
|
|
|
158
|
-
export const db = createFeltDB({
|
|
159
|
-
namespace: '${applicationName}',
|
|
160
|
-
memory: true, // Development-only; configure \`server\` for durable customer data.
|
|
161
|
-
});
|
|
167
|
+
export const db = createFeltDB(${runtimeOptions});
|
|
162
168
|
|
|
163
169
|
// Collections
|
|
164
170
|
export const documents = db.collection('documents');
|
|
165
171
|
export const reports = db.collection('reports');
|
|
166
172
|
`;
|
|
167
173
|
fs.writeFileSync(path.join(srcDir, 'feltdb.ts'), feltdbTs);
|
|
168
|
-
// Create a
|
|
174
|
+
// Create a real local-inference agent for browser projects.
|
|
169
175
|
if (hasAgents) {
|
|
170
|
-
const agentTs = `import {
|
|
176
|
+
const agentTs = `import { WebLLMProvider } from '@feltdb/webllm';
|
|
177
|
+
import { db, reports } from '../../src/feltdb';
|
|
171
178
|
|
|
172
179
|
/**
|
|
173
|
-
* Researcher
|
|
174
|
-
*
|
|
175
|
-
* This agent demonstrates distributed agent execution
|
|
176
|
-
* across peers in the FeltDB fabric.
|
|
180
|
+
* Researcher metadata is registered in FeltDB; inference runs privately in
|
|
181
|
+
* the browser through WebLLM and its worker.
|
|
177
182
|
*/
|
|
178
183
|
export const researcher = db.defineAgent({
|
|
179
184
|
name: 'researcher',
|
|
180
|
-
|
|
185
|
+
version: 1,
|
|
186
|
+
description: 'Analyze documents with private, on-device inference',
|
|
181
187
|
capabilities: [
|
|
182
188
|
'document-read',
|
|
183
|
-
'vector-search',
|
|
184
189
|
'report-write'
|
|
185
190
|
],
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
}
|
|
191
|
+
goals: ['Summarize documents without sending prompts off-device'],
|
|
192
|
+
constraints: { maxIterations: 1 },
|
|
201
193
|
});
|
|
194
|
+
|
|
195
|
+
let provider: WebLLMProvider | undefined;
|
|
196
|
+
|
|
197
|
+
export async function runResearcher(
|
|
198
|
+
prompt: string,
|
|
199
|
+
onProgress?: (message: string) => void,
|
|
200
|
+
) {
|
|
201
|
+
provider ??= new WebLLMProvider({
|
|
202
|
+
onProgress: ({ progress, text }) =>
|
|
203
|
+
onProgress?.(\`${'${Math.round(progress * 100)}'}% ${'${text}'}\`),
|
|
204
|
+
});
|
|
205
|
+
const content = await provider.generate([
|
|
206
|
+
{ role: 'system', content: 'You are a concise research assistant. Keep all reasoning local.' },
|
|
207
|
+
{ role: 'user', content: prompt },
|
|
208
|
+
]);
|
|
209
|
+
await reports.insert({
|
|
210
|
+
title: prompt.slice(0, 80) || 'Local research',
|
|
211
|
+
content,
|
|
212
|
+
createdAt: new Date(),
|
|
213
|
+
});
|
|
214
|
+
return content;
|
|
215
|
+
}
|
|
202
216
|
`;
|
|
203
217
|
fs.writeFileSync(path.join(feltdbDir, 'agents', 'researcher.ts'), agentTs);
|
|
204
218
|
}
|
|
@@ -228,12 +242,51 @@ export const capabilities = {
|
|
|
228
242
|
fs.writeFileSync(path.join(feltdbDir, 'capabilities', 'index.ts'), capabilitiesTs);
|
|
229
243
|
// Create main application file based on framework
|
|
230
244
|
if (framework === 'react') {
|
|
245
|
+
const agentImport = hasAgents
|
|
246
|
+
? "import { runResearcher } from '../feltdb/agents/researcher';"
|
|
247
|
+
: '';
|
|
248
|
+
const agentState = hasAgents
|
|
249
|
+
? ` const [prompt, setPrompt] = useState('Summarize why local-first applications are useful.');
|
|
250
|
+
const [answer, setAnswer] = useState('');
|
|
251
|
+
const [modelStatus, setModelStatus] = useState('Model not loaded');
|
|
252
|
+
const [generating, setGenerating] = useState(false);`
|
|
253
|
+
: '';
|
|
254
|
+
const agentHandler = hasAgents
|
|
255
|
+
? `
|
|
256
|
+
const handleResearch = async () => {
|
|
257
|
+
setGenerating(true);
|
|
258
|
+
setAnswer('');
|
|
259
|
+
try {
|
|
260
|
+
setAnswer(await runResearcher(prompt, setModelStatus));
|
|
261
|
+
setModelStatus('Ready — inference stayed in this browser');
|
|
262
|
+
} catch (error) {
|
|
263
|
+
setModelStatus(error instanceof Error ? error.message : String(error));
|
|
264
|
+
} finally {
|
|
265
|
+
setGenerating(false);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
`
|
|
269
|
+
: '';
|
|
270
|
+
const agentMarkup = hasAgents
|
|
271
|
+
? `
|
|
272
|
+
<section className="researcher">
|
|
273
|
+
<h2>Private WebLLM Researcher</h2>
|
|
274
|
+
<p>{modelStatus}</p>
|
|
275
|
+
<textarea value={prompt} onChange={event => setPrompt(event.target.value)} rows={4} />
|
|
276
|
+
<button onClick={handleResearch} disabled={generating || !prompt.trim()}>
|
|
277
|
+
{generating ? 'Running locally…' : 'Run researcher'}
|
|
278
|
+
</button>
|
|
279
|
+
{answer && <article><h3>Result</h3><p>{answer}</p></article>}
|
|
280
|
+
</section>`
|
|
281
|
+
: '';
|
|
231
282
|
const appTsx = `import React, { useState, useEffect } from 'react';
|
|
232
283
|
import { db, documents } from './feltdb';
|
|
284
|
+
${agentImport}
|
|
233
285
|
|
|
234
286
|
export function App() {
|
|
235
287
|
const [docs, setDocs] = useState<any[]>([]);
|
|
236
288
|
const [loading, setLoading] = useState(true);
|
|
289
|
+
${agentState}
|
|
237
290
|
|
|
238
291
|
useEffect(() => {
|
|
239
292
|
const loadDocs = async () => {
|
|
@@ -264,6 +317,7 @@ export function App() {
|
|
|
264
317
|
console.error('Error adding document:', err);
|
|
265
318
|
}
|
|
266
319
|
};
|
|
320
|
+
${agentHandler}
|
|
267
321
|
|
|
268
322
|
return (
|
|
269
323
|
<div className="app">
|
|
@@ -306,6 +360,7 @@ export function App() {
|
|
|
306
360
|
)}
|
|
307
361
|
<button onClick={handleAddDocument}>Add Document</button>
|
|
308
362
|
</section>
|
|
363
|
+
${agentMarkup}
|
|
309
364
|
</main>
|
|
310
365
|
|
|
311
366
|
<style>{\`
|
|
@@ -418,6 +473,20 @@ export function App() {
|
|
|
418
473
|
button:hover {
|
|
419
474
|
background: #5568d3;
|
|
420
475
|
}
|
|
476
|
+
|
|
477
|
+
textarea {
|
|
478
|
+
box-sizing: border-box;
|
|
479
|
+
display: block;
|
|
480
|
+
margin: 12px 0;
|
|
481
|
+
padding: 10px;
|
|
482
|
+
width: 100%;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.researcher {
|
|
486
|
+
border-top: 1px solid #eee;
|
|
487
|
+
margin-top: 30px;
|
|
488
|
+
padding-top: 20px;
|
|
489
|
+
}
|
|
421
490
|
\`}</style>
|
|
422
491
|
</div>
|
|
423
492
|
);
|
|
@@ -468,8 +537,10 @@ main().catch(console.error);
|
|
|
468
537
|
fs.writeFileSync(path.join(projectDir, 'index.html'), indexHtml);
|
|
469
538
|
// Create .env.example
|
|
470
539
|
const envExample = `# FeltDB Configuration
|
|
471
|
-
|
|
472
|
-
|
|
540
|
+
VITE_FELTDB_API_KEY=
|
|
541
|
+
VITE_FELTDB_URL=http://localhost:7700
|
|
542
|
+
# Override the versioned self-hosted container when needed:
|
|
543
|
+
FELTDB_IMAGE=
|
|
473
544
|
NODE_ENV=development
|
|
474
545
|
`;
|
|
475
546
|
fs.writeFileSync(path.join(projectDir, '.env.example'), envExample);
|
|
@@ -554,12 +625,9 @@ feltdb keys create --name development --scope '*'
|
|
|
554
625
|
|
|
555
626
|
## Agents
|
|
556
627
|
|
|
557
|
-
${hasAgents ? `The \`researcher\` agent
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
- Executes on compatible peers
|
|
561
|
-
- Creates workflows
|
|
562
|
-
- Publishes results` : 'No agents configured'}
|
|
628
|
+
${hasAgents ? `The \`researcher\` agent runs real private inference with
|
|
629
|
+
\`@feltdb/webllm\`. The model downloads on first use, runs in a Web Worker, and
|
|
630
|
+
caches its artifacts in the browser. Generated reports are stored in FeltDB.` : 'No agents configured'}
|
|
563
631
|
|
|
564
632
|
## Capabilities
|
|
565
633
|
|
package/dist/package-versions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// One release train keeps generated applications installable. The repository
|
|
2
2
|
// validation script checks these values against every workspace manifest.
|
|
3
|
-
export const FELTDB_PACKAGE_VERSION = '0.4.
|
|
3
|
+
export const FELTDB_PACKAGE_VERSION = '0.4.1';
|
|
4
4
|
export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
|