olly-molly 0.1.5 → 0.1.7

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.
Files changed (2) hide show
  1. package/bin/cli.js +103 -99
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -6,143 +6,147 @@ const fs = require('fs');
6
6
  const os = require('os');
7
7
  const https = require('https');
8
8
 
9
- const APP_NAME = 'olly-molly';
10
9
  const REPO = 'ruucm/olly-molly';
11
10
  const APP_DIR = path.join(os.homedir(), '.olly-molly');
11
+ const DB_DIR = path.join(APP_DIR, 'db');
12
12
  const TARBALL_URL = `https://github.com/${REPO}/archive/refs/heads/main.tar.gz`;
13
13
  const VERSION_URL = `https://raw.githubusercontent.com/${REPO}/main/package.json`;
14
14
 
15
- console.log('\nšŸ™ Olly Molly - Your AI Development Team\n');
15
+ console.log('\nšŸ™ Olly Molly\n');
16
16
 
17
17
  function fetchJSON(url) {
18
18
  return new Promise((resolve, reject) => {
19
- const get = (downloadUrl) => {
20
- https.get(downloadUrl, (res) => {
21
- if (res.statusCode === 302 || res.statusCode === 301) {
22
- get(res.headers.location);
23
- return;
24
- }
19
+ const get = (u) => {
20
+ https.get(u, (res) => {
21
+ if (res.statusCode === 302 || res.statusCode === 301) return get(res.headers.location);
25
22
  let data = '';
26
- res.on('data', chunk => data += chunk);
27
- res.on('end', () => {
28
- try {
29
- resolve(JSON.parse(data));
30
- } catch {
31
- reject(new Error('Invalid JSON'));
32
- }
33
- });
23
+ res.on('data', c => data += c);
24
+ res.on('end', () => { try { resolve(JSON.parse(data)); } catch { reject(); } });
34
25
  }).on('error', reject);
35
26
  };
36
27
  get(url);
37
28
  });
38
29
  }
39
30
 
40
- function downloadAndExtract(url, destDir) {
31
+ function download(url, destDir) {
41
32
  return new Promise((resolve, reject) => {
42
- const tempFile = path.join(os.tmpdir(), 'olly-molly.tar.gz');
43
- const file = fs.createWriteStream(tempFile);
44
-
45
- console.log('šŸ“„ Downloading...');
46
-
47
- const download = (downloadUrl) => {
48
- https.get(downloadUrl, (response) => {
49
- if (response.statusCode === 302 || response.statusCode === 301) {
50
- download(response.headers.location);
51
- return;
52
- }
53
- if (response.statusCode !== 200) {
54
- reject(new Error(`Download failed: ${response.statusCode}`));
55
- return;
56
- }
57
- response.pipe(file);
33
+ const tmp = path.join(os.tmpdir(), 'olly-molly.tar.gz');
34
+ const file = fs.createWriteStream(tmp);
35
+ const get = (u) => {
36
+ https.get(u, (res) => {
37
+ if (res.statusCode === 302 || res.statusCode === 301) return get(res.headers.location);
38
+ if (res.statusCode !== 200) return reject(new Error('Download failed'));
39
+ res.pipe(file);
58
40
  file.on('finish', () => {
59
41
  file.close();
60
- console.log('šŸ“¦ Extracting...');
61
- if (!fs.existsSync(destDir)) {
62
- fs.mkdirSync(destDir, { recursive: true });
63
- }
64
- try {
65
- execSync(`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`, { stdio: 'pipe' });
66
- fs.unlinkSync(tempFile);
67
- resolve();
68
- } catch (err) {
69
- reject(err);
70
- }
42
+ fs.mkdirSync(destDir, { recursive: true });
43
+ execSync(`tar -xzf "${tmp}" -C "${destDir}" --strip-components=1`, { stdio: 'pipe' });
44
+ fs.unlinkSync(tmp);
45
+ resolve();
71
46
  });
72
47
  }).on('error', reject);
73
48
  };
74
- download(url);
49
+ get(url);
75
50
  });
76
51
  }
77
52
 
78
- async function getLocalVersion() {
79
- const pkgPath = path.join(APP_DIR, 'package.json');
80
- if (!fs.existsSync(pkgPath)) return null;
53
+ function getLocalVersion() {
81
54
  try {
82
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
55
+ const pkg = JSON.parse(fs.readFileSync(path.join(APP_DIR, 'package.json'), 'utf8'));
83
56
  return pkg.version;
84
- } catch {
85
- return null;
57
+ } catch { return null; }
58
+ }
59
+
60
+ // Backup and restore user's database
61
+ function backupDB() {
62
+ const backupDir = path.join(os.tmpdir(), 'olly-molly-db-backup');
63
+ if (fs.existsSync(DB_DIR)) {
64
+ fs.cpSync(DB_DIR, backupDir, { recursive: true });
65
+ return backupDir;
66
+ }
67
+ return null;
68
+ }
69
+
70
+ function restoreDB(backupDir) {
71
+ if (backupDir && fs.existsSync(backupDir)) {
72
+ fs.mkdirSync(DB_DIR, { recursive: true });
73
+ // Only restore sqlite files, not schema files
74
+ const files = fs.readdirSync(backupDir);
75
+ for (const file of files) {
76
+ if (file.endsWith('.sqlite') || file.endsWith('.sqlite-shm') || file.endsWith('.sqlite-wal')) {
77
+ fs.copyFileSync(path.join(backupDir, file), path.join(DB_DIR, file));
78
+ }
79
+ }
80
+ fs.rmSync(backupDir, { recursive: true, force: true });
86
81
  }
87
82
  }
88
83
 
89
84
  async function main() {
85
+ let needsInstall = false;
86
+ let needsBuild = false;
87
+
88
+ // Check for updates
89
+ const localVersion = getLocalVersion();
90
+ let remoteVersion = null;
91
+
90
92
  try {
91
- // Check remote version
92
- let remoteVersion = null;
93
- try {
94
- const remotePkg = await fetchJSON(VERSION_URL);
95
- remoteVersion = remotePkg.version;
96
- } catch {
97
- // Offline or error - continue with local
98
- }
93
+ remoteVersion = (await fetchJSON(VERSION_URL)).version;
94
+ } catch {
95
+ // Offline - continue with local
96
+ }
99
97
 
100
- const localVersion = await getLocalVersion();
98
+ // Update if version changed (preserve DB!)
99
+ if (localVersion && remoteVersion && localVersion !== remoteVersion) {
100
+ console.log(`šŸ”„ Updating ${localVersion} → ${remoteVersion}\n`);
101
101
 
102
- // Auto-update if versions differ
103
- if (localVersion && remoteVersion && localVersion !== remoteVersion) {
104
- console.log(`šŸ”„ New version available: ${localVersion} → ${remoteVersion}\n`);
105
- console.log(' Updating...\n');
106
- fs.rmSync(APP_DIR, { recursive: true, force: true });
107
- }
108
-
109
- // Download if not exists
110
- if (!fs.existsSync(APP_DIR)) {
111
- console.log('šŸ“¦ Downloading Olly Molly...\n');
112
- await downloadAndExtract(TARBALL_URL, APP_DIR);
113
- console.log('āœ… Downloaded!\n');
114
- }
102
+ // Backup DB before update
103
+ const dbBackup = backupDB();
104
+
105
+ // Remove app (but DB is backed up)
106
+ fs.rmSync(APP_DIR, { recursive: true, force: true });
107
+
108
+ // Download new version
109
+ console.log('šŸ“„ Downloading...');
110
+ await download(TARBALL_URL, APP_DIR);
111
+ console.log('āœ… Done\n');
112
+
113
+ // Restore DB
114
+ restoreDB(dbBackup);
115
+
116
+ needsInstall = true;
117
+ needsBuild = true;
118
+ }
115
119
 
116
- // Install dependencies if needed
117
- const nodeModulesPath = path.join(APP_DIR, 'node_modules');
118
- if (!fs.existsSync(nodeModulesPath)) {
119
- console.log('šŸ“¦ Installing dependencies...\n');
120
- execSync('npm install --omit=dev', { cwd: APP_DIR, stdio: 'inherit' });
121
- }
120
+ // First time download
121
+ if (!fs.existsSync(APP_DIR)) {
122
+ console.log('šŸ“„ Downloading...');
123
+ await download(TARBALL_URL, APP_DIR);
124
+ console.log('āœ… Done\n');
125
+ needsInstall = true;
126
+ needsBuild = true;
127
+ }
122
128
 
123
- // Build if needed
124
- const nextPath = path.join(APP_DIR, '.next');
125
- if (!fs.existsSync(nextPath)) {
126
- console.log('šŸ”Ø Building (first time only)...\n');
127
- execSync('npm run build', { cwd: APP_DIR, stdio: 'inherit' });
128
- }
129
+ // Install if needed
130
+ if (needsInstall || !fs.existsSync(path.join(APP_DIR, 'node_modules'))) {
131
+ console.log('šŸ“¦ Installing dependencies...\n');
132
+ execSync('npm install --omit=dev', { cwd: APP_DIR, stdio: 'inherit' });
133
+ }
129
134
 
130
- console.log('\nšŸš€ http://localhost:1234\n');
135
+ // Build if needed
136
+ if (needsBuild || !fs.existsSync(path.join(APP_DIR, '.next'))) {
137
+ console.log('\nšŸ”Ø Building...\n');
138
+ execSync('npm run build', { cwd: APP_DIR, stdio: 'inherit' });
139
+ }
131
140
 
132
- // Start server
133
- const server = spawn('npx', ['next', 'start', '--port', '1234'], {
134
- cwd: APP_DIR,
135
- stdio: 'inherit'
136
- });
141
+ console.log('\nšŸš€ http://localhost:1234\n');
137
142
 
138
- server.on('close', (code) => process.exit(code || 0));
139
- process.on('SIGINT', () => { server.kill('SIGINT'); });
140
- process.on('SIGTERM', () => { server.kill('SIGTERM'); });
143
+ const server = spawn('npx', ['next', 'start', '--port', '1234'], {
144
+ cwd: APP_DIR, stdio: 'inherit'
145
+ });
141
146
 
142
- } catch (error) {
143
- console.error('āŒ Error:', error.message);
144
- process.exit(1);
145
- }
147
+ server.on('close', (code) => process.exit(code || 0));
148
+ process.on('SIGINT', () => server.kill('SIGINT'));
149
+ process.on('SIGTERM', () => server.kill('SIGTERM'));
146
150
  }
147
151
 
148
- main();
152
+ main().catch(e => { console.error('āŒ', e.message); process.exit(1); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "olly-molly",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Your AI Development Team, Running Locally - Manage AI agents (PM, Frontend, Backend, QA) from a beautiful kanban board",
5
5
  "keywords": [
6
6
  "ai",