code-mon-config 1.0.6 ā 1.0.8
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/extensions/example-extenson.js +27 -0
- package/index.js +232 -119
- package/package.json +12 -3
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Example Extension for Code Mon
|
|
2
|
+
// This demonstrates how to add new APIs or hook into the CLI
|
|
3
|
+
|
|
4
|
+
module.exports = ({ app, ROOT, spawn, processes }) => {
|
|
5
|
+
|
|
6
|
+
// Example: add a simple test route
|
|
7
|
+
app.get("/hello", (req, res) => {
|
|
8
|
+
res.json({ message: "Hello from example extension!" });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Example: a custom execute endpoint that uses the process manager
|
|
12
|
+
app.post("/execute-extension", (req, res) => {
|
|
13
|
+
const { command } = req.body;
|
|
14
|
+
if (!command) return res.json({ error: "Command required" });
|
|
15
|
+
|
|
16
|
+
const proc = spawn(command, { cwd: ROOT, shell: true });
|
|
17
|
+
const id = Object.keys(processes).length;
|
|
18
|
+
processes[id] = proc;
|
|
19
|
+
|
|
20
|
+
proc.stdout.on("data", data => console.log(`[ext-${id}]`, data.toString()));
|
|
21
|
+
proc.stderr.on("data", data => console.error(`[ext-${id}]`, data.toString()));
|
|
22
|
+
proc.on("close", code => delete processes[id]);
|
|
23
|
+
|
|
24
|
+
res.json({ success: true, processId: id });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
};
|
package/index.js
CHANGED
|
@@ -21,105 +21,163 @@ const ROOT = process.cwd();
|
|
|
21
21
|
let processes = {};
|
|
22
22
|
let processId = 0;
|
|
23
23
|
|
|
24
|
+
/* ---------------- SAFE PATH ---------------- */
|
|
25
|
+
|
|
26
|
+
function safePath(filename) {
|
|
27
|
+
|
|
28
|
+
if (!filename) {
|
|
29
|
+
throw new Error("Filename required");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const resolved = path.join(ROOT, filename);
|
|
33
|
+
|
|
34
|
+
if (!resolved.startsWith(ROOT)) {
|
|
35
|
+
throw new Error("Invalid path");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return resolved;
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
/* ---------------- FILE TREE ---------------- */
|
|
25
42
|
|
|
26
43
|
function getTree(dir, base = "") {
|
|
27
44
|
|
|
28
|
-
let results = [];
|
|
29
|
-
const list = fs.readdirSync(dir);
|
|
45
|
+
let results = [];
|
|
46
|
+
const list = fs.readdirSync(dir);
|
|
30
47
|
|
|
31
|
-
list.forEach(file => {
|
|
48
|
+
list.forEach(file => {
|
|
32
49
|
|
|
33
|
-
const full = path.join(dir, file);
|
|
34
|
-
const rel = path.join(base, file);
|
|
35
|
-
const stat = fs.statSync(full);
|
|
50
|
+
const full = path.join(dir, file);
|
|
51
|
+
const rel = path.join(base, file);
|
|
52
|
+
const stat = fs.statSync(full);
|
|
36
53
|
|
|
37
|
-
if (stat.isDirectory()) {
|
|
54
|
+
if (stat.isDirectory()) {
|
|
38
55
|
|
|
39
|
-
results.push(rel + "/");
|
|
40
|
-
results = results.concat(getTree(full, rel));
|
|
56
|
+
results.push(rel + "/");
|
|
57
|
+
results = results.concat(getTree(full, rel));
|
|
41
58
|
|
|
42
|
-
} else {
|
|
59
|
+
} else {
|
|
43
60
|
|
|
44
|
-
results.push(rel);
|
|
61
|
+
results.push(rel);
|
|
45
62
|
|
|
46
|
-
}
|
|
63
|
+
}
|
|
47
64
|
|
|
48
|
-
});
|
|
65
|
+
});
|
|
49
66
|
|
|
50
|
-
return results;
|
|
67
|
+
return results;
|
|
51
68
|
}
|
|
52
69
|
|
|
53
70
|
/* ---------------- FILE APIs ---------------- */
|
|
54
71
|
|
|
55
72
|
app.get("/list", (req, res) => {
|
|
56
73
|
|
|
57
|
-
try {
|
|
74
|
+
try {
|
|
58
75
|
|
|
59
|
-
const tree = getTree(ROOT);
|
|
76
|
+
const tree = getTree(ROOT);
|
|
60
77
|
|
|
61
|
-
res.json({
|
|
62
|
-
root: ROOT,
|
|
63
|
-
files: tree
|
|
64
|
-
});
|
|
78
|
+
res.json({
|
|
79
|
+
root: ROOT,
|
|
80
|
+
files: tree
|
|
81
|
+
});
|
|
65
82
|
|
|
66
|
-
} catch (err) {
|
|
83
|
+
} catch (err) {
|
|
67
84
|
|
|
68
|
-
res.json({ error: err.message });
|
|
85
|
+
res.json({ error: err.message });
|
|
69
86
|
|
|
70
|
-
}
|
|
87
|
+
}
|
|
71
88
|
|
|
72
89
|
});
|
|
73
90
|
|
|
91
|
+
/* LOAD FILE */
|
|
92
|
+
|
|
74
93
|
app.get("/load", (req, res) => {
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
try {
|
|
96
|
+
|
|
97
|
+
const filename = req.query.filename;
|
|
98
|
+
const filePath = safePath(filename);
|
|
99
|
+
|
|
100
|
+
if (!fs.existsSync(filePath)) {
|
|
101
|
+
return res.json({ error: "File not found" });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
78
105
|
|
|
79
|
-
|
|
80
|
-
return res.json({ error: "File not found" });
|
|
106
|
+
res.json({ content });
|
|
81
107
|
|
|
82
|
-
|
|
108
|
+
} catch (err) {
|
|
83
109
|
|
|
84
|
-
res.json({
|
|
110
|
+
res.json({ error: err.message });
|
|
111
|
+
|
|
112
|
+
}
|
|
85
113
|
|
|
86
114
|
});
|
|
87
115
|
|
|
116
|
+
/* SAVE FILE */
|
|
117
|
+
|
|
88
118
|
app.post("/save", (req, res) => {
|
|
89
119
|
|
|
90
|
-
|
|
120
|
+
try {
|
|
121
|
+
|
|
122
|
+
const { filename, content } = req.body;
|
|
123
|
+
|
|
124
|
+
const filePath = safePath(filename);
|
|
125
|
+
|
|
126
|
+
fs.writeFileSync(filePath, content || "");
|
|
91
127
|
|
|
92
|
-
|
|
128
|
+
res.json({ success: true });
|
|
93
129
|
|
|
94
|
-
|
|
130
|
+
} catch (err) {
|
|
95
131
|
|
|
96
|
-
res.json({
|
|
132
|
+
res.json({ error: err.message });
|
|
133
|
+
|
|
134
|
+
}
|
|
97
135
|
|
|
98
136
|
});
|
|
99
137
|
|
|
138
|
+
/* CREATE FILE */
|
|
139
|
+
|
|
100
140
|
app.post("/create", (req, res) => {
|
|
101
141
|
|
|
102
|
-
|
|
142
|
+
try {
|
|
143
|
+
|
|
144
|
+
const { filename } = req.body;
|
|
145
|
+
|
|
146
|
+
const filePath = safePath(filename);
|
|
147
|
+
|
|
148
|
+
fs.writeFileSync(filePath, "");
|
|
103
149
|
|
|
104
|
-
|
|
150
|
+
res.json({ success: true });
|
|
105
151
|
|
|
106
|
-
|
|
152
|
+
} catch (err) {
|
|
107
153
|
|
|
108
|
-
res.json({
|
|
154
|
+
res.json({ error: err.message });
|
|
155
|
+
|
|
156
|
+
}
|
|
109
157
|
|
|
110
158
|
});
|
|
111
159
|
|
|
160
|
+
/* DELETE FILE */
|
|
161
|
+
|
|
112
162
|
app.delete("/delete", (req, res) => {
|
|
113
163
|
|
|
114
|
-
|
|
164
|
+
try {
|
|
115
165
|
|
|
116
|
-
const
|
|
166
|
+
const { filename } = req.body;
|
|
117
167
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
168
|
+
const filePath = safePath(filename);
|
|
169
|
+
|
|
170
|
+
if (fs.existsSync(filePath)) {
|
|
171
|
+
fs.unlinkSync(filePath);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
res.json({ success: true });
|
|
121
175
|
|
|
122
|
-
|
|
176
|
+
} catch (err) {
|
|
177
|
+
|
|
178
|
+
res.json({ error: err.message });
|
|
179
|
+
|
|
180
|
+
}
|
|
123
181
|
|
|
124
182
|
});
|
|
125
183
|
|
|
@@ -127,40 +185,51 @@ res.json({ success: true });
|
|
|
127
185
|
|
|
128
186
|
app.post("/execute", (req, res) => {
|
|
129
187
|
|
|
130
|
-
|
|
188
|
+
try {
|
|
131
189
|
|
|
132
|
-
|
|
133
|
-
return res.json({ error: "Command required" });
|
|
134
|
-
}
|
|
190
|
+
const { command } = req.body;
|
|
135
191
|
|
|
136
|
-
|
|
192
|
+
if (!command) {
|
|
193
|
+
return res.json({ error: "Command required" });
|
|
194
|
+
}
|
|
137
195
|
|
|
138
|
-
const
|
|
139
|
-
cwd: ROOT,
|
|
140
|
-
shell: true
|
|
141
|
-
});
|
|
196
|
+
const parts = command.split(" ");
|
|
142
197
|
|
|
143
|
-
const
|
|
198
|
+
const proc = spawn(parts[0], parts.slice(1), {
|
|
199
|
+
cwd: ROOT,
|
|
200
|
+
shell: true
|
|
201
|
+
});
|
|
144
202
|
|
|
145
|
-
|
|
203
|
+
const id = processId++;
|
|
146
204
|
|
|
147
|
-
|
|
148
|
-
console.log(`[${id}]`, data.toString());
|
|
149
|
-
});
|
|
205
|
+
processes[id] = proc;
|
|
150
206
|
|
|
151
|
-
proc.
|
|
152
|
-
console.
|
|
153
|
-
});
|
|
207
|
+
proc.stdout.on("data", data => {
|
|
208
|
+
console.log(`[${id}]`, data.toString());
|
|
209
|
+
});
|
|
154
210
|
|
|
155
|
-
proc.on("
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
211
|
+
proc.stderr.on("data", data => {
|
|
212
|
+
console.error(`[${id}]`, data.toString());
|
|
213
|
+
});
|
|
159
214
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
215
|
+
proc.on("close", code => {
|
|
216
|
+
|
|
217
|
+
delete processes[id];
|
|
218
|
+
|
|
219
|
+
console.log(`Process ${id} exited`);
|
|
220
|
+
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
res.json({
|
|
224
|
+
success: true,
|
|
225
|
+
processId: id
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
} catch (err) {
|
|
229
|
+
|
|
230
|
+
res.json({ error: err.message });
|
|
231
|
+
|
|
232
|
+
}
|
|
164
233
|
|
|
165
234
|
});
|
|
166
235
|
|
|
@@ -168,11 +237,11 @@ processId: id
|
|
|
168
237
|
|
|
169
238
|
app.get("/processes", (req, res) => {
|
|
170
239
|
|
|
171
|
-
const list = Object.keys(processes).map(id => ({
|
|
172
|
-
id
|
|
173
|
-
}));
|
|
240
|
+
const list = Object.keys(processes).map(id => ({
|
|
241
|
+
id
|
|
242
|
+
}));
|
|
174
243
|
|
|
175
|
-
res.json(list);
|
|
244
|
+
res.json(list);
|
|
176
245
|
|
|
177
246
|
});
|
|
178
247
|
|
|
@@ -180,46 +249,87 @@ res.json(list);
|
|
|
180
249
|
|
|
181
250
|
app.post("/stop-process", (req, res) => {
|
|
182
251
|
|
|
183
|
-
|
|
252
|
+
try {
|
|
184
253
|
|
|
185
|
-
|
|
186
|
-
return res.json({ error: "Process not found" });
|
|
187
|
-
}
|
|
254
|
+
const { id } = req.body;
|
|
188
255
|
|
|
189
|
-
processes[id]
|
|
256
|
+
if (!processes[id]) {
|
|
257
|
+
return res.json({ error: "Process not found" });
|
|
258
|
+
}
|
|
190
259
|
|
|
191
|
-
|
|
260
|
+
processes[id].kill();
|
|
192
261
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
262
|
+
delete processes[id];
|
|
263
|
+
|
|
264
|
+
res.json({
|
|
265
|
+
success: true,
|
|
266
|
+
message: "Process stopped"
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
} catch (err) {
|
|
270
|
+
|
|
271
|
+
res.json({ error: err.message });
|
|
272
|
+
|
|
273
|
+
}
|
|
197
274
|
|
|
198
275
|
});
|
|
199
276
|
|
|
277
|
+
/* ---------------- EXTENSION LOADER ---------------- */
|
|
278
|
+
|
|
279
|
+
const EXTENSIONS_DIR = path.join(ROOT, "extensions");
|
|
280
|
+
|
|
281
|
+
if (fs.existsSync(EXTENSIONS_DIR)) {
|
|
282
|
+
|
|
283
|
+
fs.readdirSync(EXTENSIONS_DIR).forEach(file => {
|
|
284
|
+
|
|
285
|
+
if (file.endsWith(".js")) {
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
|
|
289
|
+
require(path.join(EXTENSIONS_DIR, file))({
|
|
290
|
+
app,
|
|
291
|
+
ROOT,
|
|
292
|
+
spawn,
|
|
293
|
+
processes
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
console.log(`š Loaded extension: ${file}`);
|
|
297
|
+
|
|
298
|
+
} catch (err) {
|
|
299
|
+
|
|
300
|
+
console.log(`ā Failed to load extension: ${file}`);
|
|
301
|
+
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
}
|
|
309
|
+
|
|
200
310
|
/* ---------------- START SERVER ---------------- */
|
|
201
311
|
|
|
202
312
|
const server = app.listen(PORT, async () => {
|
|
203
313
|
|
|
204
|
-
console.log("\nš Code Mon Config Starting...\n");
|
|
314
|
+
console.log("\nš Code Mon Config Starting...\n");
|
|
205
315
|
|
|
206
|
-
console.log("š Connected Folder:", ROOT);
|
|
316
|
+
console.log("š Connected Folder:", ROOT);
|
|
207
317
|
|
|
208
|
-
console.log("\nā
Your terminal has been connected with Code Mon Code Space\n");
|
|
318
|
+
console.log("\nā
Your terminal has been connected with Code Mon Code Space\n");
|
|
209
319
|
|
|
210
|
-
const url = "
|
|
320
|
+
const url = "http://localhost:3000";
|
|
211
321
|
|
|
212
|
-
console.log("š Opening:", url);
|
|
322
|
+
console.log("š Opening:", url);
|
|
213
323
|
|
|
214
|
-
try {
|
|
324
|
+
try {
|
|
215
325
|
|
|
216
|
-
await open(url);
|
|
326
|
+
await open(url);
|
|
217
327
|
|
|
218
|
-
} catch {
|
|
328
|
+
} catch {
|
|
219
329
|
|
|
220
|
-
console.log("Open manually:", url);
|
|
330
|
+
console.log("Open manually:", url);
|
|
221
331
|
|
|
222
|
-
}
|
|
332
|
+
}
|
|
223
333
|
|
|
224
334
|
});
|
|
225
335
|
|
|
@@ -229,48 +339,51 @@ const wss = new WebSocket.Server({ server });
|
|
|
229
339
|
|
|
230
340
|
wss.on("connection", ws => {
|
|
231
341
|
|
|
232
|
-
console.log("š¢ Terminal connected");
|
|
342
|
+
console.log("š¢ Terminal connected");
|
|
233
343
|
|
|
234
|
-
let proc;
|
|
344
|
+
let proc;
|
|
235
345
|
|
|
236
|
-
ws.on("message", msg => {
|
|
346
|
+
ws.on("message", msg => {
|
|
237
347
|
|
|
238
|
-
const command = msg.toString();
|
|
348
|
+
const command = msg.toString();
|
|
239
349
|
|
|
240
|
-
if (!proc) {
|
|
350
|
+
if (!proc) {
|
|
241
351
|
|
|
242
|
-
proc = spawn(command, {
|
|
243
|
-
cwd: ROOT,
|
|
244
|
-
shell: true
|
|
245
|
-
});
|
|
352
|
+
proc = spawn(command, {
|
|
353
|
+
cwd: ROOT,
|
|
354
|
+
shell: true
|
|
355
|
+
});
|
|
246
356
|
|
|
247
|
-
proc.stdout.on("data", data => {
|
|
248
|
-
ws.send(data.toString());
|
|
249
|
-
});
|
|
357
|
+
proc.stdout.on("data", data => {
|
|
358
|
+
ws.send(data.toString());
|
|
359
|
+
});
|
|
250
360
|
|
|
251
|
-
proc.stderr.on("data", data => {
|
|
252
|
-
ws.send(data.toString());
|
|
253
|
-
});
|
|
361
|
+
proc.stderr.on("data", data => {
|
|
362
|
+
ws.send(data.toString());
|
|
363
|
+
});
|
|
254
364
|
|
|
255
|
-
proc.on("close", code => {
|
|
256
|
-
ws.send(`\nProcess exited with code ${code}\n`);
|
|
257
|
-
proc = null;
|
|
258
|
-
});
|
|
365
|
+
proc.on("close", code => {
|
|
259
366
|
|
|
260
|
-
|
|
367
|
+
ws.send(`\nProcess exited with code ${code}\n`);
|
|
261
368
|
|
|
262
|
-
proc
|
|
369
|
+
proc = null;
|
|
263
370
|
|
|
264
|
-
}
|
|
371
|
+
});
|
|
265
372
|
|
|
266
|
-
}
|
|
373
|
+
} else {
|
|
267
374
|
|
|
268
|
-
|
|
375
|
+
proc.stdin.write(command + "\n");
|
|
269
376
|
|
|
270
|
-
|
|
377
|
+
}
|
|
271
378
|
|
|
272
|
-
|
|
379
|
+
});
|
|
273
380
|
|
|
274
|
-
|
|
381
|
+
ws.on("close", () => {
|
|
382
|
+
|
|
383
|
+
if (proc) proc.kill();
|
|
384
|
+
|
|
385
|
+
console.log("š“ Terminal disconnected");
|
|
386
|
+
|
|
387
|
+
});
|
|
275
388
|
|
|
276
389
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-mon-config",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Code Mon Code Space CLI",
|
|
3
|
+
"version": "1.0.8",
|
|
4
|
+
"description": "Code Mon Code Space CLI with plugin/extension system",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"code-mon-config": "./index.js"
|
|
@@ -14,5 +14,14 @@
|
|
|
14
14
|
"express": "^4.22.1",
|
|
15
15
|
"open": "^10.2.0",
|
|
16
16
|
"ws": "^8.19.0"
|
|
17
|
-
}
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cli",
|
|
20
|
+
"terminal",
|
|
21
|
+
"websocket",
|
|
22
|
+
"extensions",
|
|
23
|
+
"code-mon-config"
|
|
24
|
+
],
|
|
25
|
+
"author": "C69P2W",
|
|
26
|
+
"license": "MIT"
|
|
18
27
|
}
|