@chriscode/devmux 1.2.0 → 1.3.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.
- package/dist/{chunk-JDD6USSA.js → chunk-32R7KDZB.js} +8 -0
- package/dist/chunk-66UOCF5R.js +36 -0
- package/dist/chunk-6EU6ODXX.js +372 -0
- package/dist/{chunk-FVUGZCQ3.js → chunk-ALENFKSX.js} +148 -357
- package/dist/chunk-T6I3CPOV.js +437 -0
- package/dist/cli.js +269 -15
- package/dist/dashboard-3GHLOSV3.js +8 -0
- package/dist/index.d.ts +68 -31
- package/dist/index.js +21 -8
- package/dist/{server-manager-DO25DFFW.js → server-manager-6EZWZK56.js} +7 -4
- package/dist/watch/watcher-cli.js +5 -2
- package/package.json +6 -2
- package/dist/chunk-MLKGABMK.js +0 -9
|
@@ -1,27 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
createRingBuffer,
|
|
7
|
-
ensureOutputDir,
|
|
8
|
-
getPendingEvents,
|
|
9
|
-
getQueuePath,
|
|
10
|
-
isStackTraceLine,
|
|
11
|
-
matchPatterns,
|
|
12
|
-
readQueue,
|
|
13
|
-
resolvePatterns,
|
|
14
|
-
startWatcher,
|
|
15
|
-
updateEventStatus,
|
|
16
|
-
writeEvent
|
|
17
|
-
} from "./chunk-JDD6USSA.js";
|
|
18
|
-
import {
|
|
19
|
-
__export
|
|
20
|
-
} from "./chunk-MLKGABMK.js";
|
|
21
|
-
|
|
22
|
-
// src/config/loader.ts
|
|
23
|
-
import { readFileSync, existsSync } from "fs";
|
|
24
|
-
import { resolve, dirname } from "path";
|
|
2
|
+
__esm,
|
|
3
|
+
__export,
|
|
4
|
+
init_esm_shims
|
|
5
|
+
} from "./chunk-66UOCF5R.js";
|
|
25
6
|
|
|
26
7
|
// src/utils/worktree.ts
|
|
27
8
|
import { execSync } from "child_process";
|
|
@@ -54,6 +35,12 @@ function resolveInstanceId() {
|
|
|
54
35
|
}
|
|
55
36
|
return "";
|
|
56
37
|
}
|
|
38
|
+
var init_worktree = __esm({
|
|
39
|
+
"src/utils/worktree.ts"() {
|
|
40
|
+
"use strict";
|
|
41
|
+
init_esm_shims();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
57
44
|
|
|
58
45
|
// src/utils/port.ts
|
|
59
46
|
function calculatePortOffset(instanceId) {
|
|
@@ -68,13 +55,24 @@ function calculatePortOffset(instanceId) {
|
|
|
68
55
|
function resolvePort(basePort, instanceId) {
|
|
69
56
|
return basePort + calculatePortOffset(instanceId);
|
|
70
57
|
}
|
|
58
|
+
var init_port = __esm({
|
|
59
|
+
"src/utils/port.ts"() {
|
|
60
|
+
"use strict";
|
|
61
|
+
init_esm_shims();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
71
64
|
|
|
72
65
|
// src/config/loader.ts
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
var loader_exports = {};
|
|
67
|
+
__export(loader_exports, {
|
|
68
|
+
getBasePort: () => getBasePort,
|
|
69
|
+
getResolvedPort: () => getResolvedPort,
|
|
70
|
+
getServiceCwd: () => getServiceCwd,
|
|
71
|
+
getSessionName: () => getSessionName,
|
|
72
|
+
loadConfig: () => loadConfig
|
|
73
|
+
});
|
|
74
|
+
import { readFileSync, existsSync } from "fs";
|
|
75
|
+
import { resolve, dirname } from "path";
|
|
78
76
|
function findConfigFile(startDir) {
|
|
79
77
|
let dir = resolve(startDir);
|
|
80
78
|
const root = dirname(dir);
|
|
@@ -174,6 +172,20 @@ function getResolvedPort(config, serviceName) {
|
|
|
174
172
|
if (basePort === void 0) return void 0;
|
|
175
173
|
return resolvePort(basePort, config.instanceId);
|
|
176
174
|
}
|
|
175
|
+
var CONFIG_NAMES;
|
|
176
|
+
var init_loader = __esm({
|
|
177
|
+
"src/config/loader.ts"() {
|
|
178
|
+
"use strict";
|
|
179
|
+
init_esm_shims();
|
|
180
|
+
init_worktree();
|
|
181
|
+
init_port();
|
|
182
|
+
CONFIG_NAMES = [
|
|
183
|
+
"devmux.config.json",
|
|
184
|
+
".devmuxrc.json",
|
|
185
|
+
".devmuxrc"
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
});
|
|
177
189
|
|
|
178
190
|
// src/tmux/driver.ts
|
|
179
191
|
var driver_exports = {};
|
|
@@ -185,6 +197,7 @@ __export(driver_exports, {
|
|
|
185
197
|
newSession: () => newSession,
|
|
186
198
|
setRemainOnExit: () => setRemainOnExit
|
|
187
199
|
});
|
|
200
|
+
init_esm_shims();
|
|
188
201
|
import { execSync as execSync2, spawn } from "child_process";
|
|
189
202
|
function hasSession(sessionName) {
|
|
190
203
|
try {
|
|
@@ -252,23 +265,24 @@ __export(checkers_exports, {
|
|
|
252
265
|
checkTmuxPane: () => checkTmuxPane,
|
|
253
266
|
getHealthPort: () => getHealthPort
|
|
254
267
|
});
|
|
268
|
+
init_esm_shims();
|
|
255
269
|
import { createConnection } from "net";
|
|
256
270
|
import { execSync as execSync3 } from "child_process";
|
|
257
271
|
function checkPort(port, host = "127.0.0.1") {
|
|
258
|
-
return new Promise((
|
|
272
|
+
return new Promise((resolve2) => {
|
|
259
273
|
const socket = createConnection({ port, host });
|
|
260
274
|
socket.setTimeout(1e3);
|
|
261
275
|
socket.on("connect", () => {
|
|
262
276
|
socket.destroy();
|
|
263
|
-
|
|
277
|
+
resolve2(true);
|
|
264
278
|
});
|
|
265
279
|
socket.on("timeout", () => {
|
|
266
280
|
socket.destroy();
|
|
267
|
-
|
|
281
|
+
resolve2(false);
|
|
268
282
|
});
|
|
269
283
|
socket.on("error", () => {
|
|
270
284
|
socket.destroy();
|
|
271
|
-
|
|
285
|
+
resolve2(false);
|
|
272
286
|
});
|
|
273
287
|
});
|
|
274
288
|
}
|
|
@@ -323,7 +337,95 @@ function getHealthPort(health) {
|
|
|
323
337
|
}
|
|
324
338
|
|
|
325
339
|
// src/core/service.ts
|
|
340
|
+
init_esm_shims();
|
|
341
|
+
init_loader();
|
|
342
|
+
|
|
343
|
+
// src/utils/process.ts
|
|
344
|
+
init_esm_shims();
|
|
345
|
+
import fkill from "fkill";
|
|
346
|
+
import psList from "ps-list";
|
|
326
347
|
import { execSync as execSync4 } from "child_process";
|
|
348
|
+
async function getProcessOnPort(port) {
|
|
349
|
+
try {
|
|
350
|
+
if (process.platform === "win32") {
|
|
351
|
+
const output2 = execSync4(`netstat -ano | findstr :${port}`, { encoding: "utf-8" });
|
|
352
|
+
const lines = output2.trim().split("\n");
|
|
353
|
+
for (const line of lines) {
|
|
354
|
+
const parts = line.trim().split(/\s+/);
|
|
355
|
+
if (parts.length >= 5) {
|
|
356
|
+
const pid2 = parseInt(parts[4], 10);
|
|
357
|
+
if (!isNaN(pid2)) {
|
|
358
|
+
const proc = await getProcessInfo(pid2);
|
|
359
|
+
if (proc) return proc;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
const output = execSync4(`lsof -ti :${port}`, { encoding: "utf-8" }).trim();
|
|
366
|
+
if (!output) return null;
|
|
367
|
+
const pid = parseInt(output.split("\n")[0], 10);
|
|
368
|
+
if (isNaN(pid)) return null;
|
|
369
|
+
return await getProcessInfo(pid);
|
|
370
|
+
} catch {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async function getProcessInfo(pid) {
|
|
375
|
+
try {
|
|
376
|
+
const processes = await psList();
|
|
377
|
+
const proc = processes.find((p) => p.pid === pid);
|
|
378
|
+
if (!proc) return null;
|
|
379
|
+
return {
|
|
380
|
+
pid: proc.pid,
|
|
381
|
+
name: proc.name,
|
|
382
|
+
cmd: proc.cmd
|
|
383
|
+
};
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function killProcess(pid) {
|
|
389
|
+
try {
|
|
390
|
+
await fkill(pid, { force: true });
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async function getProcessesOnPort(port) {
|
|
395
|
+
const processes = [];
|
|
396
|
+
try {
|
|
397
|
+
if (process.platform === "win32") {
|
|
398
|
+
const output = execSync4(`netstat -ano | findstr :${port}`, { encoding: "utf-8" });
|
|
399
|
+
const lines = output.trim().split("\n");
|
|
400
|
+
const seenPids = /* @__PURE__ */ new Set();
|
|
401
|
+
for (const line of lines) {
|
|
402
|
+
const parts = line.trim().split(/\s+/);
|
|
403
|
+
if (parts.length >= 5) {
|
|
404
|
+
const pid = parseInt(parts[4], 10);
|
|
405
|
+
if (!isNaN(pid) && !seenPids.has(pid)) {
|
|
406
|
+
seenPids.add(pid);
|
|
407
|
+
const proc = await getProcessInfo(pid);
|
|
408
|
+
if (proc) processes.push(proc);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
const output = execSync4(`lsof -ti :${port}`, { encoding: "utf-8" }).trim();
|
|
414
|
+
if (output) {
|
|
415
|
+
const pids = output.split("\n").map((p) => parseInt(p, 10)).filter((p) => !isNaN(p));
|
|
416
|
+
const uniquePids = [...new Set(pids)];
|
|
417
|
+
for (const pid of uniquePids) {
|
|
418
|
+
const proc = await getProcessInfo(pid);
|
|
419
|
+
if (proc) processes.push(proc);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
return processes;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/core/service.ts
|
|
327
429
|
async function ensureService(config, serviceName, options = {}, _dependencyStack = /* @__PURE__ */ new Set()) {
|
|
328
430
|
if (_dependencyStack.has(serviceName)) {
|
|
329
431
|
throw new Error(`Circular dependency detected: ${Array.from(_dependencyStack).join(" -> ")} -> ${serviceName}`);
|
|
@@ -414,7 +516,7 @@ async function getAllStatus(config) {
|
|
|
414
516
|
}
|
|
415
517
|
return statuses;
|
|
416
518
|
}
|
|
417
|
-
function stopService(config, serviceName, options = {}) {
|
|
519
|
+
async function stopService(config, serviceName, options = {}) {
|
|
418
520
|
const service = config.services[serviceName];
|
|
419
521
|
if (!service) {
|
|
420
522
|
throw new Error(`Unknown service: ${serviceName}`);
|
|
@@ -437,21 +539,18 @@ function stopService(config, serviceName, options = {}) {
|
|
|
437
539
|
}
|
|
438
540
|
}
|
|
439
541
|
for (const port of [...new Set(ports)]) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
log(` \u2514\u2500 Killed process(es) on port ${port}`);
|
|
445
|
-
}
|
|
446
|
-
} catch {
|
|
542
|
+
const processes = await getProcessesOnPort(port);
|
|
543
|
+
for (const proc of processes) {
|
|
544
|
+
await killProcess(proc.pid);
|
|
545
|
+
log(` \u2514\u2500 Killed process ${proc.name} (PID ${proc.pid}) on port ${port}`);
|
|
447
546
|
}
|
|
448
547
|
}
|
|
449
548
|
}
|
|
450
549
|
log(`\u2705 ${serviceName} stopped`);
|
|
451
550
|
}
|
|
452
|
-
function stopAllServices(config, options = {}) {
|
|
551
|
+
async function stopAllServices(config, options = {}) {
|
|
453
552
|
for (const serviceName of Object.keys(config.services)) {
|
|
454
|
-
stopService(config, serviceName, options);
|
|
553
|
+
await stopService(config, serviceName, options);
|
|
455
554
|
}
|
|
456
555
|
}
|
|
457
556
|
async function restartService(config, serviceName, options = {}) {
|
|
@@ -472,7 +571,7 @@ function attachService(config, serviceName) {
|
|
|
472
571
|
attachSession(sessionName);
|
|
473
572
|
}
|
|
474
573
|
function sleep(ms) {
|
|
475
|
-
return new Promise((
|
|
574
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
476
575
|
}
|
|
477
576
|
function resolveHealthCheck(health, resolvedPort) {
|
|
478
577
|
if (!health) return void 0;
|
|
@@ -511,330 +610,22 @@ function buildServiceEnv(config, serviceName, userEnv) {
|
|
|
511
610
|
return env;
|
|
512
611
|
}
|
|
513
612
|
|
|
514
|
-
// src/core/run.ts
|
|
515
|
-
import { spawn as spawn2 } from "child_process";
|
|
516
|
-
async function runWithServices(config, command, options) {
|
|
517
|
-
const { services, stopOnExit = true, quiet = false } = options;
|
|
518
|
-
const log = quiet ? () => {
|
|
519
|
-
} : console.log;
|
|
520
|
-
const startedByUs = [];
|
|
521
|
-
for (const serviceName of services) {
|
|
522
|
-
const service = config.services[serviceName];
|
|
523
|
-
if (!service) {
|
|
524
|
-
console.error(`\u274C Unknown service: ${serviceName}`);
|
|
525
|
-
process.exit(1);
|
|
526
|
-
}
|
|
527
|
-
const sessionName = getSessionName(config, serviceName);
|
|
528
|
-
const wasHealthy = await checkHealth(service.health, sessionName);
|
|
529
|
-
if (wasHealthy) {
|
|
530
|
-
log(`\u2705 ${serviceName} already running (will keep on exit)`);
|
|
531
|
-
} else {
|
|
532
|
-
const result = await ensureService(config, serviceName, { quiet });
|
|
533
|
-
if (result.startedByUs) {
|
|
534
|
-
startedByUs.push(result);
|
|
535
|
-
log(` (will stop on Ctrl+C)`);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
log("");
|
|
540
|
-
const cleanup = () => {
|
|
541
|
-
if (stopOnExit && startedByUs.length > 0) {
|
|
542
|
-
log("");
|
|
543
|
-
log("\u{1F9F9} Cleaning up services we started...");
|
|
544
|
-
for (const result of startedByUs) {
|
|
545
|
-
stopService(config, result.serviceName, { killPorts: true, quiet: true });
|
|
546
|
-
log(` \u2514\u2500 Stopped ${result.serviceName}`);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
process.on("SIGINT", () => {
|
|
551
|
-
cleanup();
|
|
552
|
-
process.exit(130);
|
|
553
|
-
});
|
|
554
|
-
process.on("SIGTERM", () => {
|
|
555
|
-
cleanup();
|
|
556
|
-
process.exit(143);
|
|
557
|
-
});
|
|
558
|
-
process.on("exit", cleanup);
|
|
559
|
-
const [cmd, ...args] = command;
|
|
560
|
-
const child = spawn2(cmd, args, {
|
|
561
|
-
stdio: "inherit",
|
|
562
|
-
shell: true
|
|
563
|
-
});
|
|
564
|
-
return new Promise((resolve3) => {
|
|
565
|
-
child.on("close", (code) => {
|
|
566
|
-
resolve3(code ?? 0);
|
|
567
|
-
});
|
|
568
|
-
child.on("error", (err) => {
|
|
569
|
-
console.error(`Failed to run command: ${err.message}`);
|
|
570
|
-
resolve3(1);
|
|
571
|
-
});
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// src/discovery/turbo.ts
|
|
576
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
577
|
-
import { resolve as resolve2, relative } from "path";
|
|
578
|
-
function loadTurboConfig(root) {
|
|
579
|
-
const turboPath = resolve2(root, "turbo.json");
|
|
580
|
-
if (!existsSync2(turboPath)) return null;
|
|
581
|
-
try {
|
|
582
|
-
return JSON.parse(readFileSync2(turboPath, "utf-8"));
|
|
583
|
-
} catch {
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
function getPersistentTasks(turbo) {
|
|
588
|
-
const tasks = turbo.tasks ?? turbo.pipeline ?? {};
|
|
589
|
-
return Object.entries(tasks).filter(([_, task]) => task.persistent).map(([name]) => name.replace(/^\/\/#/, ""));
|
|
590
|
-
}
|
|
591
|
-
function findWorkspacePackages(root) {
|
|
592
|
-
const packages = [];
|
|
593
|
-
const rootPkg = resolve2(root, "package.json");
|
|
594
|
-
if (!existsSync2(rootPkg)) return packages;
|
|
595
|
-
try {
|
|
596
|
-
const pkg = JSON.parse(
|
|
597
|
-
readFileSync2(rootPkg, "utf-8")
|
|
598
|
-
);
|
|
599
|
-
const workspaces = pkg.workspaces ?? [];
|
|
600
|
-
for (const pattern of workspaces) {
|
|
601
|
-
const cleanPattern = pattern.replace(/\/\*$/, "");
|
|
602
|
-
const pkgPath = resolve2(root, cleanPattern, "package.json");
|
|
603
|
-
if (existsSync2(pkgPath)) {
|
|
604
|
-
const subPkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
605
|
-
packages.push({
|
|
606
|
-
name: subPkg.name ?? cleanPattern,
|
|
607
|
-
path: relative(root, resolve2(root, cleanPattern)) || ".",
|
|
608
|
-
scripts: Object.keys(subPkg.scripts ?? {})
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
for (const subdir of ["app", "api", "web", "packages", "apps"]) {
|
|
613
|
-
const pkgPath = resolve2(root, subdir, "package.json");
|
|
614
|
-
if (existsSync2(pkgPath) && !packages.some((p) => p.path === subdir)) {
|
|
615
|
-
const subPkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
616
|
-
packages.push({
|
|
617
|
-
name: subPkg.name ?? subdir,
|
|
618
|
-
path: subdir,
|
|
619
|
-
scripts: Object.keys(subPkg.scripts ?? {})
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
} catch {
|
|
624
|
-
}
|
|
625
|
-
return packages;
|
|
626
|
-
}
|
|
627
|
-
function discoverFromTurbo(root) {
|
|
628
|
-
const turbo = loadTurboConfig(root);
|
|
629
|
-
if (!turbo) return null;
|
|
630
|
-
const persistentTasks = getPersistentTasks(turbo);
|
|
631
|
-
if (persistentTasks.length === 0) return null;
|
|
632
|
-
const packages = findWorkspacePackages(root);
|
|
633
|
-
const services = {};
|
|
634
|
-
for (const pkg of packages) {
|
|
635
|
-
for (const task of persistentTasks) {
|
|
636
|
-
if (pkg.scripts.includes(task)) {
|
|
637
|
-
const serviceName = pkg.path === "." ? task : `${pkg.path.replace(/\//g, "-")}-${task}`;
|
|
638
|
-
services[serviceName] = {
|
|
639
|
-
cwd: pkg.path,
|
|
640
|
-
command: `pnpm ${task}`
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
if (Object.keys(services).length === 0) return null;
|
|
646
|
-
return {
|
|
647
|
-
version: 1,
|
|
648
|
-
project: "my-project",
|
|
649
|
-
services
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
function formatDiscoveredConfig(config) {
|
|
653
|
-
const lines = [
|
|
654
|
-
"# Discovered from turbo.json",
|
|
655
|
-
"# Review and update:",
|
|
656
|
-
"# 1. Set 'project' name",
|
|
657
|
-
"# 2. Add health checks (port or http) for each service",
|
|
658
|
-
"# 3. Remove services you don't want to manage",
|
|
659
|
-
"",
|
|
660
|
-
JSON.stringify(config, null, 2)
|
|
661
|
-
];
|
|
662
|
-
return lines.join("\n");
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// src/watch/manager.ts
|
|
666
|
-
import { execSync as execSync5 } from "child_process";
|
|
667
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
668
|
-
import { fileURLToPath } from "url";
|
|
669
|
-
function getWatcherCliPath() {
|
|
670
|
-
const thisFileDir = dirname2(fileURLToPath(import.meta.url));
|
|
671
|
-
if (thisFileDir.endsWith("watch")) {
|
|
672
|
-
return join2(thisFileDir, "watcher-cli.js");
|
|
673
|
-
}
|
|
674
|
-
return join2(thisFileDir, "watch", "watcher-cli.js");
|
|
675
|
-
}
|
|
676
|
-
function getWatchConfig(config) {
|
|
677
|
-
return config.watch;
|
|
678
|
-
}
|
|
679
|
-
function getServiceWatchConfig(config, serviceName) {
|
|
680
|
-
return config.services[serviceName]?.watch;
|
|
681
|
-
}
|
|
682
|
-
function isWatchEnabled(config, serviceName) {
|
|
683
|
-
const globalWatch = getWatchConfig(config);
|
|
684
|
-
const serviceWatch = getServiceWatchConfig(config, serviceName);
|
|
685
|
-
if (serviceWatch?.enabled !== void 0) {
|
|
686
|
-
return serviceWatch.enabled;
|
|
687
|
-
}
|
|
688
|
-
return globalWatch?.enabled ?? false;
|
|
689
|
-
}
|
|
690
|
-
function isPipeActive(sessionName) {
|
|
691
|
-
try {
|
|
692
|
-
const output = execSync5(`tmux show-options -t "${sessionName}" -p pipe-command 2>/dev/null || true`, {
|
|
693
|
-
encoding: "utf-8"
|
|
694
|
-
});
|
|
695
|
-
return output.includes("watcher-cli");
|
|
696
|
-
} catch {
|
|
697
|
-
return false;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
function getWatcherStatus(config, serviceName) {
|
|
701
|
-
const sessionName = getSessionName(config, serviceName);
|
|
702
|
-
const hasSession2 = hasSession(sessionName);
|
|
703
|
-
return {
|
|
704
|
-
service: serviceName,
|
|
705
|
-
sessionName,
|
|
706
|
-
pipeActive: hasSession2 && isPipeActive(sessionName)
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
function getAllWatcherStatuses(config) {
|
|
710
|
-
return Object.keys(config.services).map((serviceName) => getWatcherStatus(config, serviceName));
|
|
711
|
-
}
|
|
712
|
-
function startWatcher2(config, serviceName, options = {}) {
|
|
713
|
-
const sessionName = getSessionName(config, serviceName);
|
|
714
|
-
const log = options.quiet ? () => {
|
|
715
|
-
} : console.log;
|
|
716
|
-
if (!hasSession(sessionName)) {
|
|
717
|
-
log(`\u274C Service ${serviceName} is not running (no tmux session: ${sessionName})`);
|
|
718
|
-
return false;
|
|
719
|
-
}
|
|
720
|
-
if (isPipeActive(sessionName)) {
|
|
721
|
-
log(`\u2705 Watcher already active for ${serviceName}`);
|
|
722
|
-
return true;
|
|
723
|
-
}
|
|
724
|
-
const globalWatch = getWatchConfig(config);
|
|
725
|
-
const serviceWatch = getServiceWatchConfig(config, serviceName);
|
|
726
|
-
const patterns = resolvePatterns(globalWatch, serviceWatch);
|
|
727
|
-
if (patterns.length === 0) {
|
|
728
|
-
log(`\u26A0\uFE0F No patterns configured for ${serviceName}`);
|
|
729
|
-
return false;
|
|
730
|
-
}
|
|
731
|
-
const outputDir = globalWatch?.outputDir ?? `${process.env.HOME}/.opencode/triggers`;
|
|
732
|
-
const dedupeWindowMs = globalWatch?.dedupeWindowMs ?? 5e3;
|
|
733
|
-
const contextLines = globalWatch?.contextLines ?? 20;
|
|
734
|
-
const patternsJson = JSON.stringify(patterns).replace(/"/g, '\\"');
|
|
735
|
-
const watcherCliPath = getWatcherCliPath();
|
|
736
|
-
const cmd = [
|
|
737
|
-
`node "${watcherCliPath}"`,
|
|
738
|
-
`--service=${serviceName}`,
|
|
739
|
-
`--project=${config.project}`,
|
|
740
|
-
`--session=${sessionName}`,
|
|
741
|
-
`--output=${outputDir}`,
|
|
742
|
-
`--dedupe=${dedupeWindowMs}`,
|
|
743
|
-
`--context=${contextLines}`,
|
|
744
|
-
`--patterns="${patternsJson}"`
|
|
745
|
-
].join(" ");
|
|
746
|
-
try {
|
|
747
|
-
execSync5(`tmux pipe-pane -t "${sessionName}" '${cmd}'`, { stdio: "pipe" });
|
|
748
|
-
log(`\u{1F441}\uFE0F Started watching ${serviceName}`);
|
|
749
|
-
return true;
|
|
750
|
-
} catch (e) {
|
|
751
|
-
log(`\u274C Failed to start watcher for ${serviceName}: ${e}`);
|
|
752
|
-
return false;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
function stopWatcher(config, serviceName, options = {}) {
|
|
756
|
-
const sessionName = getSessionName(config, serviceName);
|
|
757
|
-
const log = options.quiet ? () => {
|
|
758
|
-
} : console.log;
|
|
759
|
-
if (!hasSession(sessionName)) {
|
|
760
|
-
log(`\u26A0\uFE0F Service ${serviceName} is not running`);
|
|
761
|
-
return false;
|
|
762
|
-
}
|
|
763
|
-
if (!isPipeActive(sessionName)) {
|
|
764
|
-
log(`\u26A0\uFE0F Watcher not active for ${serviceName}`);
|
|
765
|
-
return false;
|
|
766
|
-
}
|
|
767
|
-
try {
|
|
768
|
-
execSync5(`tmux pipe-pane -t "${sessionName}"`, { stdio: "pipe" });
|
|
769
|
-
log(`\u{1F6D1} Stopped watching ${serviceName}`);
|
|
770
|
-
return true;
|
|
771
|
-
} catch (e) {
|
|
772
|
-
log(`\u274C Failed to stop watcher for ${serviceName}: ${e}`);
|
|
773
|
-
return false;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
function startAllWatchers(config, options = {}) {
|
|
777
|
-
for (const serviceName of Object.keys(config.services)) {
|
|
778
|
-
if (isWatchEnabled(config, serviceName)) {
|
|
779
|
-
startWatcher2(config, serviceName, options);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
function stopAllWatchers(config, options = {}) {
|
|
784
|
-
for (const serviceName of Object.keys(config.services)) {
|
|
785
|
-
const status = getWatcherStatus(config, serviceName);
|
|
786
|
-
if (status.pipeActive) {
|
|
787
|
-
stopWatcher(config, serviceName, options);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// src/watch/index.ts
|
|
793
|
-
var watch_exports = {};
|
|
794
|
-
__export(watch_exports, {
|
|
795
|
-
BUILTIN_PATTERN_SETS: () => BUILTIN_PATTERN_SETS,
|
|
796
|
-
DedupeCache: () => DedupeCache,
|
|
797
|
-
clearQueue: () => clearQueue,
|
|
798
|
-
computeContentHash: () => computeContentHash,
|
|
799
|
-
createRingBuffer: () => createRingBuffer,
|
|
800
|
-
ensureOutputDir: () => ensureOutputDir,
|
|
801
|
-
getAllWatcherStatuses: () => getAllWatcherStatuses,
|
|
802
|
-
getPendingEvents: () => getPendingEvents,
|
|
803
|
-
getQueuePath: () => getQueuePath,
|
|
804
|
-
getWatcherStatus: () => getWatcherStatus,
|
|
805
|
-
isStackTraceLine: () => isStackTraceLine,
|
|
806
|
-
matchPatterns: () => matchPatterns,
|
|
807
|
-
readQueue: () => readQueue,
|
|
808
|
-
resolvePatterns: () => resolvePatterns,
|
|
809
|
-
startAllWatchers: () => startAllWatchers,
|
|
810
|
-
startServiceWatcher: () => startWatcher2,
|
|
811
|
-
startWatcher: () => startWatcher,
|
|
812
|
-
stopAllWatchers: () => stopAllWatchers,
|
|
813
|
-
stopServiceWatcher: () => stopWatcher,
|
|
814
|
-
updateEventStatus: () => updateEventStatus,
|
|
815
|
-
writeEvent: () => writeEvent
|
|
816
|
-
});
|
|
817
|
-
|
|
818
613
|
export {
|
|
819
614
|
loadConfig,
|
|
820
615
|
getSessionName,
|
|
821
616
|
getServiceCwd,
|
|
617
|
+
loader_exports,
|
|
618
|
+
init_loader,
|
|
619
|
+
hasSession,
|
|
822
620
|
driver_exports,
|
|
621
|
+
checkHealth,
|
|
823
622
|
checkers_exports,
|
|
623
|
+
getProcessOnPort,
|
|
824
624
|
ensureService,
|
|
825
625
|
getStatus,
|
|
826
626
|
getAllStatus,
|
|
827
627
|
stopService,
|
|
828
628
|
stopAllServices,
|
|
829
629
|
restartService,
|
|
830
|
-
attachService
|
|
831
|
-
runWithServices,
|
|
832
|
-
discoverFromTurbo,
|
|
833
|
-
formatDiscoveredConfig,
|
|
834
|
-
getAllWatcherStatuses,
|
|
835
|
-
startWatcher2 as startWatcher,
|
|
836
|
-
stopWatcher,
|
|
837
|
-
startAllWatchers,
|
|
838
|
-
stopAllWatchers,
|
|
839
|
-
watch_exports
|
|
630
|
+
attachService
|
|
840
631
|
};
|