@feltdb/core 0.4.5 → 0.4.6

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.
@@ -0,0 +1,211 @@
1
+ /**
2
+ * CLI lifecycle scripts for FeltDB applications
3
+ *
4
+ * Generates scripts for common operations:
5
+ * - feltdb:up - Start services
6
+ * - feltdb:down - Stop services
7
+ * - feltdb:logs - View logs
8
+ * - feltdb:status - Check health
9
+ * - feltdb:reset - Reset data
10
+ * - feltdb:ps - List running services
11
+ */
12
+ export function generateUpScript() {
13
+ return `#!/bin/bash
14
+
15
+ # Start FeltDB application stack
16
+ # Usage: npm run feltdb:up
17
+
18
+ set -e
19
+
20
+ echo "🚀 Starting FeltDB application..."
21
+
22
+ # Load environment
23
+ if [ -f .env.local ]; then
24
+ export \$(cat .env.local | grep -v '^#' | xargs)
25
+ fi
26
+
27
+ # Build and start containers
28
+ docker-compose up -d
29
+
30
+ echo "⏳ Waiting for services to be healthy..."
31
+
32
+ # Wait for FeltDB
33
+ TIMEOUT=60
34
+ ELAPSED=0
35
+ while [ \$ELAPSED -lt \$TIMEOUT ]; do
36
+ if curl -s -f http://localhost:7700/health > /dev/null 2>&1; then
37
+ STATE=\$(curl -s http://localhost:7700/health | grep -o '"state":"[^"]*' | cut -d'"' -f4)
38
+ if [ "\$STATE" = "ready" ] || [ "\$STATE" = "recovering" ]; then
39
+ echo "✅ FeltDB is \$STATE"
40
+ break
41
+ fi
42
+ fi
43
+ sleep 2
44
+ ELAPSED=\$((ELAPSED + 2))
45
+ done
46
+
47
+ # Wait for application
48
+ ELAPSED=0
49
+ while [ \$ELAPSED -lt \$TIMEOUT ]; do
50
+ if curl -s -f http://localhost:3000/health > /dev/null 2>&1; then
51
+ echo "✅ Application is ready"
52
+ break
53
+ fi
54
+ sleep 2
55
+ ELAPSED=\$((ELAPSED + 2))
56
+ done
57
+
58
+ echo ""
59
+ echo "✨ FeltDB stack is running!"
60
+ echo ""
61
+ echo "Services:"
62
+ echo " FeltDB: http://localhost:7700"
63
+ echo " App: http://localhost:3000"
64
+ echo " Studio: http://localhost:8000"
65
+ echo ""
66
+ echo "Next: npm run feltdb:logs"
67
+ `;
68
+ }
69
+ export function generateDownScript() {
70
+ return `#!/bin/bash
71
+
72
+ # Stop FeltDB application stack
73
+ # Usage: npm run feltdb:down
74
+
75
+ echo "🛑 Stopping FeltDB application..."
76
+
77
+ docker-compose down
78
+
79
+ echo "✅ Services stopped"
80
+ `;
81
+ }
82
+ export function generateLogsScript() {
83
+ return `#!/bin/bash
84
+
85
+ # View logs from FeltDB stack
86
+ # Usage: npm run feltdb:logs [service]
87
+ # npm run feltdb:logs feltdb
88
+ # npm run feltdb:logs app
89
+ # npm run feltdb:logs studio
90
+
91
+ SERVICE=\$1
92
+
93
+ if [ -z "\$SERVICE" ]; then
94
+ echo "📋 Logs from all services (Ctrl+C to exit):"
95
+ docker-compose logs -f
96
+ else
97
+ echo "📋 Logs from \$SERVICE (Ctrl+C to exit):"
98
+ docker-compose logs -f \$SERVICE
99
+ fi
100
+ `;
101
+ }
102
+ export function generateStatusScript() {
103
+ return `#!/bin/bash
104
+
105
+ # Check health and status of FeltDB stack
106
+ # Usage: npm run feltdb:status
107
+
108
+ echo "🔍 FeltDB Application Status"
109
+ echo ""
110
+
111
+ # Docker Compose status
112
+ echo "Containers:"
113
+ docker-compose ps
114
+
115
+ echo ""
116
+
117
+ # FeltDB health
118
+ echo "FeltDB Health:"
119
+ if RESPONSE=\$(curl -s -m 5 http://localhost:7700/health 2>/dev/null); then
120
+ STATE=\$(echo \$RESPONSE | grep -o '"state":"[^"]*' | cut -d'"' -f4)
121
+ VERSION=\$(echo \$RESPONSE | grep -o '"version":"[^"]*' | cut -d'"' -f4)
122
+ echo " State: \$STATE"
123
+ echo " Version: \$VERSION"
124
+ else
125
+ echo " ❌ No response"
126
+ fi
127
+
128
+ # Application health
129
+ echo ""
130
+ echo "Application Health:"
131
+ if RESPONSE=\$(curl -s -m 5 http://localhost:3000/health 2>/dev/null); then
132
+ echo " ✅ Responding"
133
+ else
134
+ echo " ❌ No response"
135
+ fi
136
+
137
+ echo ""
138
+ echo "Log tail: npm run feltdb:logs"
139
+ `;
140
+ }
141
+ export function generateResetScript() {
142
+ return `#!/bin/bash
143
+
144
+ # Reset FeltDB data (WARNING: destructive)
145
+ # Usage: npm run feltdb:reset
146
+
147
+ echo "⚠️ This will delete all FeltDB data!"
148
+ read -p "Are you sure? (type 'yes' to confirm): " CONFIRM
149
+
150
+ if [ "\$CONFIRM" != "yes" ]; then
151
+ echo "Cancelled."
152
+ exit 1
153
+ fi
154
+
155
+ echo "🧹 Resetting FeltDB data..."
156
+
157
+ # Stop containers
158
+ docker-compose down
159
+
160
+ # Remove volumes
161
+ docker volume rm \$(docker volume ls -q | grep feltdb_data) 2>/dev/null || true
162
+
163
+ # Restart
164
+ echo "🚀 Restarting services..."
165
+ docker-compose up -d
166
+
167
+ echo "✅ Data reset complete"
168
+ `;
169
+ }
170
+ export function generatePsScript() {
171
+ return `#!/bin/bash
172
+
173
+ # List running FeltDB services
174
+ # Usage: npm run feltdb:ps
175
+
176
+ docker-compose ps
177
+ `;
178
+ }
179
+ export function generatePackageJsonScripts(runtime) {
180
+ const scripts = {
181
+ 'feltdb:up': 'bash scripts/feltdb-up.sh',
182
+ 'feltdb:down': 'bash scripts/feltdb-down.sh',
183
+ 'feltdb:logs': 'bash scripts/feltdb-logs.sh',
184
+ 'feltdb:status': 'bash scripts/feltdb-status.sh',
185
+ 'feltdb:reset': 'bash scripts/feltdb-reset.sh',
186
+ 'feltdb:ps': 'bash scripts/feltdb-ps.sh',
187
+ };
188
+ if (runtime === 'browser') {
189
+ // Browser doesn't need container scripts
190
+ return {
191
+ dev: 'vite',
192
+ build: 'tsc && vite build',
193
+ };
194
+ }
195
+ if (runtime === 'node') {
196
+ return {
197
+ dev: 'tsx src/index.ts',
198
+ build: 'tsc',
199
+ start: 'node dist/index.js',
200
+ ...scripts,
201
+ };
202
+ }
203
+ if (runtime === 'self-hosted') {
204
+ return {
205
+ dev: 'docker-compose up -d --build',
206
+ build: 'docker-compose build',
207
+ ...scripts,
208
+ };
209
+ }
210
+ return scripts;
211
+ }
@@ -0,0 +1,202 @@
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 { spawn } from 'child_process';
11
+ import { createProject } from './create.js';
12
+ import { FELTDB_PACKAGE_VERSION } from './package-versions.js';
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ function parseArgs(args) {
15
+ const options = {
16
+ runtime: 'browser',
17
+ framework: 'react',
18
+ distributed: true,
19
+ agents: true,
20
+ capabilities: 'search',
21
+ };
22
+ for (let i = 0; i < args.length; i++) {
23
+ switch (args[i]) {
24
+ case '--runtime':
25
+ options.runtime = args[++i];
26
+ break;
27
+ case '--framework':
28
+ options.framework = args[++i];
29
+ break;
30
+ case '--no-distributed':
31
+ options.distributed = false;
32
+ break;
33
+ case '--no-agents':
34
+ options.agents = false;
35
+ break;
36
+ case '--capabilities':
37
+ options.capabilities = args[++i];
38
+ break;
39
+ }
40
+ }
41
+ return options;
42
+ }
43
+ function run(command, args, cwd) {
44
+ return new Promise((resolve, reject) => {
45
+ const child = spawn(command, args, { cwd, stdio: 'inherit' });
46
+ child.once('error', reject);
47
+ child.once('exit', code => code === 0
48
+ ? resolve()
49
+ : reject(new Error(`${command} exited with status ${code ?? 'unknown'}`)));
50
+ });
51
+ }
52
+ async function select(message, choices, initialValue) {
53
+ let selected = Math.max(0, choices.findIndex(choice => choice.value === initialValue));
54
+ const input = process.stdin;
55
+ const output = process.stdout;
56
+ const wasRaw = input.isRaw;
57
+ readline.emitKeypressEvents(input);
58
+ input.setRawMode(true);
59
+ input.resume();
60
+ const render = (moveUp) => {
61
+ if (moveUp)
62
+ output.write(`\x1b[${choices.length}A`);
63
+ for (let index = 0; index < choices.length; index++) {
64
+ const choice = choices[index];
65
+ const active = index === selected;
66
+ const detail = choice.description ? ` \x1b[2m— ${choice.description}\x1b[22m` : '';
67
+ output.write(`\x1b[2K\r ${active ? '\x1b[36m❯' : ' '} ${choice.label}${active ? '\x1b[0m' : ''}${detail}\n`);
68
+ }
69
+ };
70
+ output.write(`${message}\n`);
71
+ render(false);
72
+ return new Promise(resolve => {
73
+ const finish = () => {
74
+ input.off('keypress', onKeypress);
75
+ input.setRawMode(Boolean(wasRaw));
76
+ input.pause();
77
+ resolve(choices[selected].value);
78
+ };
79
+ const onKeypress = (_value, key) => {
80
+ if (key.ctrl && key.name === 'c') {
81
+ output.write('\n');
82
+ input.setRawMode(Boolean(wasRaw));
83
+ process.exit(130);
84
+ }
85
+ if (key.name === 'up')
86
+ selected = (selected - 1 + choices.length) % choices.length;
87
+ else if (key.name === 'down')
88
+ selected = (selected + 1) % choices.length;
89
+ else if (key.name === 'return' || key.name === 'enter')
90
+ return finish();
91
+ else
92
+ return;
93
+ render(true);
94
+ };
95
+ input.on('keypress', onKeypress);
96
+ });
97
+ }
98
+ async function promptForOptions(defaults) {
99
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
100
+ return defaults;
101
+ console.log('Configure your application. Use ↑/↓ to move and Enter to select.\n');
102
+ const yesNo = [
103
+ { label: 'Yes', value: true },
104
+ { label: 'No', value: false },
105
+ ];
106
+ return {
107
+ runtime: await select('Where should FeltDB run?', [
108
+ { label: 'Browser', value: 'browser', description: 'local-first with durable browser storage' },
109
+ { label: 'Node.js', value: 'node', description: 'application server or worker' },
110
+ { label: 'Self-hosted', value: 'self-hosted', description: 'dedicated FeltDB server' },
111
+ ], defaults.runtime),
112
+ framework: await select('Choose an application framework:', [
113
+ { label: 'React', value: 'react' },
114
+ { label: 'Vanilla TypeScript', value: 'vanilla' },
115
+ ], defaults.framework),
116
+ distributed: await select('Enable distributed operation?', yesNo, defaults.distributed),
117
+ agents: await select('Include an autonomous agent example?', yesNo, defaults.agents),
118
+ capabilities: await select('Choose starter capabilities:', [
119
+ { label: 'Search', value: 'search' },
120
+ { label: 'Vector search', value: 'vector' },
121
+ { label: 'Search + vector search', value: 'search,vector' },
122
+ ], defaults.capabilities),
123
+ };
124
+ }
125
+ async function main() {
126
+ const args = process.argv.slice(2);
127
+ if (args.includes('--help') || args.includes('-h')) {
128
+ console.log(`create-feltdb ${FELTDB_PACKAGE_VERSION}\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`);
129
+ return;
130
+ }
131
+ if (args.includes('--version')) {
132
+ console.log(FELTDB_PACKAGE_VERSION);
133
+ return;
134
+ }
135
+ // Find project name (first non-flag argument)
136
+ let projectName = 'feltdb-app';
137
+ for (let i = 0; i < args.length; i++) {
138
+ const arg = args[i];
139
+ if (!arg.startsWith('-')) {
140
+ projectName = arg;
141
+ break;
142
+ }
143
+ // Skip next arg if current arg is a flag that takes a value
144
+ if (arg === '--runtime' || arg === '--framework' || arg === '--capabilities') {
145
+ i++;
146
+ }
147
+ }
148
+ const shouldAutoYes = args.includes('--yes') || args.includes('-y');
149
+ const shouldInstall = !args.includes('--no-install');
150
+ const shouldStart = !args.includes('--no-start');
151
+ let options = parseArgs(args);
152
+ console.log('\n✨ Creating FeltDB Application\n');
153
+ if (!shouldAutoYes) {
154
+ options = await promptForOptions(options);
155
+ console.log('\nConfiguration:');
156
+ console.log(` Runtime: ${options.runtime}`);
157
+ console.log(` Framework: ${options.framework}`);
158
+ console.log(` Distributed: ${options.distributed ? 'yes' : 'no'}`);
159
+ console.log(` Agents: ${options.agents ? 'yes' : 'no'}`);
160
+ console.log(` Capabilities: ${options.capabilities}\n`);
161
+ }
162
+ try {
163
+ await createProject({
164
+ projectName,
165
+ autoYes: shouldAutoYes,
166
+ templatesDir: path.join(__dirname, '../templates'),
167
+ runtime: options.runtime,
168
+ framework: options.framework,
169
+ distributed: options.distributed,
170
+ agents: options.agents,
171
+ capabilities: options.capabilities,
172
+ });
173
+ console.log('\n✅ FeltDB application created successfully!\n');
174
+ const projectDir = path.resolve(process.cwd(), projectName);
175
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
176
+ if (shouldInstall) {
177
+ console.log('📦 Installing application, Studio, and local AI dependencies...\n');
178
+ await run(npm, ['install'], projectDir);
179
+ }
180
+ if (shouldStart) {
181
+ if (!shouldInstall) {
182
+ console.log(`Start after installing dependencies:\n cd ${projectName}\n npm install\n npm run dev\n`);
183
+ }
184
+ else {
185
+ console.log('\n🚀 Starting the application and FeltDB Studio...\n');
186
+ await run(npm, ['run', 'dev'], projectDir);
187
+ }
188
+ }
189
+ else {
190
+ console.log('Project is ready. Start everything with:');
191
+ console.log(` cd ${projectName}`);
192
+ if (!shouldInstall)
193
+ console.log(' npm install');
194
+ console.log(' npm run dev');
195
+ }
196
+ }
197
+ catch (error) {
198
+ console.error('❌ Failed to create project:', error);
199
+ process.exit(1);
200
+ }
201
+ }
202
+ main();