buddy-workbench 0.1.30 → 0.1.32
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 +14 -0
- package/bin/devbuddy.js +102 -11
- package/package.json +1 -1
- package/server/config.js +1 -0
- package/server/repositories/presentations.js +31 -0
- package/server/routes/presentations.js +19 -0
- package/server.js +2 -0
- package/ui/dist/assets/index-DfO9LrPe.js +534 -0
- package/ui/dist/assets/index-jOfBtkeJ.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-D-d3VqIY.js +0 -534
- package/ui/dist/assets/index-D15KPzAZ.css +0 -1
package/README.md
CHANGED
|
@@ -12,6 +12,20 @@ npm start
|
|
|
12
12
|
|
|
13
13
|
Open `http://localhost:3100`. To use another port, run `PORT=4000 npm start`. Startup attempts to stop any process currently using the selected port.
|
|
14
14
|
|
|
15
|
+
When using the `devbuddy` CLI, pass the port directly with `--port`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
devbuddy start --port 4000
|
|
19
|
+
devbuddy restart --port 4000
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Update the globally installed CLI from npm:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
devbuddy self-update
|
|
26
|
+
devbuddy self-update 0.1.32
|
|
27
|
+
```
|
|
28
|
+
|
|
15
29
|
## Running on macOS
|
|
16
30
|
|
|
17
31
|
### Double-click Launcher (Terminal)
|
package/bin/devbuddy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { execSync, spawn } from 'node:child_process';
|
|
3
|
+
import { execFileSync, execSync, spawn } from 'node:child_process';
|
|
4
4
|
import { existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
@@ -13,6 +13,85 @@ const dataDir = join(root, 'data');
|
|
|
13
13
|
const pidFile = join(dataDir, 'devbuddy.pid');
|
|
14
14
|
const logFile = join(dataDir, 'devbuddy.log');
|
|
15
15
|
const serverJs = join(root, 'server.js');
|
|
16
|
+
const packageJsonFile = join(root, 'package.json');
|
|
17
|
+
const defaultPort = 3100;
|
|
18
|
+
|
|
19
|
+
function parsePort(args) {
|
|
20
|
+
let port;
|
|
21
|
+
|
|
22
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
23
|
+
const arg = args[index];
|
|
24
|
+
if (arg === '--port') {
|
|
25
|
+
if (port !== undefined || args[index + 1] === undefined) {
|
|
26
|
+
throw new Error('Usage: --port <number>');
|
|
27
|
+
}
|
|
28
|
+
port = args[++index];
|
|
29
|
+
} else if (arg.startsWith('--port=')) {
|
|
30
|
+
if (port !== undefined) throw new Error('Port was specified more than once.');
|
|
31
|
+
port = arg.slice('--port='.length);
|
|
32
|
+
} else {
|
|
33
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const value = port ?? process.env.PORT ?? defaultPort;
|
|
38
|
+
if (!/^\d+$/.test(String(value)) || Number(value) < 1 || Number(value) > 65535) {
|
|
39
|
+
throw new Error('Port must be a number between 1 and 65535.');
|
|
40
|
+
}
|
|
41
|
+
return Number(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function packageDetails() {
|
|
45
|
+
return JSON.parse(readFileSync(packageJsonFile, 'utf8'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function npmCommand() {
|
|
49
|
+
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseUpdateVersion(args) {
|
|
53
|
+
if (args.length > 1) throw new Error('Usage: devbuddy self-update [version]');
|
|
54
|
+
if (!args[0]) return 'latest';
|
|
55
|
+
if (args[0].startsWith('-') || /\s/.test(args[0])) {
|
|
56
|
+
throw new Error('Version must be a package version, tag, or dist-tag.');
|
|
57
|
+
}
|
|
58
|
+
return args[0];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function latestPublishedVersion(packageName) {
|
|
62
|
+
try {
|
|
63
|
+
return execFileSync(npmCommand(), ['view', packageName, 'version'], {
|
|
64
|
+
cwd: root,
|
|
65
|
+
encoding: 'utf8',
|
|
66
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
67
|
+
}).trim();
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error(`Unable to query the latest ${packageName} version from npm.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function selfUpdate(args) {
|
|
74
|
+
const details = packageDetails();
|
|
75
|
+
const version = parseUpdateVersion(args);
|
|
76
|
+
const currentVersion = details.version;
|
|
77
|
+
const targetVersion = version === 'latest' ? latestPublishedVersion(details.name) : version;
|
|
78
|
+
|
|
79
|
+
if (version === 'latest' && targetVersion === currentVersion) {
|
|
80
|
+
console.log(`${details.name} is already up to date (${currentVersion}).`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log(`Updating ${details.name} from ${currentVersion} to ${version}…`);
|
|
85
|
+
try {
|
|
86
|
+
execFileSync(npmCommand(), ['install', '--global', `${details.name}@${version}`], {
|
|
87
|
+
cwd: root,
|
|
88
|
+
stdio: 'inherit'
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
throw new Error(`Unable to install ${details.name}@${version}. Check npm permissions and network access.`);
|
|
92
|
+
}
|
|
93
|
+
console.log(`Updated ${details.name} to ${targetVersion}.`);
|
|
94
|
+
}
|
|
16
95
|
|
|
17
96
|
function isProcessRunning(pid) {
|
|
18
97
|
try {
|
|
@@ -58,11 +137,11 @@ function sendSignal(pid, signal) {
|
|
|
58
137
|
}
|
|
59
138
|
}
|
|
60
139
|
|
|
61
|
-
async function start() {
|
|
140
|
+
async function start(port) {
|
|
62
141
|
const existingPid = getRunningPid();
|
|
63
142
|
if (existingPid) {
|
|
64
143
|
console.log(`DevBuddy is already running (PID ${existingPid}).`);
|
|
65
|
-
console.log(`URL: http://localhost:${
|
|
144
|
+
console.log(`URL: http://localhost:${port}`);
|
|
66
145
|
return;
|
|
67
146
|
}
|
|
68
147
|
|
|
@@ -75,7 +154,7 @@ async function start() {
|
|
|
75
154
|
detached: true,
|
|
76
155
|
stdio: ['ignore', out, err],
|
|
77
156
|
cwd: root,
|
|
78
|
-
env: { ...process.env }
|
|
157
|
+
env: { ...process.env, PORT: String(port) }
|
|
79
158
|
});
|
|
80
159
|
|
|
81
160
|
if (!child.pid) {
|
|
@@ -90,7 +169,6 @@ async function start() {
|
|
|
90
169
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
91
170
|
|
|
92
171
|
if (isProcessRunning(child.pid)) {
|
|
93
|
-
const port = process.env.PORT || 3100;
|
|
94
172
|
console.log(`DevBuddy started successfully (PID ${child.pid}).`);
|
|
95
173
|
console.log(`URL: http://localhost:${port}`);
|
|
96
174
|
console.log(`Logs: ${logFile}`);
|
|
@@ -133,10 +211,9 @@ async function stop() {
|
|
|
133
211
|
console.log('DevBuddy stopped.');
|
|
134
212
|
}
|
|
135
213
|
|
|
136
|
-
function status() {
|
|
214
|
+
function status(port) {
|
|
137
215
|
const pid = getRunningPid();
|
|
138
216
|
if (pid) {
|
|
139
|
-
const port = process.env.PORT || 3100;
|
|
140
217
|
console.log(`DevBuddy is running (PID ${pid}).`);
|
|
141
218
|
console.log(`URL: http://localhost:${port}`);
|
|
142
219
|
} else {
|
|
@@ -149,33 +226,47 @@ function help() {
|
|
|
149
226
|
DevBuddy CLI
|
|
150
227
|
|
|
151
228
|
Usage:
|
|
152
|
-
devbuddy <command>
|
|
229
|
+
devbuddy <command> [--port <number>]
|
|
153
230
|
|
|
154
231
|
Commands:
|
|
155
232
|
start Start DevBuddy in the background
|
|
156
233
|
stop Stop the running DevBuddy instance
|
|
157
234
|
status Check the status of DevBuddy
|
|
158
235
|
restart Restart DevBuddy
|
|
236
|
+
self-update [version]
|
|
237
|
+
Update the globally installed DevBuddy CLI
|
|
159
238
|
help Display this help message
|
|
239
|
+
|
|
240
|
+
Options:
|
|
241
|
+
--port <number> Port to listen on (default: 3100)
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
devbuddy start --port 4000
|
|
160
245
|
`);
|
|
161
246
|
}
|
|
162
247
|
|
|
163
248
|
async function main() {
|
|
164
249
|
const command = process.argv[2] || 'help';
|
|
250
|
+
const args = process.argv.slice(3);
|
|
165
251
|
|
|
166
252
|
switch (command.toLowerCase()) {
|
|
167
253
|
case 'start':
|
|
168
|
-
await start();
|
|
254
|
+
await start(parsePort(args));
|
|
169
255
|
break;
|
|
170
256
|
case 'stop':
|
|
171
257
|
await stop();
|
|
172
258
|
break;
|
|
173
259
|
case 'status':
|
|
174
|
-
status();
|
|
260
|
+
status(parsePort(args));
|
|
175
261
|
break;
|
|
176
262
|
case 'restart':
|
|
177
263
|
await stop();
|
|
178
|
-
await start();
|
|
264
|
+
await start(parsePort(args));
|
|
265
|
+
break;
|
|
266
|
+
case 'self-update':
|
|
267
|
+
case 'selfupdate':
|
|
268
|
+
case 'selfupgrade':
|
|
269
|
+
selfUpdate(args);
|
|
179
270
|
break;
|
|
180
271
|
case 'help':
|
|
181
272
|
case '-h':
|
package/package.json
CHANGED
package/server/config.js
CHANGED
|
@@ -14,6 +14,7 @@ export const paths = {
|
|
|
14
14
|
errors: join(root, 'data', 'errors.json'),
|
|
15
15
|
postman: join(root, 'data', 'postman.json'),
|
|
16
16
|
branchSync: join(root, 'data', 'branch-sync.json'),
|
|
17
|
+
presentations: join(root, 'data', 'presentations.json'),
|
|
17
18
|
shutdownLog: join(root, 'data', 'shutdown.log'),
|
|
18
19
|
clipboardDir: join(root, 'data', 'clipboard'),
|
|
19
20
|
plugins: join(root, 'plugins'),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { paths } from '../config.js';
|
|
4
|
+
|
|
5
|
+
const EMPTY_DATA = { plans: [], activePlan: null };
|
|
6
|
+
|
|
7
|
+
export function getPresentations() {
|
|
8
|
+
try {
|
|
9
|
+
if (!existsSync(paths.presentations)) return EMPTY_DATA;
|
|
10
|
+
const parsed = JSON.parse(readFileSync(paths.presentations, 'utf8'));
|
|
11
|
+
return {
|
|
12
|
+
plans: Array.isArray(parsed.plans) ? parsed.plans : [],
|
|
13
|
+
activePlan: parsed.activePlan || null
|
|
14
|
+
};
|
|
15
|
+
} catch {
|
|
16
|
+
return EMPTY_DATA;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function savePresentations(data) {
|
|
21
|
+
mkdirSync(dirname(paths.presentations), { recursive: true });
|
|
22
|
+
const current = getPresentations();
|
|
23
|
+
const next = {
|
|
24
|
+
plans: Array.isArray(data.plans) ? data.plans : [],
|
|
25
|
+
activePlan: Object.prototype.hasOwnProperty.call(data, 'activePlan')
|
|
26
|
+
? data.activePlan || null
|
|
27
|
+
: current.activePlan
|
|
28
|
+
};
|
|
29
|
+
writeFileSync(paths.presentations, JSON.stringify(next, null, 2), 'utf8');
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { getPresentations, savePresentations } from '../repositories/presentations.js';
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
router.get('/', (_req, res) => {
|
|
7
|
+
res.json(getPresentations());
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
router.put('/', (req, res) => {
|
|
11
|
+
res.json(savePresentations(req.body || {}));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
router.put('/active', (req, res) => {
|
|
15
|
+
const current = getPresentations();
|
|
16
|
+
res.json(savePresentations({ ...current, activePlan: req.body?.plan || null }));
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export default router;
|
package/server.js
CHANGED
|
@@ -23,6 +23,7 @@ import fileOrganizerRoutes from './server/routes/file-organizer.js';
|
|
|
23
23
|
import branchSyncRoutes from './server/routes/branch-sync.js';
|
|
24
24
|
import updateRoutes from './server/routes/updates.js';
|
|
25
25
|
import dataBackupRoutes from './server/routes/data-backup.js';
|
|
26
|
+
import presentationsRoutes from './server/routes/presentations.js';
|
|
26
27
|
import { addErrorRecord } from './server/repositories/errors.js';
|
|
27
28
|
import { startClipboardCapture } from './server/services/clipboard-history.js';
|
|
28
29
|
|
|
@@ -81,6 +82,7 @@ app.use('/api/file-organizer', fileOrganizerRoutes);
|
|
|
81
82
|
app.use('/api/branch-sync', branchSyncRoutes);
|
|
82
83
|
app.use('/api/updates', updateRoutes);
|
|
83
84
|
app.use('/api/data-backup', dataBackupRoutes);
|
|
85
|
+
app.use('/api/presentations', presentationsRoutes);
|
|
84
86
|
|
|
85
87
|
app.use((err, req, res, _next) => {
|
|
86
88
|
addErrorRecord({
|