feedeas 0.1.0-alpha.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.
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bun
2
+ import { Command } from 'commander';
3
+ import { createServer } from './server/index';
4
+ import open from 'open';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import fs from 'fs';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ const program = new Command();
13
+
14
+ program
15
+ .name('feedeas')
16
+ .description('Feedeas CLI - Start your local editor')
17
+ .version('0.0.1');
18
+
19
+ program
20
+ .command('start')
21
+ .description('Start the Feedeas server in the current directory')
22
+ .option('-p, --port <number>', 'Port to run on', '3000')
23
+ .option('--no-open', 'Do not open browser')
24
+ .action(async (options) => {
25
+ const port = parseInt(options.port);
26
+ const cwd = process.cwd();
27
+
28
+ console.log(`Starting Feedeas in ${cwd}...`);
29
+
30
+ // Determine static root
31
+ // Try resolving dist/ui relative to this file
32
+ // In dev: src/cli/index.ts -> ../../dist/ui
33
+ // In prod: dist/cli/index.js -> ../ui
34
+
35
+ let staticRoot = path.resolve(__dirname, '../../dist/ui');
36
+ if (!fs.existsSync(staticRoot)) {
37
+ staticRoot = path.resolve(__dirname, '../ui');
38
+ }
39
+
40
+ // Fallback for when running from project root in dev without build
41
+ if (!fs.existsSync(staticRoot)) {
42
+ // Maybe we are in dev and want to point to src/ui? No, we serve built assets.
43
+ // User must build ui first.
44
+ console.warn(`Warning: UI assets not found at ${staticRoot}. Did you run 'bun run build'?`);
45
+ }
46
+
47
+ const app = createServer(staticRoot);
48
+
49
+ const server = Bun.serve({
50
+ port,
51
+ fetch: app.fetch,
52
+ });
53
+
54
+ const url = `http://localhost:${port}`;
55
+ console.log(`Server running at ${url}`);
56
+
57
+ if (options.open) {
58
+ await open(url);
59
+ }
60
+ });
61
+
62
+ // Default action
63
+ if (!process.argv.slice(2).length) {
64
+ program.parse(['start'], { from: 'user' });
65
+ } else {
66
+ program.parse(process.argv);
67
+ }
@@ -0,0 +1,57 @@
1
+ import { Hono } from 'hono';
2
+ import { readdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+
5
+ const api = new Hono();
6
+
7
+ // Helper to get absolute path
8
+ const getPath = (cwd: string, path: string) => join(cwd, path);
9
+
10
+ // List files
11
+ api.get('/fs/list', async (c) => {
12
+ const cwd = process.cwd();
13
+ // Optional: Accept a query param for subdirectory
14
+ try {
15
+ const files = await readdir(cwd, { withFileTypes: true });
16
+ const result = files.map(f => ({
17
+ name: f.name,
18
+ isDirectory: f.isDirectory(),
19
+ size: 0 // Simplification for now
20
+ }));
21
+ return c.json({ path: cwd, files: result });
22
+ } catch (e: any) {
23
+ return c.json({ error: e.message }, 500);
24
+ }
25
+ });
26
+
27
+ // Read file
28
+ api.get('/fs/read', async (c) => {
29
+ const path = c.req.query('path');
30
+ if (!path) return c.json({ error: 'Path required' }, 400);
31
+
32
+ try {
33
+ const fullPath = getPath(process.cwd(), path);
34
+ const content = await readFile(fullPath, 'utf-8');
35
+ return c.json({ content });
36
+ } catch (e: any) {
37
+ return c.json({ error: e.message }, 500);
38
+ }
39
+ });
40
+
41
+ // Write file
42
+ api.post('/fs/write', async (c) => {
43
+ const body = await c.req.json();
44
+ const { path, content } = body;
45
+
46
+ if (!path || content === undefined) return c.json({ error: 'Path and content required' }, 400);
47
+
48
+ try {
49
+ const fullPath = getPath(process.cwd(), path);
50
+ await writeFile(fullPath, content, 'utf-8');
51
+ return c.json({ success: true });
52
+ } catch (e: any) {
53
+ return c.json({ error: e.message }, 500);
54
+ }
55
+ });
56
+
57
+ export default api;
@@ -0,0 +1,53 @@
1
+ import { Hono } from 'hono';
2
+ import { serveStatic } from 'hono/bun';
3
+ import { cors } from 'hono/cors';
4
+ import api from './api';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const app = new Hono();
9
+
10
+ // CORS is helpful if we run UI on different port in dev
11
+ app.use('/*', cors());
12
+
13
+ // Mount API
14
+ app.route('/api', api);
15
+
16
+ // Serve Static UI
17
+ // We need to determine where 'dist/ui' is located relative to this file
18
+ // In dev: src/cli/server/index.ts -> ../../../dist/ui
19
+ // In prod: dist/cli/index.js (bundled) -> ../ui
20
+ // But if bundled, __dirname might be part of the bundle?
21
+ // Actually, Bun.resolveSync or checking relative paths is safer.
22
+
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = path.dirname(__filename);
25
+
26
+ // Simple heuristic: check if ../../../dist/ui exists (dev), else ../ui (prod)
27
+ // Actually, for simplicity, we will assume we are running from project root in dev,
28
+ // or installed globally.
29
+ // Let's pass the static root from the caller (CLI entry point) to be sure.
30
+
31
+ export const createServer = (staticRoot: string) => {
32
+ const app = new Hono();
33
+
34
+ app.use('/*', cors());
35
+ app.route('/api', api);
36
+
37
+ // Serve static files
38
+ app.use('/*', serveStatic({
39
+ root: staticRoot,
40
+ // If file not found, serve index.html for SPA
41
+ onNotFound: (path, c) => {
42
+ return c.req.path.startsWith('/api') ? undefined : void 0;
43
+ }
44
+ }));
45
+
46
+ // Fallback to index.html for SPA routing
47
+ app.get('*', serveStatic({
48
+ root: staticRoot,
49
+ path: 'index.html'
50
+ }));
51
+
52
+ return app;
53
+ }