@stackmemoryai/stackmemory 1.2.2 → 1.2.4
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/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -1
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +25 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/tool-definitions.js +215 -1
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +0 -102
- package/dist/src/utils/hook-installer.js +8 -0
- package/package.json +5 -5
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
class DaemonContextService {
|
|
10
|
+
config;
|
|
11
|
+
state;
|
|
12
|
+
intervalId;
|
|
13
|
+
isRunning = false;
|
|
14
|
+
onLog;
|
|
15
|
+
constructor(config, onLog) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.onLog = onLog;
|
|
18
|
+
this.state = {
|
|
19
|
+
lastSaveTime: 0,
|
|
20
|
+
saveCount: 0,
|
|
21
|
+
errors: []
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
start() {
|
|
25
|
+
if (this.isRunning || !this.config.enabled) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.isRunning = true;
|
|
29
|
+
const intervalMs = this.config.interval * 60 * 1e3;
|
|
30
|
+
this.onLog("INFO", "Context service started", {
|
|
31
|
+
interval: this.config.interval
|
|
32
|
+
});
|
|
33
|
+
this.saveContext();
|
|
34
|
+
this.intervalId = setInterval(() => {
|
|
35
|
+
this.saveContext();
|
|
36
|
+
}, intervalMs);
|
|
37
|
+
}
|
|
38
|
+
stop() {
|
|
39
|
+
if (this.intervalId) {
|
|
40
|
+
clearInterval(this.intervalId);
|
|
41
|
+
this.intervalId = void 0;
|
|
42
|
+
}
|
|
43
|
+
this.isRunning = false;
|
|
44
|
+
this.onLog("INFO", "Context service stopped");
|
|
45
|
+
}
|
|
46
|
+
getState() {
|
|
47
|
+
return { ...this.state };
|
|
48
|
+
}
|
|
49
|
+
updateConfig(config) {
|
|
50
|
+
const wasRunning = this.isRunning;
|
|
51
|
+
if (wasRunning) {
|
|
52
|
+
this.stop();
|
|
53
|
+
}
|
|
54
|
+
this.config = { ...this.config, ...config };
|
|
55
|
+
if (wasRunning && this.config.enabled) {
|
|
56
|
+
this.start();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
forceSave() {
|
|
60
|
+
this.saveContext();
|
|
61
|
+
}
|
|
62
|
+
saveContext() {
|
|
63
|
+
if (!this.isRunning) return;
|
|
64
|
+
try {
|
|
65
|
+
const stackmemoryBin = this.getStackMemoryBin();
|
|
66
|
+
if (!stackmemoryBin) {
|
|
67
|
+
this.onLog("WARN", "StackMemory binary not found");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const message = this.config.checkpointMessage || `Auto-checkpoint #${this.state.saveCount + 1}`;
|
|
71
|
+
const fullMessage = `${message} at ${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
72
|
+
execSync(`"${stackmemoryBin}" context add observation "${fullMessage}"`, {
|
|
73
|
+
timeout: 3e4,
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
stdio: "pipe"
|
|
76
|
+
});
|
|
77
|
+
this.state.saveCount++;
|
|
78
|
+
this.state.lastSaveTime = Date.now();
|
|
79
|
+
this.onLog("INFO", "Context saved", {
|
|
80
|
+
saveCount: this.state.saveCount
|
|
81
|
+
});
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
84
|
+
if (!errorMsg.includes("EBUSY") && !errorMsg.includes("EAGAIN")) {
|
|
85
|
+
this.state.errors.push(errorMsg);
|
|
86
|
+
this.onLog("WARN", "Failed to save context", { error: errorMsg });
|
|
87
|
+
if (this.state.errors.length > 10) {
|
|
88
|
+
this.state.errors = this.state.errors.slice(-10);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
getStackMemoryBin() {
|
|
94
|
+
const homeDir = homedir();
|
|
95
|
+
const locations = [
|
|
96
|
+
join(homeDir, ".stackmemory", "bin", "stackmemory"),
|
|
97
|
+
join(homeDir, ".local", "bin", "stackmemory"),
|
|
98
|
+
"/usr/local/bin/stackmemory",
|
|
99
|
+
"/opt/homebrew/bin/stackmemory"
|
|
100
|
+
];
|
|
101
|
+
for (const loc of locations) {
|
|
102
|
+
if (existsSync(loc)) {
|
|
103
|
+
return loc;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const result = execSync("which stackmemory", {
|
|
108
|
+
encoding: "utf8",
|
|
109
|
+
stdio: "pipe"
|
|
110
|
+
}).trim();
|
|
111
|
+
if (result && existsSync(result)) {
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
DaemonContextService
|
|
121
|
+
};
|
|
@@ -10,6 +10,7 @@ class DaemonMaintenanceService {
|
|
|
10
10
|
state;
|
|
11
11
|
embeddingProvider = null;
|
|
12
12
|
intervalId;
|
|
13
|
+
gcIntervalId;
|
|
13
14
|
isRunning = false;
|
|
14
15
|
onLog;
|
|
15
16
|
constructor(config, onLog) {
|
|
@@ -48,12 +49,26 @@ class DaemonMaintenanceService {
|
|
|
48
49
|
);
|
|
49
50
|
});
|
|
50
51
|
}, intervalMs);
|
|
52
|
+
if (this.config.gcEnabled !== false) {
|
|
53
|
+
const gcMs = (this.config.gcIntervalSeconds ?? 60) * 1e3;
|
|
54
|
+
this.gcIntervalId = setInterval(() => {
|
|
55
|
+
this.runGCCycle().catch((err) => {
|
|
56
|
+
this.addError(
|
|
57
|
+
`GC cycle: ${err instanceof Error ? err.message : String(err)}`
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
}, gcMs);
|
|
61
|
+
}
|
|
51
62
|
}
|
|
52
63
|
stop() {
|
|
53
64
|
if (this.intervalId) {
|
|
54
65
|
clearInterval(this.intervalId);
|
|
55
66
|
this.intervalId = void 0;
|
|
56
67
|
}
|
|
68
|
+
if (this.gcIntervalId) {
|
|
69
|
+
clearInterval(this.gcIntervalId);
|
|
70
|
+
this.gcIntervalId = void 0;
|
|
71
|
+
}
|
|
57
72
|
this.isRunning = false;
|
|
58
73
|
this.onLog("INFO", "Maintenance service stopped");
|
|
59
74
|
}
|
|
@@ -93,6 +108,7 @@ class DaemonMaintenanceService {
|
|
|
93
108
|
await this.backfillEmbeddings(db);
|
|
94
109
|
await this.maybeVacuum(db);
|
|
95
110
|
await this.generateMissingDigests(db);
|
|
111
|
+
await this.recomputeScores(db);
|
|
96
112
|
await this.runGC(db);
|
|
97
113
|
await db.disconnect();
|
|
98
114
|
this.state.lastRunTime = Date.now();
|
|
@@ -280,14 +296,73 @@ class DaemonMaintenanceService {
|
|
|
280
296
|
);
|
|
281
297
|
}
|
|
282
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Dedicated lightweight GC cycle (runs more frequently than full maintenance).
|
|
301
|
+
* Queries active run_ids for protection and delegates to adapter.runGC().
|
|
302
|
+
*/
|
|
303
|
+
async runGCCycle() {
|
|
304
|
+
try {
|
|
305
|
+
const db = await this.getDatabase();
|
|
306
|
+
if (!db) return;
|
|
307
|
+
const rawDb = db.getRawDatabase?.();
|
|
308
|
+
let protectedRunIds = [];
|
|
309
|
+
if (rawDb) {
|
|
310
|
+
const rows = rawDb.prepare(
|
|
311
|
+
"SELECT DISTINCT run_id FROM frames WHERE state = 'active' LIMIT 10"
|
|
312
|
+
).all();
|
|
313
|
+
protectedRunIds = rows.map((r) => r.run_id);
|
|
314
|
+
}
|
|
315
|
+
const result = await db.runGC({
|
|
316
|
+
retentionDays: this.config.gcRetentionDays ?? 90,
|
|
317
|
+
batchSize: this.config.gcBatchSize ?? 100,
|
|
318
|
+
dryRun: false,
|
|
319
|
+
protectedRunIds
|
|
320
|
+
});
|
|
321
|
+
this.state.framesGarbageCollected += result.framesDeleted;
|
|
322
|
+
this.state.lastGcRun = Date.now();
|
|
323
|
+
if (result.framesDeleted > 0) {
|
|
324
|
+
this.onLog(
|
|
325
|
+
"INFO",
|
|
326
|
+
`GC cycle deleted ${result.framesDeleted} expired frames`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
await db.disconnect();
|
|
330
|
+
} catch (err) {
|
|
331
|
+
this.addError(
|
|
332
|
+
`GC cycle: ${err instanceof Error ? err.message : String(err)}`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async recomputeScores(db) {
|
|
337
|
+
try {
|
|
338
|
+
if (typeof db.recomputeImportanceScores !== "function") return;
|
|
339
|
+
const updated = db.recomputeImportanceScores(100);
|
|
340
|
+
if (updated > 0) {
|
|
341
|
+
this.onLog("INFO", `Recomputed ${updated} importance scores`);
|
|
342
|
+
}
|
|
343
|
+
} catch (err) {
|
|
344
|
+
this.addError(
|
|
345
|
+
`Score recompute: ${err instanceof Error ? err.message : String(err)}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
283
349
|
async runGC(db) {
|
|
284
350
|
try {
|
|
285
351
|
if (this.config.gcEnabled === false) return;
|
|
286
352
|
if (typeof db.runGC !== "function") return;
|
|
353
|
+
const rawDb = db.getRawDatabase?.();
|
|
354
|
+
let protectedRunIds = [];
|
|
355
|
+
if (rawDb) {
|
|
356
|
+
const rows = rawDb.prepare(
|
|
357
|
+
"SELECT DISTINCT run_id FROM frames WHERE state = 'active' LIMIT 10"
|
|
358
|
+
).all();
|
|
359
|
+
protectedRunIds = rows.map((r) => r.run_id);
|
|
360
|
+
}
|
|
287
361
|
const result = await db.runGC({
|
|
288
362
|
retentionDays: this.config.gcRetentionDays ?? 90,
|
|
289
363
|
batchSize: this.config.gcBatchSize ?? 100,
|
|
290
|
-
dryRun: false
|
|
364
|
+
dryRun: false,
|
|
365
|
+
protectedRunIds
|
|
291
366
|
});
|
|
292
367
|
this.state.framesGarbageCollected += result.framesDeleted;
|
|
293
368
|
this.state.lastGcRun = Date.now();
|
|
@@ -49,7 +49,7 @@ function trimContentAroundCursor(lines, cursorLine, cursorCol, maxTokens) {
|
|
|
49
49
|
const windowSize = Math.floor(targetChars / avgLineLength);
|
|
50
50
|
const halfWindow = Math.floor(windowSize / 2);
|
|
51
51
|
let start = Math.max(0, cursorLine - halfWindow);
|
|
52
|
-
|
|
52
|
+
const end = Math.min(lines.length, start + windowSize);
|
|
53
53
|
if (end === lines.length) {
|
|
54
54
|
start = Math.max(0, end - windowSize);
|
|
55
55
|
}
|
|
@@ -60,7 +60,7 @@ function trimContentAroundCursor(lines, cursorLine, cursorCol, maxTokens) {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
function parseCompletion(completionText, originalLines, windowStart, windowEnd) {
|
|
63
|
-
|
|
63
|
+
const text = completionText.replace(/<\|file_sep\|>$/, "").replace(/<\/s>$/, "").trimEnd();
|
|
64
64
|
if (!text || text.trim().length === 0) {
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
@@ -62,7 +62,9 @@ class LinearConfigManager {
|
|
|
62
62
|
* Get default configuration with project isolation
|
|
63
63
|
*/
|
|
64
64
|
getDefaultConfig() {
|
|
65
|
-
const projectId = this.isolationManager.getProjectIdentification(
|
|
65
|
+
const projectId = this.isolationManager.getProjectIdentification(
|
|
66
|
+
this.projectRoot
|
|
67
|
+
);
|
|
66
68
|
return {
|
|
67
69
|
enabled: true,
|
|
68
70
|
interval: 5,
|
|
@@ -5,7 +5,6 @@ const __dirname = __pathDirname(__filename);
|
|
|
5
5
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
import { logger } from "../../core/monitoring/logger.js";
|
|
8
|
-
import { IntegrationError, ErrorCode } from "../../core/errors/index.js";
|
|
9
8
|
import { LinearClient } from "./client.js";
|
|
10
9
|
class LinearSyncEngine {
|
|
11
10
|
taskStore;
|
|
@@ -15,6 +14,7 @@ class LinearSyncEngine {
|
|
|
15
14
|
mappings = /* @__PURE__ */ new Map();
|
|
16
15
|
projectRoot;
|
|
17
16
|
mappingsPath;
|
|
17
|
+
_isConfigured = true;
|
|
18
18
|
constructor(taskStore, authManager, config, projectRoot) {
|
|
19
19
|
this.taskStore = taskStore;
|
|
20
20
|
this.authManager = authManager;
|
|
@@ -33,10 +33,9 @@ class LinearSyncEngine {
|
|
|
33
33
|
} else {
|
|
34
34
|
const tokens = this.authManager.loadTokens();
|
|
35
35
|
if (!tokens) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
36
|
+
logger.info("Linear API key not configured \u2014 skipping Linear sync");
|
|
37
|
+
this._isConfigured = false;
|
|
38
|
+
return;
|
|
40
39
|
}
|
|
41
40
|
this.linearClient = new LinearClient({
|
|
42
41
|
apiKey: tokens.accessToken,
|
|
@@ -49,6 +48,12 @@ class LinearSyncEngine {
|
|
|
49
48
|
}
|
|
50
49
|
this.loadMappings();
|
|
51
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if Linear credentials are available
|
|
53
|
+
*/
|
|
54
|
+
get isConfigured() {
|
|
55
|
+
return this._isConfigured;
|
|
56
|
+
}
|
|
52
57
|
/**
|
|
53
58
|
* Update sync configuration
|
|
54
59
|
*/
|
|
@@ -59,6 +64,14 @@ class LinearSyncEngine {
|
|
|
59
64
|
* Perform bi-directional sync
|
|
60
65
|
*/
|
|
61
66
|
async sync() {
|
|
67
|
+
if (!this._isConfigured) {
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
synced: { toLinear: 0, fromLinear: 0, updated: 0 },
|
|
71
|
+
conflicts: [],
|
|
72
|
+
errors: []
|
|
73
|
+
};
|
|
74
|
+
}
|
|
62
75
|
if (!this.config.enabled) {
|
|
63
76
|
return {
|
|
64
77
|
success: false,
|
|
@@ -27,7 +27,12 @@ class CodeExecutionHandler {
|
|
|
27
27
|
* Execute code in a controlled environment
|
|
28
28
|
*/
|
|
29
29
|
async executeCode(params) {
|
|
30
|
-
const {
|
|
30
|
+
const {
|
|
31
|
+
language,
|
|
32
|
+
code,
|
|
33
|
+
workingDirectory,
|
|
34
|
+
timeout = EXECUTION_TIMEOUT
|
|
35
|
+
} = params;
|
|
31
36
|
if (!this.allowedLanguages.includes(language.toLowerCase())) {
|
|
32
37
|
return {
|
|
33
38
|
success: false,
|
|
@@ -43,7 +48,12 @@ class CodeExecutionHandler {
|
|
|
43
48
|
);
|
|
44
49
|
try {
|
|
45
50
|
await fs.writeFile(tempFile, code, "utf-8");
|
|
46
|
-
const result = await this.runCode(
|
|
51
|
+
const result = await this.runCode(
|
|
52
|
+
language,
|
|
53
|
+
tempFile,
|
|
54
|
+
workingDirectory || this.sandboxDir,
|
|
55
|
+
timeout
|
|
56
|
+
);
|
|
47
57
|
await fs.unlink(tempFile).catch(() => {
|
|
48
58
|
});
|
|
49
59
|
return result;
|
|
@@ -98,8 +108,15 @@ class CodeExecutionHandler {
|
|
|
98
108
|
clearTimeout(timeoutId);
|
|
99
109
|
let outputFile;
|
|
100
110
|
if (truncated) {
|
|
101
|
-
outputFile = join(
|
|
102
|
-
|
|
111
|
+
outputFile = join(
|
|
112
|
+
this.sandboxDir,
|
|
113
|
+
`output_${randomBytes(8).toString("hex")}.txt`
|
|
114
|
+
);
|
|
115
|
+
await fs.writeFile(
|
|
116
|
+
outputFile,
|
|
117
|
+
stdout + "\n---STDERR---\n" + stderr,
|
|
118
|
+
"utf-8"
|
|
119
|
+
).catch(() => {
|
|
103
120
|
});
|
|
104
121
|
}
|
|
105
122
|
resolve({
|
|
@@ -175,12 +192,21 @@ class CodeExecutionHandler {
|
|
|
175
192
|
const warnings = [];
|
|
176
193
|
const dangerousPatterns = [
|
|
177
194
|
{ pattern: /import\s+os/i, message: "Importing os module detected" },
|
|
178
|
-
{
|
|
195
|
+
{
|
|
196
|
+
pattern: /import\s+subprocess/i,
|
|
197
|
+
message: "Subprocess module detected"
|
|
198
|
+
},
|
|
179
199
|
{ pattern: /exec\s*\(/i, message: "exec() function detected" },
|
|
180
200
|
{ pattern: /eval\s*\(/i, message: "eval() function detected" },
|
|
181
201
|
{ pattern: /__import__/i, message: "__import__ detected" },
|
|
182
|
-
{
|
|
183
|
-
|
|
202
|
+
{
|
|
203
|
+
pattern: /open\s*\([^)]*['"]\//i,
|
|
204
|
+
message: "Absolute path file access detected"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
pattern: /require\s*\([^)]*child_process/i,
|
|
208
|
+
message: "child_process module detected"
|
|
209
|
+
},
|
|
184
210
|
{ pattern: /require\s*\([^)]*fs/i, message: "fs module access detected" }
|
|
185
211
|
];
|
|
186
212
|
for (const { pattern, message } of dangerousPatterns) {
|