@chriscode/devmux 1.0.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-32R7KDZB.js +319 -0
- package/dist/chunk-66UOCF5R.js +36 -0
- package/dist/chunk-6EU6ODXX.js +372 -0
- package/dist/chunk-ALENFKSX.js +631 -0
- package/dist/chunk-T6I3CPOV.js +437 -0
- package/dist/cli.js +484 -9
- package/dist/dashboard-3GHLOSV3.js +8 -0
- package/dist/index.d.ts +215 -10
- package/dist/index.js +24 -5
- package/dist/server-manager-6EZWZK56.js +106 -0
- package/dist/skill/SKILL.md +118 -0
- package/dist/skill/references/SETUP.md +109 -0
- package/dist/watch/watcher-cli.d.ts +1 -0
- package/dist/watch/watcher-cli.js +32 -0
- package/package.json +7 -3
- package/dist/chunk-7JJYYMUP.js +0 -542
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import {
|
|
2
|
+
init_esm_shims
|
|
3
|
+
} from "./chunk-66UOCF5R.js";
|
|
4
|
+
|
|
5
|
+
// src/watch/queue.ts
|
|
6
|
+
init_esm_shims();
|
|
7
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, statSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
|
|
11
|
+
// src/watch/deduper.ts
|
|
12
|
+
init_esm_shims();
|
|
13
|
+
import { createHash } from "crypto";
|
|
14
|
+
function computeContentHash(service, patternName, content) {
|
|
15
|
+
const normalized = content.replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z?/g, "TIMESTAMP").replace(/\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/g, "TIMESTAMP").replace(/:\d+:\d+/g, ":LINE:COL").replace(/0x[0-9a-f]+/gi, "0xADDR").replace(/\b\d{5,}\b/g, "NUM");
|
|
16
|
+
return createHash("sha256").update(`${service}:${patternName}:${normalized}`).digest("hex").slice(0, 16);
|
|
17
|
+
}
|
|
18
|
+
function createRingBuffer(capacity) {
|
|
19
|
+
const buffer = [];
|
|
20
|
+
let writeIndex = 0;
|
|
21
|
+
let full = false;
|
|
22
|
+
return {
|
|
23
|
+
push(item) {
|
|
24
|
+
if (buffer.length < capacity) {
|
|
25
|
+
buffer.push(item);
|
|
26
|
+
} else {
|
|
27
|
+
buffer[writeIndex] = item;
|
|
28
|
+
full = true;
|
|
29
|
+
}
|
|
30
|
+
writeIndex = (writeIndex + 1) % capacity;
|
|
31
|
+
},
|
|
32
|
+
getAll() {
|
|
33
|
+
if (!full) return [...buffer];
|
|
34
|
+
const result = [];
|
|
35
|
+
for (let i = 0; i < capacity; i++) {
|
|
36
|
+
result.push(buffer[(writeIndex + i) % capacity]);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
},
|
|
40
|
+
clear() {
|
|
41
|
+
buffer.length = 0;
|
|
42
|
+
writeIndex = 0;
|
|
43
|
+
full = false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var DedupeCache = class {
|
|
48
|
+
cache = /* @__PURE__ */ new Map();
|
|
49
|
+
windowMs;
|
|
50
|
+
maxSize;
|
|
51
|
+
constructor(windowMs, maxSize = 1e3) {
|
|
52
|
+
this.windowMs = windowMs;
|
|
53
|
+
this.maxSize = maxSize;
|
|
54
|
+
}
|
|
55
|
+
isDuplicate(hash) {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const lastSeen = this.cache.get(hash);
|
|
58
|
+
if (lastSeen && now - lastSeen < this.windowMs) {
|
|
59
|
+
this.cache.set(hash, now);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
this.cache.set(hash, now);
|
|
63
|
+
this.cleanup(now);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
cleanup(now) {
|
|
67
|
+
if (this.cache.size <= this.maxSize) return;
|
|
68
|
+
const cutoff = now - this.windowMs * 2;
|
|
69
|
+
for (const [hash, timestamp] of this.cache) {
|
|
70
|
+
if (timestamp < cutoff) {
|
|
71
|
+
this.cache.delete(hash);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/watch/queue.ts
|
|
78
|
+
var DEFAULT_OUTPUT_DIR = join(process.env.HOME ?? "~", ".opencode", "triggers");
|
|
79
|
+
var AUTO_PRUNE_THRESHOLD_BYTES = 50 * 1024 * 1024;
|
|
80
|
+
var EVENTS_TO_KEEP_AFTER_PRUNE = 1e4;
|
|
81
|
+
function ensureOutputDir(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
82
|
+
if (!existsSync(outputDir)) {
|
|
83
|
+
mkdirSync(outputDir, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
return outputDir;
|
|
86
|
+
}
|
|
87
|
+
function getQueuePath(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
88
|
+
return join(outputDir, "queue.jsonl");
|
|
89
|
+
}
|
|
90
|
+
function pruneQueueIfNeeded(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
91
|
+
const queuePath = getQueuePath(outputDir);
|
|
92
|
+
if (!existsSync(queuePath)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const stats = statSync(queuePath);
|
|
96
|
+
if (stats.size < AUTO_PRUNE_THRESHOLD_BYTES) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const content = readFileSync(queuePath, "utf-8");
|
|
100
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
101
|
+
const linesToKeep = lines.slice(-EVENTS_TO_KEEP_AFTER_PRUNE);
|
|
102
|
+
writeFileSync(queuePath, linesToKeep.join("\n") + "\n");
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
function writeEvent(options, pattern, rawContent, context, stackTrace) {
|
|
106
|
+
const outputDir = ensureOutputDir(options.outputDir);
|
|
107
|
+
const queuePath = getQueuePath(outputDir);
|
|
108
|
+
const event = {
|
|
109
|
+
id: randomUUID(),
|
|
110
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111
|
+
source: `devmux:${options.service}`,
|
|
112
|
+
service: options.service,
|
|
113
|
+
project: options.project,
|
|
114
|
+
severity: pattern.severity,
|
|
115
|
+
pattern: pattern.name,
|
|
116
|
+
rawContent,
|
|
117
|
+
context,
|
|
118
|
+
stackTrace: stackTrace && stackTrace.length > 0 ? stackTrace : void 0,
|
|
119
|
+
status: "pending",
|
|
120
|
+
contentHash: computeContentHash(options.service, pattern.name, rawContent),
|
|
121
|
+
firstSeen: (/* @__PURE__ */ new Date()).toISOString()
|
|
122
|
+
};
|
|
123
|
+
appendFileSync(queuePath, JSON.stringify(event) + "\n");
|
|
124
|
+
pruneQueueIfNeeded(outputDir);
|
|
125
|
+
return event;
|
|
126
|
+
}
|
|
127
|
+
function readQueue(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
128
|
+
const queuePath = getQueuePath(outputDir);
|
|
129
|
+
if (!existsSync(queuePath)) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const content = readFileSync(queuePath, "utf-8");
|
|
133
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
134
|
+
return lines.map((line) => JSON.parse(line));
|
|
135
|
+
}
|
|
136
|
+
function getPendingEvents(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
137
|
+
return readQueue(outputDir).filter((e) => e.status === "pending");
|
|
138
|
+
}
|
|
139
|
+
function clearQueue(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
140
|
+
const queuePath = getQueuePath(outputDir);
|
|
141
|
+
if (existsSync(queuePath)) {
|
|
142
|
+
unlinkSync(queuePath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function updateEventStatus(eventId, status, outputDir = DEFAULT_OUTPUT_DIR) {
|
|
146
|
+
const queuePath = getQueuePath(outputDir);
|
|
147
|
+
if (!existsSync(queuePath)) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const events = readQueue(outputDir);
|
|
151
|
+
let found = false;
|
|
152
|
+
const updated = events.map((e) => {
|
|
153
|
+
if (e.id === eventId) {
|
|
154
|
+
found = true;
|
|
155
|
+
return { ...e, status };
|
|
156
|
+
}
|
|
157
|
+
return e;
|
|
158
|
+
});
|
|
159
|
+
if (!found) return false;
|
|
160
|
+
writeFileSync(queuePath, updated.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/watch/watcher.ts
|
|
165
|
+
init_esm_shims();
|
|
166
|
+
import { createInterface } from "readline";
|
|
167
|
+
|
|
168
|
+
// src/watch/patterns.ts
|
|
169
|
+
init_esm_shims();
|
|
170
|
+
var BUILTIN_PATTERN_SETS = {
|
|
171
|
+
node: [
|
|
172
|
+
{ name: "unhandled-rejection", regex: "UnhandledPromiseRejection|unhandledRejection", severity: "critical", extractStackTrace: true },
|
|
173
|
+
{ name: "oom", regex: "out of memory|OutOfMemoryError|heap out of memory|ENOMEM|OOM killer", severity: "critical" },
|
|
174
|
+
{ name: "type-error", regex: "TypeError:|ReferenceError:|SyntaxError:", severity: "error", extractStackTrace: true },
|
|
175
|
+
{ name: "js-error", regex: "^Error:|^\\w*Error:|^\\s+at\\s+", severity: "error", extractStackTrace: true }
|
|
176
|
+
],
|
|
177
|
+
web: [
|
|
178
|
+
{ name: "http-5xx", regex: `HTTP/[\\d.]+["'\\s]+5\\d{2}|"(status|statusCode)":\\s*5\\d{2}`, severity: "error" },
|
|
179
|
+
{ name: "http-4xx-important", regex: `HTTP/[\\d.]+["'\\s]+40[134]`, severity: "warning" }
|
|
180
|
+
],
|
|
181
|
+
react: [
|
|
182
|
+
{ name: "react-error", regex: "Uncaught Error:|Error: Minified React|Hydration failed", severity: "error", extractStackTrace: true }
|
|
183
|
+
],
|
|
184
|
+
nextjs: [
|
|
185
|
+
{ name: "webpack-error", regex: "^ERROR in|Module build failed|Failed to compile", severity: "error" },
|
|
186
|
+
{ name: "hydration-error", regex: "Hydration failed|Text content does not match|There was an error while hydrating", severity: "error" }
|
|
187
|
+
],
|
|
188
|
+
database: [
|
|
189
|
+
{ name: "db-error", regex: "ECONNREFUSED|connection refused|Connection lost|\\bdeadlock\\b", severity: "error" }
|
|
190
|
+
],
|
|
191
|
+
fatal: [
|
|
192
|
+
{ name: "fatal", regex: "\\bFATAL\\b|\\bPANIC\\b|Segmentation fault|SIGKILL|SIGSEGV", severity: "critical" }
|
|
193
|
+
],
|
|
194
|
+
python: [
|
|
195
|
+
{ name: "exception", regex: "^Exception:|Traceback \\(most recent", severity: "error", extractStackTrace: true }
|
|
196
|
+
]
|
|
197
|
+
};
|
|
198
|
+
var compiledPatterns = /* @__PURE__ */ new Map();
|
|
199
|
+
function getCompiledRegex(pattern) {
|
|
200
|
+
const cached = compiledPatterns.get(pattern.regex);
|
|
201
|
+
if (cached) return cached;
|
|
202
|
+
const compiled = new RegExp(pattern.regex, "i");
|
|
203
|
+
compiledPatterns.set(pattern.regex, compiled);
|
|
204
|
+
return compiled;
|
|
205
|
+
}
|
|
206
|
+
function matchPatterns(line, patterns) {
|
|
207
|
+
for (const pattern of patterns) {
|
|
208
|
+
const regex = getCompiledRegex(pattern);
|
|
209
|
+
if (regex.test(line)) {
|
|
210
|
+
return { pattern, line };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
function resolvePatterns(globalConfig, serviceConfig) {
|
|
216
|
+
const patterns = [];
|
|
217
|
+
const excludes = new Set(serviceConfig?.exclude ?? []);
|
|
218
|
+
const overrides = serviceConfig?.overrides ?? {};
|
|
219
|
+
for (const setName of serviceConfig?.include ?? []) {
|
|
220
|
+
const builtinSet = BUILTIN_PATTERN_SETS[setName];
|
|
221
|
+
const customSet = globalConfig?.patternSets?.[setName];
|
|
222
|
+
const set = builtinSet ?? customSet;
|
|
223
|
+
if (!set) continue;
|
|
224
|
+
for (const p of set) {
|
|
225
|
+
if (excludes.has(p.name)) continue;
|
|
226
|
+
patterns.push({
|
|
227
|
+
...p,
|
|
228
|
+
severity: overrides[p.name] ?? p.severity
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const p of serviceConfig?.patterns ?? []) {
|
|
233
|
+
patterns.push(p);
|
|
234
|
+
}
|
|
235
|
+
return patterns;
|
|
236
|
+
}
|
|
237
|
+
function isStackTraceLine(line) {
|
|
238
|
+
return /^\s+at\s+/.test(line) || /^\s+\.\.\.\s*\d+\s*more/.test(line);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/watch/watcher.ts
|
|
242
|
+
function startWatcher(options) {
|
|
243
|
+
const contextBuffer = createRingBuffer(options.contextLines);
|
|
244
|
+
const dedupeCache = new DedupeCache(options.dedupeWindowMs);
|
|
245
|
+
let stackAccumulator = null;
|
|
246
|
+
const rl = createInterface({
|
|
247
|
+
input: process.stdin,
|
|
248
|
+
crlfDelay: Infinity
|
|
249
|
+
});
|
|
250
|
+
function flushStackTrace() {
|
|
251
|
+
if (!stackAccumulator) return;
|
|
252
|
+
if (stackAccumulator.timeout) {
|
|
253
|
+
clearTimeout(stackAccumulator.timeout);
|
|
254
|
+
}
|
|
255
|
+
const { match, context, lines } = stackAccumulator;
|
|
256
|
+
const stackTrace = lines.slice(1);
|
|
257
|
+
writeEvent(options, match.pattern, match.line, context, stackTrace);
|
|
258
|
+
console.error(`[devmux-watch] Captured ${match.pattern.severity}: ${match.pattern.name}`);
|
|
259
|
+
stackAccumulator = null;
|
|
260
|
+
}
|
|
261
|
+
rl.on("line", (line) => {
|
|
262
|
+
if (stackAccumulator) {
|
|
263
|
+
if (isStackTraceLine(line)) {
|
|
264
|
+
stackAccumulator.lines.push(line);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
flushStackTrace();
|
|
268
|
+
}
|
|
269
|
+
contextBuffer.push(line);
|
|
270
|
+
const match = matchPatterns(line, options.patterns);
|
|
271
|
+
if (!match) return;
|
|
272
|
+
const context = contextBuffer.getAll().slice(0, -1);
|
|
273
|
+
const hash = `${options.service}:${match.pattern.name}:${line}`;
|
|
274
|
+
if (dedupeCache.isDuplicate(hash)) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (match.pattern.extractStackTrace) {
|
|
278
|
+
stackAccumulator = {
|
|
279
|
+
lines: [line],
|
|
280
|
+
match,
|
|
281
|
+
context,
|
|
282
|
+
timeout: setTimeout(flushStackTrace, 500)
|
|
283
|
+
};
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
writeEvent(options, match.pattern, line, context);
|
|
287
|
+
console.error(`[devmux-watch] Captured ${match.pattern.severity}: ${match.pattern.name}`);
|
|
288
|
+
});
|
|
289
|
+
rl.on("close", () => {
|
|
290
|
+
flushStackTrace();
|
|
291
|
+
process.exit(0);
|
|
292
|
+
});
|
|
293
|
+
process.on("SIGTERM", () => {
|
|
294
|
+
flushStackTrace();
|
|
295
|
+
rl.close();
|
|
296
|
+
});
|
|
297
|
+
process.on("SIGINT", () => {
|
|
298
|
+
flushStackTrace();
|
|
299
|
+
rl.close();
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export {
|
|
304
|
+
BUILTIN_PATTERN_SETS,
|
|
305
|
+
matchPatterns,
|
|
306
|
+
resolvePatterns,
|
|
307
|
+
isStackTraceLine,
|
|
308
|
+
computeContentHash,
|
|
309
|
+
createRingBuffer,
|
|
310
|
+
DedupeCache,
|
|
311
|
+
ensureOutputDir,
|
|
312
|
+
getQueuePath,
|
|
313
|
+
writeEvent,
|
|
314
|
+
readQueue,
|
|
315
|
+
getPendingEvents,
|
|
316
|
+
clearQueue,
|
|
317
|
+
updateEventStatus,
|
|
318
|
+
startWatcher
|
|
319
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// ../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
|
|
23
|
+
import path from "path";
|
|
24
|
+
import { fileURLToPath } from "url";
|
|
25
|
+
var init_esm_shims = __esm({
|
|
26
|
+
"../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
__esm,
|
|
33
|
+
__export,
|
|
34
|
+
__toCommonJS,
|
|
35
|
+
init_esm_shims
|
|
36
|
+
};
|