create-feltdb 0.3.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 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
- Create a new project:
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
- Or use the installed command:
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
- - `--yes` or `-y` - Skip interactive prompts and use defaults
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
- ## Quick Start
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,15 +124,29 @@ 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.3.0\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 -y, --yes\n -h, --help\n --version`);
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.3.0');
131
+ console.log('0.4.1');
122
132
  return;
123
133
  }
124
- const projectName = args[0] || 'feltdb-app';
134
+ // Find project name (first non-flag argument)
135
+ let projectName = 'feltdb-app';
136
+ for (let i = 0; i < args.length; i++) {
137
+ const arg = args[i];
138
+ if (!arg.startsWith('-')) {
139
+ projectName = arg;
140
+ break;
141
+ }
142
+ // Skip next arg if current arg is a flag that takes a value
143
+ if (arg === '--runtime' || arg === '--framework' || arg === '--capabilities') {
144
+ i++;
145
+ }
146
+ }
125
147
  const shouldAutoYes = args.includes('--yes') || args.includes('-y');
148
+ const shouldInstall = !args.includes('--no-install');
149
+ const shouldStart = !args.includes('--no-start');
126
150
  let options = parseArgs(args);
127
151
  console.log('\n✨ Creating FeltDB Application\n');
128
152
  if (!shouldAutoYes) {
@@ -146,16 +170,28 @@ async function main() {
146
170
  capabilities: options.capabilities,
147
171
  });
148
172
  console.log('\n✅ FeltDB application created successfully!\n');
149
- console.log(`Next steps:`);
150
- console.log(` cd ${projectName}`);
151
- console.log(` npm install`);
152
- if (options.runtime === 'self-hosted') {
153
- console.log(` feltdb server --data ./data`);
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
+ }
154
187
  }
155
188
  else {
156
- console.log(` npm run dev`);
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');
157
194
  }
158
- console.log('');
159
195
  }
160
196
  catch (error) {
161
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 sample agent if enabled
174
+ // Create a real local-inference agent for browser projects.
169
175
  if (hasAgents) {
170
- const agentTs = `import { db } from '../feltdb';
176
+ const agentTs = `import { WebLLMProvider } from '@feltdb/webllm';
177
+ import { db, reports } from '../../src/feltdb';
171
178
 
172
179
  /**
173
- * Researcher Agent
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
- description: 'Search and analyze documents across the fabric',
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
- 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
- }
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
- FELTDB_API_KEY=
472
- FELTDB_URL=http://localhost:7700
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 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'}
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
 
@@ -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.3.0';
3
+ export const FELTDB_PACKAGE_VERSION = '0.4.1';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-feltdb",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Create a new FeltDB application with one command",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -10,8 +10,7 @@
10
10
  "type": "module",
11
11
  "files": [
12
12
  "dist/",
13
- "bin/",
14
- "templates/"
13
+ "bin/"
15
14
  ],
16
15
  "scripts": {
17
16
  "build": "rm -rf dist && tsc",