@putervision/behavior-mcp 0.1.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/CONTRIBUTING.md +7 -0
- package/LICENSE +21 -0
- package/PROJECT_INSTRUCTIONS_TEMPLATE.md +9 -0
- package/README.md +94 -0
- package/SECURITY.md +11 -0
- package/dist/chunk-FXYS43D6.js +291 -0
- package/dist/chunk-FXYS43D6.js.map +1 -0
- package/dist/chunk-L2LJRSDU.js +608 -0
- package/dist/chunk-L2LJRSDU.js.map +1 -0
- package/dist/chunk-MXYFWAJG.js +1176 -0
- package/dist/chunk-MXYFWAJG.js.map +1 -0
- package/dist/chunk-NCJFRFRK.js +399 -0
- package/dist/chunk-NCJFRFRK.js.map +1 -0
- package/dist/chunk-QU5PNQP4.js +266 -0
- package/dist/chunk-QU5PNQP4.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +169 -0
- package/dist/cli.js.map +1 -0
- package/dist/db-ZOU6XEDM.js +35 -0
- package/dist/db-ZOU6XEDM.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/init-QMBHF4FZ.js +15 -0
- package/dist/init-QMBHF4FZ.js.map +1 -0
- package/dist/lib.d.ts +672 -0
- package/dist/lib.js +443 -0
- package/dist/lib.js.map +1 -0
- package/glama.json +44 -0
- package/manifest.json +26 -0
- package/package.json +133 -0
- package/server.json +42 -0
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
// src/engine/db.ts
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
import * as path3 from "path";
|
|
4
|
+
import * as fs2 from "fs";
|
|
5
|
+
import * as os2 from "os";
|
|
6
|
+
|
|
7
|
+
// src/utils/logger.ts
|
|
8
|
+
var LOG_LEVELS = {
|
|
9
|
+
debug: 0,
|
|
10
|
+
info: 1,
|
|
11
|
+
warn: 2,
|
|
12
|
+
error: 3
|
|
13
|
+
};
|
|
14
|
+
function getLogLevel() {
|
|
15
|
+
const envLevel = (process.env.BEHAVIOR_LOG_LEVEL || process.env.BEHAVIOR_RUNTIME_MCP_LOG_LEVEL)?.toLowerCase();
|
|
16
|
+
if (envLevel && envLevel in LOG_LEVELS) {
|
|
17
|
+
return LOG_LEVELS[envLevel];
|
|
18
|
+
}
|
|
19
|
+
return LOG_LEVELS.info;
|
|
20
|
+
}
|
|
21
|
+
var logger = {
|
|
22
|
+
debug: (message, ...args) => {
|
|
23
|
+
if (getLogLevel() <= LOG_LEVELS.debug) {
|
|
24
|
+
console.error(`[DEBUG] ${message}`, ...args);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
info: (message, ...args) => {
|
|
28
|
+
if (getLogLevel() <= LOG_LEVELS.info) {
|
|
29
|
+
console.error(`[INFO] ${message}`, ...args);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
warn: (message, ...args) => {
|
|
33
|
+
if (getLogLevel() <= LOG_LEVELS.warn) {
|
|
34
|
+
console.error(`[WARN] ${message}`, ...args);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
error: (message, ...args) => {
|
|
38
|
+
if (getLogLevel() <= LOG_LEVELS.error) {
|
|
39
|
+
console.error(`[ERROR] ${message}`, ...args);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/utils/errors.ts
|
|
45
|
+
var BehaviorRuntimeError = class extends Error {
|
|
46
|
+
code;
|
|
47
|
+
details;
|
|
48
|
+
constructor(message, code = "INTERNAL_ERROR", details) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = this.constructor.name;
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.details = details;
|
|
53
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var DatabaseError = class extends BehaviorRuntimeError {
|
|
57
|
+
constructor(message, details) {
|
|
58
|
+
super(message, "DATABASE_ERROR", details);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var ValidationError = class extends BehaviorRuntimeError {
|
|
62
|
+
constructor(message, details) {
|
|
63
|
+
super(message, "VALIDATION_ERROR", details);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var NotFoundError = class extends BehaviorRuntimeError {
|
|
67
|
+
constructor(message, details) {
|
|
68
|
+
super(message, "NOT_FOUND_ERROR", details);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var ExecutionError = class extends BehaviorRuntimeError {
|
|
72
|
+
constructor(message, details) {
|
|
73
|
+
super(message, "EXECUTION_ERROR", details);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var SafetyError = class extends BehaviorRuntimeError {
|
|
77
|
+
constructor(message, details) {
|
|
78
|
+
super(message, "SAFETY_ERROR", details);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/engine/config.ts
|
|
83
|
+
import * as fs from "fs";
|
|
84
|
+
import * as path from "path";
|
|
85
|
+
import { z } from "zod";
|
|
86
|
+
var ProjectConfigSchema = z.object({
|
|
87
|
+
projectName: z.string().optional(),
|
|
88
|
+
defaultBranch: z.string().optional(),
|
|
89
|
+
storagePath: z.string().optional(),
|
|
90
|
+
allowedExportDirs: z.array(z.string()).optional(),
|
|
91
|
+
busyTimeoutMs: z.number().int().positive().optional(),
|
|
92
|
+
mmapSizeBytes: z.number().int().positive().optional(),
|
|
93
|
+
tickRateHz: z.number().int().positive().optional(),
|
|
94
|
+
stuckThreshold: z.number().int().positive().optional(),
|
|
95
|
+
maxActionsPerSec: z.number().int().positive().optional(),
|
|
96
|
+
allowlistOrigins: z.array(z.string()).optional(),
|
|
97
|
+
accessMode: z.enum(["normal", "read_only"]).optional()
|
|
98
|
+
}).passthrough();
|
|
99
|
+
var cachedConfigs = /* @__PURE__ */ new Map();
|
|
100
|
+
var CONFIG_TTL_MS = 2e3;
|
|
101
|
+
function loadProjectConfig(projectRoot) {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const cached = cachedConfigs.get(projectRoot);
|
|
104
|
+
if (cached && now - cached.timestamp < CONFIG_TTL_MS) {
|
|
105
|
+
return cached.config;
|
|
106
|
+
}
|
|
107
|
+
const effectiveRoot = process.env.PUTERVISION_PROJECT_DIR || projectRoot;
|
|
108
|
+
const configPath = path.join(effectiveRoot, ".behavior-mcp.json");
|
|
109
|
+
const legacyConfigPath = path.join(effectiveRoot, ".behavior-runtime-mcp.json");
|
|
110
|
+
let config = {};
|
|
111
|
+
const activePath = fs.existsSync(configPath) ? configPath : fs.existsSync(legacyConfigPath) ? legacyConfigPath : null;
|
|
112
|
+
if (activePath) {
|
|
113
|
+
try {
|
|
114
|
+
const raw = fs.readFileSync(activePath, "utf-8");
|
|
115
|
+
const parsed = JSON.parse(raw);
|
|
116
|
+
const validated = ProjectConfigSchema.safeParse(parsed);
|
|
117
|
+
if (validated.success) {
|
|
118
|
+
config = validated.data;
|
|
119
|
+
} else {
|
|
120
|
+
logger.warn(`Invalid ${path.basename(activePath)} schema: ${validated.error.message}`);
|
|
121
|
+
config = parsed;
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logger.warn(`Failed to parse ${path.basename(activePath)}: ${err.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (process.env.PUTERVISION_PROJECT_SLUG && !config.projectName) {
|
|
128
|
+
config.projectName = process.env.PUTERVISION_PROJECT_SLUG;
|
|
129
|
+
}
|
|
130
|
+
cachedConfigs.set(projectRoot, { config, timestamp: now });
|
|
131
|
+
return config;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/utils/path-validator.ts
|
|
135
|
+
import * as path2 from "path";
|
|
136
|
+
import * as os from "os";
|
|
137
|
+
function getDefaultAllowedDirs(projectRoot) {
|
|
138
|
+
const resolvedRoot = path2.resolve(projectRoot);
|
|
139
|
+
const homeBackups = path2.join(os.homedir(), ".behavior-mcp", "backups");
|
|
140
|
+
return [resolvedRoot, homeBackups];
|
|
141
|
+
}
|
|
142
|
+
function loadPathConfig(projectRoot) {
|
|
143
|
+
return {
|
|
144
|
+
projectRoot: path2.resolve(projectRoot),
|
|
145
|
+
allowedExportDirs: getDefaultAllowedDirs(projectRoot)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function validatePath(filePath, config) {
|
|
149
|
+
if (!filePath || typeof filePath !== "string") {
|
|
150
|
+
throw new ValidationError("File path must be a non-empty string.");
|
|
151
|
+
}
|
|
152
|
+
let resolved;
|
|
153
|
+
if (path2.isAbsolute(filePath)) {
|
|
154
|
+
resolved = path2.resolve(filePath);
|
|
155
|
+
} else {
|
|
156
|
+
resolved = path2.resolve(config.projectRoot, filePath);
|
|
157
|
+
}
|
|
158
|
+
const allowed = (config.allowedExportDirs || [config.projectRoot]).map((d) => path2.resolve(d));
|
|
159
|
+
const isAllowed = allowed.some((dir) => {
|
|
160
|
+
return resolved === dir || resolved.startsWith(dir + path2.sep);
|
|
161
|
+
});
|
|
162
|
+
if (!isAllowed) {
|
|
163
|
+
throw new ValidationError(
|
|
164
|
+
`Access denied: path "${filePath}" resolves outside allowed directories.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return resolved;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/engine/migrations.ts
|
|
171
|
+
function runMigrations(db) {
|
|
172
|
+
db.exec(`
|
|
173
|
+
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
174
|
+
key TEXT PRIMARY KEY,
|
|
175
|
+
value TEXT NOT NULL
|
|
176
|
+
);
|
|
177
|
+
`);
|
|
178
|
+
const currentVersionRow = db.prepare("SELECT value FROM schema_meta WHERE key = 'version'").get();
|
|
179
|
+
const currentVersion = currentVersionRow ? parseInt(currentVersionRow.value, 10) : 0;
|
|
180
|
+
if (currentVersion < 1) {
|
|
181
|
+
logger.info("Applying migration v1 for behavior-mcp...");
|
|
182
|
+
db.exec(`
|
|
183
|
+
CREATE TABLE IF NOT EXISTS behavior_definitions (
|
|
184
|
+
id TEXT PRIMARY KEY,
|
|
185
|
+
project TEXT NOT NULL,
|
|
186
|
+
name TEXT NOT NULL,
|
|
187
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
188
|
+
description TEXT,
|
|
189
|
+
tree_json TEXT NOT NULL,
|
|
190
|
+
tree_hash TEXT NOT NULL,
|
|
191
|
+
is_active INTEGER NOT NULL DEFAULT 1,
|
|
192
|
+
metadata_json TEXT,
|
|
193
|
+
client_request_id TEXT,
|
|
194
|
+
created_at TEXT NOT NULL,
|
|
195
|
+
UNIQUE(project, name, version)
|
|
196
|
+
);
|
|
197
|
+
CREATE INDEX IF NOT EXISTS idx_behaviors_project ON behavior_definitions(project);
|
|
198
|
+
CREATE INDEX IF NOT EXISTS idx_behaviors_name ON behavior_definitions(project, name);
|
|
199
|
+
CREATE INDEX IF NOT EXISTS idx_behaviors_client_req ON behavior_definitions(project, client_request_id);
|
|
200
|
+
|
|
201
|
+
CREATE TABLE IF NOT EXISTS execution_state (
|
|
202
|
+
id TEXT PRIMARY KEY,
|
|
203
|
+
project TEXT NOT NULL,
|
|
204
|
+
behavior_name TEXT NOT NULL,
|
|
205
|
+
behavior_version INTEGER NOT NULL DEFAULT 1,
|
|
206
|
+
session_id TEXT,
|
|
207
|
+
intention_id TEXT,
|
|
208
|
+
status TEXT NOT NULL DEFAULT 'idle',
|
|
209
|
+
active_node_path TEXT,
|
|
210
|
+
blackboard_json TEXT NOT NULL DEFAULT '{}',
|
|
211
|
+
current_tick INTEGER NOT NULL DEFAULT 0,
|
|
212
|
+
tick_rate_hz INTEGER NOT NULL DEFAULT 60,
|
|
213
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
214
|
+
stuck_score REAL NOT NULL DEFAULT 0.0,
|
|
215
|
+
error TEXT,
|
|
216
|
+
metadata_json TEXT,
|
|
217
|
+
created_at TEXT NOT NULL,
|
|
218
|
+
updated_at TEXT NOT NULL
|
|
219
|
+
);
|
|
220
|
+
CREATE INDEX IF NOT EXISTS idx_execution_project ON execution_state(project);
|
|
221
|
+
CREATE INDEX IF NOT EXISTS idx_execution_status ON execution_state(project, status);
|
|
222
|
+
CREATE INDEX IF NOT EXISTS idx_execution_intention ON execution_state(intention_id);
|
|
223
|
+
|
|
224
|
+
CREATE TABLE IF NOT EXISTS triggers (
|
|
225
|
+
id TEXT PRIMARY KEY,
|
|
226
|
+
project TEXT NOT NULL,
|
|
227
|
+
name TEXT NOT NULL,
|
|
228
|
+
behavior_name TEXT NOT NULL,
|
|
229
|
+
condition_type TEXT NOT NULL,
|
|
230
|
+
condition_params_json TEXT NOT NULL,
|
|
231
|
+
priority REAL NOT NULL DEFAULT 0.5,
|
|
232
|
+
cooldown_ms INTEGER NOT NULL DEFAULT 1000,
|
|
233
|
+
last_fired_at TEXT,
|
|
234
|
+
is_enabled INTEGER NOT NULL DEFAULT 1,
|
|
235
|
+
metadata_json TEXT,
|
|
236
|
+
created_at TEXT NOT NULL,
|
|
237
|
+
updated_at TEXT NOT NULL,
|
|
238
|
+
UNIQUE(project, name)
|
|
239
|
+
);
|
|
240
|
+
CREATE INDEX IF NOT EXISTS idx_triggers_project ON triggers(project);
|
|
241
|
+
CREATE INDEX IF NOT EXISTS idx_triggers_enabled ON triggers(project, is_enabled);
|
|
242
|
+
|
|
243
|
+
CREATE TABLE IF NOT EXISTS execution_metrics (
|
|
244
|
+
id TEXT PRIMARY KEY,
|
|
245
|
+
project TEXT NOT NULL,
|
|
246
|
+
execution_id TEXT NOT NULL,
|
|
247
|
+
session_id TEXT,
|
|
248
|
+
intention_id TEXT,
|
|
249
|
+
behavior_name TEXT NOT NULL,
|
|
250
|
+
status TEXT NOT NULL,
|
|
251
|
+
tick_count INTEGER NOT NULL DEFAULT 0,
|
|
252
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
253
|
+
avg_tick_ms REAL NOT NULL DEFAULT 0.0,
|
|
254
|
+
max_tick_ms REAL NOT NULL DEFAULT 0.0,
|
|
255
|
+
stuck_count INTEGER NOT NULL DEFAULT 0,
|
|
256
|
+
interrupt_count INTEGER NOT NULL DEFAULT 0,
|
|
257
|
+
category_metrics_json TEXT,
|
|
258
|
+
created_at TEXT NOT NULL,
|
|
259
|
+
FOREIGN KEY (execution_id) REFERENCES execution_state(id) ON DELETE CASCADE
|
|
260
|
+
);
|
|
261
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_project ON execution_metrics(project);
|
|
262
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_execution ON execution_metrics(execution_id);
|
|
263
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_behavior ON execution_metrics(project, behavior_name);
|
|
264
|
+
|
|
265
|
+
CREATE TABLE IF NOT EXISTS recordings (
|
|
266
|
+
id TEXT PRIMARY KEY,
|
|
267
|
+
project TEXT NOT NULL,
|
|
268
|
+
execution_id TEXT NOT NULL,
|
|
269
|
+
behavior_name TEXT NOT NULL,
|
|
270
|
+
total_frames INTEGER NOT NULL DEFAULT 0,
|
|
271
|
+
frames_json TEXT NOT NULL,
|
|
272
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
273
|
+
metadata_json TEXT,
|
|
274
|
+
created_at TEXT NOT NULL,
|
|
275
|
+
FOREIGN KEY (execution_id) REFERENCES execution_state(id) ON DELETE CASCADE
|
|
276
|
+
);
|
|
277
|
+
CREATE INDEX IF NOT EXISTS idx_recordings_project ON recordings(project);
|
|
278
|
+
CREATE INDEX IF NOT EXISTS idx_recordings_execution ON recordings(execution_id);
|
|
279
|
+
|
|
280
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
281
|
+
id TEXT PRIMARY KEY,
|
|
282
|
+
project TEXT NOT NULL,
|
|
283
|
+
entity_id TEXT NOT NULL,
|
|
284
|
+
entity_type TEXT NOT NULL,
|
|
285
|
+
action TEXT NOT NULL,
|
|
286
|
+
prev_hash TEXT NOT NULL,
|
|
287
|
+
hash TEXT NOT NULL,
|
|
288
|
+
details_json TEXT,
|
|
289
|
+
timestamp TEXT NOT NULL
|
|
290
|
+
);
|
|
291
|
+
CREATE INDEX IF NOT EXISTS idx_events_project ON events(project);
|
|
292
|
+
CREATE INDEX IF NOT EXISTS idx_events_entity ON events(project, entity_id);
|
|
293
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
294
|
+
|
|
295
|
+
CREATE TABLE IF NOT EXISTS snapshots (
|
|
296
|
+
id TEXT PRIMARY KEY,
|
|
297
|
+
project TEXT NOT NULL,
|
|
298
|
+
name TEXT NOT NULL,
|
|
299
|
+
description TEXT,
|
|
300
|
+
data_json TEXT NOT NULL,
|
|
301
|
+
created_at TEXT NOT NULL,
|
|
302
|
+
UNIQUE(project, name)
|
|
303
|
+
);
|
|
304
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_project ON snapshots(project);
|
|
305
|
+
|
|
306
|
+
INSERT INTO schema_meta (key, value) VALUES ('version', '1')
|
|
307
|
+
ON CONFLICT(key) DO UPDATE SET value = '1';
|
|
308
|
+
`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/engine/db.ts
|
|
313
|
+
function validatePath2(filePath, project) {
|
|
314
|
+
const projectRoot = resolveProjectRoot(project);
|
|
315
|
+
const pathConfig = loadPathConfig(projectRoot);
|
|
316
|
+
return validatePath(filePath, pathConfig);
|
|
317
|
+
}
|
|
318
|
+
var DEFAULT_REGISTRY_PATH = path3.join(os2.homedir(), ".behavior-mcp", "projects.json");
|
|
319
|
+
var LEGACY_REGISTRY_PATH = path3.join(os2.homedir(), ".behavior-runtime-mcp", "projects.json");
|
|
320
|
+
function getRegistryPath() {
|
|
321
|
+
if (process.env.BEHAVIOR_REGISTRY_PATH) return process.env.BEHAVIOR_REGISTRY_PATH;
|
|
322
|
+
if (!fs2.existsSync(DEFAULT_REGISTRY_PATH) && fs2.existsSync(LEGACY_REGISTRY_PATH)) {
|
|
323
|
+
return LEGACY_REGISTRY_PATH;
|
|
324
|
+
}
|
|
325
|
+
return DEFAULT_REGISTRY_PATH;
|
|
326
|
+
}
|
|
327
|
+
var registryCache = null;
|
|
328
|
+
var REGISTRY_TTL_MS = 2e3;
|
|
329
|
+
function getRegistry() {
|
|
330
|
+
const now = Date.now();
|
|
331
|
+
if (registryCache && now - registryCache.timestamp < REGISTRY_TTL_MS) {
|
|
332
|
+
return registryCache.registry;
|
|
333
|
+
}
|
|
334
|
+
const regPath = getRegistryPath();
|
|
335
|
+
try {
|
|
336
|
+
if (fs2.existsSync(regPath)) {
|
|
337
|
+
const raw = fs2.readFileSync(regPath, "utf-8");
|
|
338
|
+
const registry = JSON.parse(raw);
|
|
339
|
+
registryCache = { registry, timestamp: now };
|
|
340
|
+
return registry;
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
logger.warn(`Failed to read registry: ${err.message}`);
|
|
344
|
+
}
|
|
345
|
+
return {};
|
|
346
|
+
}
|
|
347
|
+
function registerProject(projectName, projectRoot) {
|
|
348
|
+
const regPath = getRegistryPath();
|
|
349
|
+
const dir = path3.dirname(regPath);
|
|
350
|
+
if (!fs2.existsSync(dir)) {
|
|
351
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
352
|
+
}
|
|
353
|
+
const registry = getRegistry();
|
|
354
|
+
const slug = sanitizeSlug(projectName);
|
|
355
|
+
registry[slug] = path3.resolve(projectRoot);
|
|
356
|
+
const tmpPath = `${regPath}.tmp.${Math.random().toString(36).substring(2, 8)}`;
|
|
357
|
+
fs2.writeFileSync(tmpPath, JSON.stringify(registry, null, 2) + "\n", { mode: 384 });
|
|
358
|
+
fs2.renameSync(tmpPath, regPath);
|
|
359
|
+
registryCache = { registry, timestamp: Date.now() };
|
|
360
|
+
logger.debug(`Registered project: ${slug} -> ${projectRoot}`);
|
|
361
|
+
}
|
|
362
|
+
function unregisterProject(projectName) {
|
|
363
|
+
const regPath = getRegistryPath();
|
|
364
|
+
const registry = getRegistry();
|
|
365
|
+
const slug = sanitizeSlug(projectName);
|
|
366
|
+
if (registry[slug]) {
|
|
367
|
+
delete registry[slug];
|
|
368
|
+
const tmpPath = `${regPath}.tmp.${Math.random().toString(36).substring(2, 8)}`;
|
|
369
|
+
fs2.writeFileSync(tmpPath, JSON.stringify(registry, null, 2) + "\n", { mode: 384 });
|
|
370
|
+
fs2.renameSync(tmpPath, regPath);
|
|
371
|
+
registryCache = { registry, timestamp: Date.now() };
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function sanitizeSlug(str) {
|
|
375
|
+
return str.trim().toLowerCase().replace(/[^a-z0-9-_]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
376
|
+
}
|
|
377
|
+
function resolveProjectRoot(project, cwd = process.cwd()) {
|
|
378
|
+
const currentCwd = path3.resolve(cwd);
|
|
379
|
+
if (project) {
|
|
380
|
+
const registry2 = getRegistry();
|
|
381
|
+
const slug = sanitizeSlug(project);
|
|
382
|
+
if (registry2[slug] && fs2.existsSync(registry2[slug])) {
|
|
383
|
+
return registry2[slug];
|
|
384
|
+
}
|
|
385
|
+
if (registry2[project] && fs2.existsSync(registry2[project])) {
|
|
386
|
+
return registry2[project];
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const registry = getRegistry();
|
|
390
|
+
for (const [, projectPath] of Object.entries(registry)) {
|
|
391
|
+
const resolvedPath = path3.resolve(projectPath);
|
|
392
|
+
if (resolvedPath === currentCwd && fs2.existsSync(resolvedPath)) {
|
|
393
|
+
return resolvedPath;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (fs2.existsSync(path3.join(currentCwd, ".behavior-mcp")) || fs2.existsSync(path3.join(currentCwd, ".behavior-runtime-mcp")) || fs2.existsSync(path3.join(currentCwd, ".state-memory-mcp")) || fs2.existsSync(path3.join(currentCwd, ".world-model-mcp")) || fs2.existsSync(path3.join(currentCwd, ".vision-memory-mcp")) || fs2.existsSync(path3.join(currentCwd, "package.json"))) {
|
|
397
|
+
return currentCwd;
|
|
398
|
+
}
|
|
399
|
+
let bestMatch;
|
|
400
|
+
for (const [, projectPath] of Object.entries(registry)) {
|
|
401
|
+
const resolvedPath = path3.resolve(projectPath);
|
|
402
|
+
if (resolvedPath === os2.homedir()) continue;
|
|
403
|
+
if (currentCwd.startsWith(resolvedPath + path3.sep)) {
|
|
404
|
+
if (fs2.existsSync(resolvedPath)) {
|
|
405
|
+
if (!bestMatch || resolvedPath.length > bestMatch.length) {
|
|
406
|
+
bestMatch = resolvedPath;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (bestMatch) {
|
|
412
|
+
return bestMatch;
|
|
413
|
+
}
|
|
414
|
+
let curr = currentCwd;
|
|
415
|
+
const home = os2.homedir();
|
|
416
|
+
while (curr !== path3.dirname(curr) && curr !== home) {
|
|
417
|
+
if (fs2.existsSync(path3.join(curr, ".behavior-mcp")) || fs2.existsSync(path3.join(curr, ".behavior-runtime-mcp")) || fs2.existsSync(path3.join(curr, ".state-memory-mcp")) || fs2.existsSync(path3.join(curr, ".world-model-mcp")) || fs2.existsSync(path3.join(curr, ".vision-memory-mcp")) || fs2.existsSync(path3.join(curr, "package.json")) || fs2.existsSync(path3.join(curr, ".git"))) {
|
|
418
|
+
return curr;
|
|
419
|
+
}
|
|
420
|
+
curr = path3.dirname(curr);
|
|
421
|
+
}
|
|
422
|
+
return currentCwd;
|
|
423
|
+
}
|
|
424
|
+
function getProjectSlug(project, cwd = process.cwd()) {
|
|
425
|
+
if (project && project.trim().length > 0) {
|
|
426
|
+
return sanitizeSlug(project);
|
|
427
|
+
}
|
|
428
|
+
const root = resolveProjectRoot(project, cwd);
|
|
429
|
+
const config = loadProjectConfig(root);
|
|
430
|
+
if (config.projectName) {
|
|
431
|
+
return sanitizeSlug(config.projectName);
|
|
432
|
+
}
|
|
433
|
+
return sanitizeSlug(path3.basename(root));
|
|
434
|
+
}
|
|
435
|
+
function getBaseDir(projectRoot) {
|
|
436
|
+
const config = loadProjectConfig(projectRoot);
|
|
437
|
+
if (config.storagePath) {
|
|
438
|
+
return path3.resolve(projectRoot, config.storagePath);
|
|
439
|
+
}
|
|
440
|
+
if (process.env.BEHAVIOR_MCP_DIR || process.env.BEHAVIOR_RUNTIME_MCP_DIR) {
|
|
441
|
+
return path3.resolve(
|
|
442
|
+
projectRoot,
|
|
443
|
+
process.env.BEHAVIOR_MCP_DIR || process.env.BEHAVIOR_RUNTIME_MCP_DIR
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
if (fs2.existsSync(path3.join(projectRoot, ".behavior-mcp"))) {
|
|
447
|
+
return path3.join(projectRoot, ".behavior-mcp");
|
|
448
|
+
}
|
|
449
|
+
if (fs2.existsSync(path3.join(projectRoot, ".behavior-runtime-mcp"))) {
|
|
450
|
+
return path3.join(projectRoot, ".behavior-runtime-mcp");
|
|
451
|
+
}
|
|
452
|
+
return path3.join(projectRoot, ".behavior-mcp");
|
|
453
|
+
}
|
|
454
|
+
function getProjectDbDir(project, cwd = process.cwd()) {
|
|
455
|
+
const root = resolveProjectRoot(project, cwd);
|
|
456
|
+
const slug = getProjectSlug(project, cwd);
|
|
457
|
+
const baseDir = getBaseDir(root);
|
|
458
|
+
return path3.join(baseDir, slug);
|
|
459
|
+
}
|
|
460
|
+
function getDbPath(project, cwd = process.cwd()) {
|
|
461
|
+
const dbDir = getProjectDbDir(project, cwd);
|
|
462
|
+
return path3.join(dbDir, "behavior.db");
|
|
463
|
+
}
|
|
464
|
+
var dbCache = /* @__PURE__ */ new Map();
|
|
465
|
+
var readOnlyDbCache = /* @__PURE__ */ new Map();
|
|
466
|
+
var MAX_CACHED_DBS = 5;
|
|
467
|
+
function getDb(project, cwd = process.cwd()) {
|
|
468
|
+
const dbPath = getDbPath(project, cwd);
|
|
469
|
+
if (dbCache.has(dbPath)) {
|
|
470
|
+
return dbCache.get(dbPath);
|
|
471
|
+
}
|
|
472
|
+
const dbDir = path3.dirname(dbPath);
|
|
473
|
+
if (!fs2.existsSync(dbDir)) {
|
|
474
|
+
fs2.mkdirSync(dbDir, { recursive: true, mode: 448 });
|
|
475
|
+
}
|
|
476
|
+
const root = resolveProjectRoot(project, cwd);
|
|
477
|
+
const config = loadProjectConfig(root);
|
|
478
|
+
try {
|
|
479
|
+
const db = new Database(dbPath, {
|
|
480
|
+
timeout: config.busyTimeoutMs || 5e3
|
|
481
|
+
});
|
|
482
|
+
db.pragma("journal_mode = WAL");
|
|
483
|
+
db.pragma("synchronous = NORMAL");
|
|
484
|
+
db.pragma(`busy_timeout = ${config.busyTimeoutMs || 5e3}`);
|
|
485
|
+
db.pragma("foreign_keys = ON");
|
|
486
|
+
db.pragma("cache_size = -20000");
|
|
487
|
+
db.pragma(`mmap_size = ${config.mmapSizeBytes || 134217728}`);
|
|
488
|
+
db.pragma("trusted_schema = OFF");
|
|
489
|
+
runMigrations(db);
|
|
490
|
+
if (dbCache.size >= MAX_CACHED_DBS) {
|
|
491
|
+
const firstKey = dbCache.keys().next().value;
|
|
492
|
+
if (firstKey) {
|
|
493
|
+
try {
|
|
494
|
+
dbCache.get(firstKey)?.close();
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
dbCache.delete(firstKey);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
dbCache.set(dbPath, db);
|
|
501
|
+
return db;
|
|
502
|
+
} catch (err) {
|
|
503
|
+
throw new DatabaseError(
|
|
504
|
+
`Failed to open behavior-runtime database at ${dbPath}: ${err.message}`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
function getReadOnlyDb(project, cwd = process.cwd()) {
|
|
509
|
+
const dbPath = getDbPath(project, cwd);
|
|
510
|
+
if (readOnlyDbCache.has(dbPath)) {
|
|
511
|
+
return readOnlyDbCache.get(dbPath);
|
|
512
|
+
}
|
|
513
|
+
if (!fs2.existsSync(dbPath)) {
|
|
514
|
+
getDb(project, cwd);
|
|
515
|
+
}
|
|
516
|
+
const root = resolveProjectRoot(project, cwd);
|
|
517
|
+
const config = loadProjectConfig(root);
|
|
518
|
+
try {
|
|
519
|
+
const db = new Database(dbPath, {
|
|
520
|
+
readonly: true,
|
|
521
|
+
timeout: config.busyTimeoutMs || 5e3
|
|
522
|
+
});
|
|
523
|
+
db.pragma("query_only = ON");
|
|
524
|
+
db.pragma(`busy_timeout = ${config.busyTimeoutMs || 5e3}`);
|
|
525
|
+
db.pragma("foreign_keys = ON");
|
|
526
|
+
db.pragma("enable_load_extension = 0");
|
|
527
|
+
db.pragma("cache_size = -20000");
|
|
528
|
+
db.pragma(`mmap_size = ${config.mmapSizeBytes || 134217728}`);
|
|
529
|
+
db.pragma("trusted_schema = OFF");
|
|
530
|
+
if (readOnlyDbCache.size >= MAX_CACHED_DBS) {
|
|
531
|
+
const firstKey = readOnlyDbCache.keys().next().value;
|
|
532
|
+
if (firstKey) {
|
|
533
|
+
try {
|
|
534
|
+
readOnlyDbCache.get(firstKey)?.close();
|
|
535
|
+
} catch {
|
|
536
|
+
}
|
|
537
|
+
readOnlyDbCache.delete(firstKey);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
readOnlyDbCache.set(dbPath, db);
|
|
541
|
+
return db;
|
|
542
|
+
} catch (err) {
|
|
543
|
+
throw new DatabaseError(
|
|
544
|
+
`Failed to open read-only behavior database at ${dbPath}: ${err.message}`
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
function closeDb(project, cwd = process.cwd()) {
|
|
549
|
+
const dbPath = getDbPath(project, cwd);
|
|
550
|
+
if (dbCache.has(dbPath)) {
|
|
551
|
+
try {
|
|
552
|
+
dbCache.get(dbPath)?.close();
|
|
553
|
+
} catch {
|
|
554
|
+
}
|
|
555
|
+
dbCache.delete(dbPath);
|
|
556
|
+
}
|
|
557
|
+
if (readOnlyDbCache.has(dbPath)) {
|
|
558
|
+
try {
|
|
559
|
+
readOnlyDbCache.get(dbPath)?.close();
|
|
560
|
+
} catch {
|
|
561
|
+
}
|
|
562
|
+
readOnlyDbCache.delete(dbPath);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function closeAllDbs() {
|
|
566
|
+
for (const [key, db] of dbCache.entries()) {
|
|
567
|
+
try {
|
|
568
|
+
db.close();
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
dbCache.clear();
|
|
573
|
+
for (const [key, db] of readOnlyDbCache.entries()) {
|
|
574
|
+
try {
|
|
575
|
+
db.close();
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
readOnlyDbCache.clear();
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export {
|
|
583
|
+
LOG_LEVELS,
|
|
584
|
+
getLogLevel,
|
|
585
|
+
logger,
|
|
586
|
+
BehaviorRuntimeError,
|
|
587
|
+
DatabaseError,
|
|
588
|
+
ValidationError,
|
|
589
|
+
NotFoundError,
|
|
590
|
+
ExecutionError,
|
|
591
|
+
SafetyError,
|
|
592
|
+
validatePath2 as validatePath,
|
|
593
|
+
getRegistryPath,
|
|
594
|
+
getRegistry,
|
|
595
|
+
registerProject,
|
|
596
|
+
unregisterProject,
|
|
597
|
+
sanitizeSlug,
|
|
598
|
+
resolveProjectRoot,
|
|
599
|
+
getProjectSlug,
|
|
600
|
+
getBaseDir,
|
|
601
|
+
getProjectDbDir,
|
|
602
|
+
getDbPath,
|
|
603
|
+
getDb,
|
|
604
|
+
getReadOnlyDb,
|
|
605
|
+
closeDb,
|
|
606
|
+
closeAllDbs
|
|
607
|
+
};
|
|
608
|
+
//# sourceMappingURL=chunk-L2LJRSDU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engine/db.ts","../src/utils/logger.ts","../src/utils/errors.ts","../src/engine/config.ts","../src/utils/path-validator.ts","../src/engine/migrations.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport { logger } from '../utils/logger.js';\nimport { DatabaseError } from '../utils/errors.js';\nimport { loadProjectConfig } from './config.js';\nimport { validatePath as validatePathCore, loadPathConfig } from '../utils/path-validator.js';\nimport { runMigrations } from './migrations.js';\n\nexport function validatePath(filePath: string, project?: string): string {\n const projectRoot = resolveProjectRoot(project);\n const pathConfig = loadPathConfig(projectRoot);\n return validatePathCore(filePath, pathConfig);\n}\n\nconst DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.behavior-mcp', 'projects.json');\nconst LEGACY_REGISTRY_PATH = path.join(os.homedir(), '.behavior-runtime-mcp', 'projects.json');\n\nexport function getRegistryPath(): string {\n if (process.env.BEHAVIOR_REGISTRY_PATH) return process.env.BEHAVIOR_REGISTRY_PATH;\n if (!fs.existsSync(DEFAULT_REGISTRY_PATH) && fs.existsSync(LEGACY_REGISTRY_PATH)) {\n return LEGACY_REGISTRY_PATH;\n }\n return DEFAULT_REGISTRY_PATH;\n}\n\nlet registryCache: { registry: Record<string, string>; timestamp: number } | null = null;\nconst REGISTRY_TTL_MS = 2000;\n\nexport function getRegistry(): Record<string, string> {\n const now = Date.now();\n if (registryCache && now - registryCache.timestamp < REGISTRY_TTL_MS) {\n return registryCache.registry;\n }\n\n const regPath = getRegistryPath();\n try {\n if (fs.existsSync(regPath)) {\n const raw = fs.readFileSync(regPath, 'utf-8');\n const registry = JSON.parse(raw);\n registryCache = { registry, timestamp: now };\n return registry;\n }\n } catch (err: any) {\n logger.warn(`Failed to read registry: ${err.message}`);\n }\n return {};\n}\n\nexport function registerProject(projectName: string, projectRoot: string): void {\n const regPath = getRegistryPath();\n const dir = path.dirname(regPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const registry = getRegistry();\n const slug = sanitizeSlug(projectName);\n registry[slug] = path.resolve(projectRoot);\n\n const tmpPath = `${regPath}.tmp.${Math.random().toString(36).substring(2, 8)}`;\n fs.writeFileSync(tmpPath, JSON.stringify(registry, null, 2) + '\\n', { mode: 0o600 });\n fs.renameSync(tmpPath, regPath);\n\n registryCache = { registry, timestamp: Date.now() };\n logger.debug(`Registered project: ${slug} -> ${projectRoot}`);\n}\n\nexport function unregisterProject(projectName: string): void {\n const regPath = getRegistryPath();\n const registry = getRegistry();\n const slug = sanitizeSlug(projectName);\n if (registry[slug]) {\n delete registry[slug];\n const tmpPath = `${regPath}.tmp.${Math.random().toString(36).substring(2, 8)}`;\n fs.writeFileSync(tmpPath, JSON.stringify(registry, null, 2) + '\\n', { mode: 0o600 });\n fs.renameSync(tmpPath, regPath);\n registryCache = { registry, timestamp: Date.now() };\n }\n}\n\nexport function sanitizeSlug(str: string): string {\n return str\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9-_]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nexport function resolveProjectRoot(project?: string, cwd = process.cwd()): string {\n const currentCwd = path.resolve(cwd);\n\n // 1. Explicit project parameter lookup\n if (project) {\n const registry = getRegistry();\n const slug = sanitizeSlug(project);\n if (registry[slug] && fs.existsSync(registry[slug])) {\n return registry[slug];\n }\n if (registry[project] && fs.existsSync(registry[project])) {\n return registry[project];\n }\n }\n\n // 2. Exact match in registry for current CWD\n const registry = getRegistry();\n for (const [, projectPath] of Object.entries(registry)) {\n const resolvedPath = path.resolve(projectPath);\n if (resolvedPath === currentCwd && fs.existsSync(resolvedPath)) {\n return resolvedPath;\n }\n }\n\n // 3. If current directory has own package.json or local MCP marker, it is its own project root\n if (\n fs.existsSync(path.join(currentCwd, '.behavior-mcp')) ||\n fs.existsSync(path.join(currentCwd, '.behavior-runtime-mcp')) ||\n fs.existsSync(path.join(currentCwd, '.state-memory-mcp')) ||\n fs.existsSync(path.join(currentCwd, '.world-model-mcp')) ||\n fs.existsSync(path.join(currentCwd, '.vision-memory-mcp')) ||\n fs.existsSync(path.join(currentCwd, 'package.json'))\n ) {\n return currentCwd;\n }\n\n // 4. Longest matching registered ancestor directory (excluding homedir)\n let bestMatch: string | undefined;\n for (const [, projectPath] of Object.entries(registry)) {\n const resolvedPath = path.resolve(projectPath);\n if (resolvedPath === os.homedir()) continue;\n if (currentCwd.startsWith(resolvedPath + path.sep)) {\n if (fs.existsSync(resolvedPath)) {\n if (!bestMatch || resolvedPath.length > bestMatch.length) {\n bestMatch = resolvedPath;\n }\n }\n }\n }\n if (bestMatch) {\n return bestMatch;\n }\n\n // 5. Walk up directory tree to find project markers\n let curr = currentCwd;\n const home = os.homedir();\n while (curr !== path.dirname(curr) && curr !== home) {\n if (\n fs.existsSync(path.join(curr, '.behavior-mcp')) ||\n fs.existsSync(path.join(curr, '.behavior-runtime-mcp')) ||\n fs.existsSync(path.join(curr, '.state-memory-mcp')) ||\n fs.existsSync(path.join(curr, '.world-model-mcp')) ||\n fs.existsSync(path.join(curr, '.vision-memory-mcp')) ||\n fs.existsSync(path.join(curr, 'package.json')) ||\n fs.existsSync(path.join(curr, '.git'))\n ) {\n return curr;\n }\n curr = path.dirname(curr);\n }\n return currentCwd;\n}\n\nexport function getProjectSlug(project?: string, cwd = process.cwd()): string {\n if (project && project.trim().length > 0) {\n return sanitizeSlug(project);\n }\n const root = resolveProjectRoot(project, cwd);\n const config = loadProjectConfig(root);\n if (config.projectName) {\n return sanitizeSlug(config.projectName);\n }\n return sanitizeSlug(path.basename(root));\n}\n\nexport function getBaseDir(projectRoot: string): string {\n const config = loadProjectConfig(projectRoot);\n if (config.storagePath) {\n return path.resolve(projectRoot, config.storagePath);\n }\n if (process.env.BEHAVIOR_MCP_DIR || process.env.BEHAVIOR_RUNTIME_MCP_DIR) {\n return path.resolve(\n projectRoot,\n (process.env.BEHAVIOR_MCP_DIR || process.env.BEHAVIOR_RUNTIME_MCP_DIR)!\n );\n }\n if (fs.existsSync(path.join(projectRoot, '.behavior-mcp'))) {\n return path.join(projectRoot, '.behavior-mcp');\n }\n if (fs.existsSync(path.join(projectRoot, '.behavior-runtime-mcp'))) {\n return path.join(projectRoot, '.behavior-runtime-mcp');\n }\n return path.join(projectRoot, '.behavior-mcp');\n}\n\nexport function getProjectDbDir(project?: string, cwd = process.cwd()): string {\n const root = resolveProjectRoot(project, cwd);\n const slug = getProjectSlug(project, cwd);\n const baseDir = getBaseDir(root);\n return path.join(baseDir, slug);\n}\n\nexport function getDbPath(project?: string, cwd = process.cwd()): string {\n const dbDir = getProjectDbDir(project, cwd);\n return path.join(dbDir, 'behavior.db');\n}\n\nconst dbCache = new Map<string, Database.Database>();\nconst readOnlyDbCache = new Map<string, Database.Database>();\nconst MAX_CACHED_DBS = 5;\n\nexport function getDb(project?: string, cwd = process.cwd()): Database.Database {\n const dbPath = getDbPath(project, cwd);\n if (dbCache.has(dbPath)) {\n return dbCache.get(dbPath)!;\n }\n\n const dbDir = path.dirname(dbPath);\n if (!fs.existsSync(dbDir)) {\n fs.mkdirSync(dbDir, { recursive: true, mode: 0o700 });\n }\n\n const root = resolveProjectRoot(project, cwd);\n const config = loadProjectConfig(root);\n\n try {\n const db = new Database(dbPath, {\n timeout: config.busyTimeoutMs || 5000,\n });\n\n db.pragma('journal_mode = WAL');\n db.pragma('synchronous = NORMAL');\n db.pragma(`busy_timeout = ${config.busyTimeoutMs || 5000}`);\n db.pragma('foreign_keys = ON');\n db.pragma('cache_size = -20000');\n db.pragma(`mmap_size = ${config.mmapSizeBytes || 134217728}`);\n db.pragma('trusted_schema = OFF');\n\n runMigrations(db);\n\n if (dbCache.size >= MAX_CACHED_DBS) {\n const firstKey = dbCache.keys().next().value;\n if (firstKey) {\n try {\n dbCache.get(firstKey)?.close();\n } catch {}\n dbCache.delete(firstKey);\n }\n }\n\n dbCache.set(dbPath, db);\n return db;\n } catch (err: any) {\n throw new DatabaseError(\n `Failed to open behavior-runtime database at ${dbPath}: ${err.message}`\n );\n }\n}\n\nexport function getReadOnlyDb(project?: string, cwd = process.cwd()): Database.Database {\n const dbPath = getDbPath(project, cwd);\n if (readOnlyDbCache.has(dbPath)) {\n return readOnlyDbCache.get(dbPath)!;\n }\n\n if (!fs.existsSync(dbPath)) {\n getDb(project, cwd);\n }\n\n const root = resolveProjectRoot(project, cwd);\n const config = loadProjectConfig(root);\n\n try {\n const db = new Database(dbPath, {\n readonly: true,\n timeout: config.busyTimeoutMs || 5000,\n });\n\n db.pragma('query_only = ON');\n db.pragma(`busy_timeout = ${config.busyTimeoutMs || 5000}`);\n db.pragma('foreign_keys = ON');\n db.pragma('enable_load_extension = 0');\n db.pragma('cache_size = -20000');\n db.pragma(`mmap_size = ${config.mmapSizeBytes || 134217728}`);\n db.pragma('trusted_schema = OFF');\n\n if (readOnlyDbCache.size >= MAX_CACHED_DBS) {\n const firstKey = readOnlyDbCache.keys().next().value;\n if (firstKey) {\n try {\n readOnlyDbCache.get(firstKey)?.close();\n } catch {}\n readOnlyDbCache.delete(firstKey);\n }\n }\n\n readOnlyDbCache.set(dbPath, db);\n return db;\n } catch (err: any) {\n throw new DatabaseError(\n `Failed to open read-only behavior database at ${dbPath}: ${err.message}`\n );\n }\n}\n\nexport function closeDb(project?: string, cwd = process.cwd()): void {\n const dbPath = getDbPath(project, cwd);\n if (dbCache.has(dbPath)) {\n try {\n dbCache.get(dbPath)?.close();\n } catch {}\n dbCache.delete(dbPath);\n }\n if (readOnlyDbCache.has(dbPath)) {\n try {\n readOnlyDbCache.get(dbPath)?.close();\n } catch {}\n readOnlyDbCache.delete(dbPath);\n }\n}\n\nexport function closeAllDbs(): void {\n for (const [key, db] of dbCache.entries()) {\n try {\n db.close();\n } catch {}\n }\n dbCache.clear();\n\n for (const [key, db] of readOnlyDbCache.entries()) {\n try {\n db.close();\n } catch {}\n }\n readOnlyDbCache.clear();\n}\n","type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport const LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nexport function getLogLevel(): number {\n const envLevel = (\n process.env.BEHAVIOR_LOG_LEVEL || process.env.BEHAVIOR_RUNTIME_MCP_LOG_LEVEL\n )?.toLowerCase() as LogLevel | undefined;\n if (envLevel && envLevel in LOG_LEVELS) {\n return LOG_LEVELS[envLevel];\n }\n return LOG_LEVELS.info;\n}\n\nexport const logger = {\n debug: (message: string, ...args: unknown[]) => {\n if (getLogLevel() <= LOG_LEVELS.debug) {\n console.error(`[DEBUG] ${message}`, ...args);\n }\n },\n info: (message: string, ...args: unknown[]) => {\n if (getLogLevel() <= LOG_LEVELS.info) {\n console.error(`[INFO] ${message}`, ...args);\n }\n },\n warn: (message: string, ...args: unknown[]) => {\n if (getLogLevel() <= LOG_LEVELS.warn) {\n console.error(`[WARN] ${message}`, ...args);\n }\n },\n error: (message: string, ...args: unknown[]) => {\n if (getLogLevel() <= LOG_LEVELS.error) {\n console.error(`[ERROR] ${message}`, ...args);\n }\n },\n};\n","export class BehaviorRuntimeError extends Error {\n public readonly code: string;\n public readonly details?: unknown;\n\n constructor(message: string, code = 'INTERNAL_ERROR', details?: unknown) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.details = details;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class DatabaseError extends BehaviorRuntimeError {\n constructor(message: string, details?: unknown) {\n super(message, 'DATABASE_ERROR', details);\n }\n}\n\nexport class ValidationError extends BehaviorRuntimeError {\n constructor(message: string, details?: unknown) {\n super(message, 'VALIDATION_ERROR', details);\n }\n}\n\nexport class NotFoundError extends BehaviorRuntimeError {\n constructor(message: string, details?: unknown) {\n super(message, 'NOT_FOUND_ERROR', details);\n }\n}\n\nexport class ExecutionError extends BehaviorRuntimeError {\n constructor(message: string, details?: unknown) {\n super(message, 'EXECUTION_ERROR', details);\n }\n}\n\nexport class SafetyError extends BehaviorRuntimeError {\n constructor(message: string, details?: unknown) {\n super(message, 'SAFETY_ERROR', details);\n }\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport { z } from 'zod';\nimport { logger } from '../utils/logger.js';\n\nexport const ProjectConfigSchema = z\n .object({\n projectName: z.string().optional(),\n defaultBranch: z.string().optional(),\n storagePath: z.string().optional(),\n allowedExportDirs: z.array(z.string()).optional(),\n busyTimeoutMs: z.number().int().positive().optional(),\n mmapSizeBytes: z.number().int().positive().optional(),\n tickRateHz: z.number().int().positive().optional(),\n stuckThreshold: z.number().int().positive().optional(),\n maxActionsPerSec: z.number().int().positive().optional(),\n allowlistOrigins: z.array(z.string()).optional(),\n accessMode: z.enum(['normal', 'read_only']).optional(),\n })\n .passthrough();\n\nexport type ProjectConfig = z.infer<typeof ProjectConfigSchema>;\n\nconst cachedConfigs = new Map<string, { config: ProjectConfig; timestamp: number }>();\nconst CONFIG_TTL_MS = 2000;\n\nexport function loadProjectConfig(projectRoot: string): ProjectConfig {\n const now = Date.now();\n const cached = cachedConfigs.get(projectRoot);\n if (cached && now - cached.timestamp < CONFIG_TTL_MS) {\n return cached.config;\n }\n\n const effectiveRoot = process.env.PUTERVISION_PROJECT_DIR || projectRoot;\n const configPath = path.join(effectiveRoot, '.behavior-mcp.json');\n const legacyConfigPath = path.join(effectiveRoot, '.behavior-runtime-mcp.json');\n let config: ProjectConfig = {};\n\n const activePath = fs.existsSync(configPath)\n ? configPath\n : fs.existsSync(legacyConfigPath)\n ? legacyConfigPath\n : null;\n\n if (activePath) {\n try {\n const raw = fs.readFileSync(activePath, 'utf-8');\n const parsed = JSON.parse(raw);\n const validated = ProjectConfigSchema.safeParse(parsed);\n if (validated.success) {\n config = validated.data;\n } else {\n logger.warn(`Invalid ${path.basename(activePath)} schema: ${validated.error.message}`);\n config = parsed;\n }\n } catch (err: any) {\n logger.warn(`Failed to parse ${path.basename(activePath)}: ${err.message}`);\n }\n }\n\n if (process.env.PUTERVISION_PROJECT_SLUG && !config.projectName) {\n config.projectName = process.env.PUTERVISION_PROJECT_SLUG;\n }\n\n cachedConfigs.set(projectRoot, { config, timestamp: now });\n return config;\n}\n","import * as path from 'path';\nimport * as os from 'os';\nimport { ValidationError } from './errors.js';\n\nexport interface PathConfig {\n projectRoot: string;\n allowedExportDirs?: string[];\n}\n\nexport function getDefaultAllowedDirs(projectRoot: string): string[] {\n const resolvedRoot = path.resolve(projectRoot);\n const homeBackups = path.join(os.homedir(), '.behavior-mcp', 'backups');\n return [resolvedRoot, homeBackups];\n}\n\nexport function loadPathConfig(projectRoot: string): PathConfig {\n return {\n projectRoot: path.resolve(projectRoot),\n allowedExportDirs: getDefaultAllowedDirs(projectRoot),\n };\n}\n\nexport function validatePath(filePath: string, config: PathConfig): string {\n if (!filePath || typeof filePath !== 'string') {\n throw new ValidationError('File path must be a non-empty string.');\n }\n\n let resolved: string;\n if (path.isAbsolute(filePath)) {\n resolved = path.resolve(filePath);\n } else {\n resolved = path.resolve(config.projectRoot, filePath);\n }\n\n const allowed = (config.allowedExportDirs || [config.projectRoot]).map((d) => path.resolve(d));\n const isAllowed = allowed.some((dir) => {\n return resolved === dir || resolved.startsWith(dir + path.sep);\n });\n\n if (!isAllowed) {\n throw new ValidationError(\n `Access denied: path \"${filePath}\" resolves outside allowed directories.`\n );\n }\n\n return resolved;\n}\n","import Database from 'better-sqlite3';\nimport { logger } from '../utils/logger.js';\n\nexport function runMigrations(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n\n const currentVersionRow = db\n .prepare(\"SELECT value FROM schema_meta WHERE key = 'version'\")\n .get() as { value: string } | undefined;\n const currentVersion = currentVersionRow ? parseInt(currentVersionRow.value, 10) : 0;\n\n if (currentVersion < 1) {\n logger.info('Applying migration v1 for behavior-mcp...');\n db.exec(`\n CREATE TABLE IF NOT EXISTS behavior_definitions (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n name TEXT NOT NULL,\n version INTEGER NOT NULL DEFAULT 1,\n description TEXT,\n tree_json TEXT NOT NULL,\n tree_hash TEXT NOT NULL,\n is_active INTEGER NOT NULL DEFAULT 1,\n metadata_json TEXT,\n client_request_id TEXT,\n created_at TEXT NOT NULL,\n UNIQUE(project, name, version)\n );\n CREATE INDEX IF NOT EXISTS idx_behaviors_project ON behavior_definitions(project);\n CREATE INDEX IF NOT EXISTS idx_behaviors_name ON behavior_definitions(project, name);\n CREATE INDEX IF NOT EXISTS idx_behaviors_client_req ON behavior_definitions(project, client_request_id);\n\n CREATE TABLE IF NOT EXISTS execution_state (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n behavior_name TEXT NOT NULL,\n behavior_version INTEGER NOT NULL DEFAULT 1,\n session_id TEXT,\n intention_id TEXT,\n status TEXT NOT NULL DEFAULT 'idle',\n active_node_path TEXT,\n blackboard_json TEXT NOT NULL DEFAULT '{}',\n current_tick INTEGER NOT NULL DEFAULT 0,\n tick_rate_hz INTEGER NOT NULL DEFAULT 60,\n duration_ms INTEGER NOT NULL DEFAULT 0,\n stuck_score REAL NOT NULL DEFAULT 0.0,\n error TEXT,\n metadata_json TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_execution_project ON execution_state(project);\n CREATE INDEX IF NOT EXISTS idx_execution_status ON execution_state(project, status);\n CREATE INDEX IF NOT EXISTS idx_execution_intention ON execution_state(intention_id);\n\n CREATE TABLE IF NOT EXISTS triggers (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n name TEXT NOT NULL,\n behavior_name TEXT NOT NULL,\n condition_type TEXT NOT NULL,\n condition_params_json TEXT NOT NULL,\n priority REAL NOT NULL DEFAULT 0.5,\n cooldown_ms INTEGER NOT NULL DEFAULT 1000,\n last_fired_at TEXT,\n is_enabled INTEGER NOT NULL DEFAULT 1,\n metadata_json TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE(project, name)\n );\n CREATE INDEX IF NOT EXISTS idx_triggers_project ON triggers(project);\n CREATE INDEX IF NOT EXISTS idx_triggers_enabled ON triggers(project, is_enabled);\n\n CREATE TABLE IF NOT EXISTS execution_metrics (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n execution_id TEXT NOT NULL,\n session_id TEXT,\n intention_id TEXT,\n behavior_name TEXT NOT NULL,\n status TEXT NOT NULL,\n tick_count INTEGER NOT NULL DEFAULT 0,\n duration_ms INTEGER NOT NULL DEFAULT 0,\n avg_tick_ms REAL NOT NULL DEFAULT 0.0,\n max_tick_ms REAL NOT NULL DEFAULT 0.0,\n stuck_count INTEGER NOT NULL DEFAULT 0,\n interrupt_count INTEGER NOT NULL DEFAULT 0,\n category_metrics_json TEXT,\n created_at TEXT NOT NULL,\n FOREIGN KEY (execution_id) REFERENCES execution_state(id) ON DELETE CASCADE\n );\n CREATE INDEX IF NOT EXISTS idx_metrics_project ON execution_metrics(project);\n CREATE INDEX IF NOT EXISTS idx_metrics_execution ON execution_metrics(execution_id);\n CREATE INDEX IF NOT EXISTS idx_metrics_behavior ON execution_metrics(project, behavior_name);\n\n CREATE TABLE IF NOT EXISTS recordings (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n execution_id TEXT NOT NULL,\n behavior_name TEXT NOT NULL,\n total_frames INTEGER NOT NULL DEFAULT 0,\n frames_json TEXT NOT NULL,\n duration_ms INTEGER NOT NULL DEFAULT 0,\n metadata_json TEXT,\n created_at TEXT NOT NULL,\n FOREIGN KEY (execution_id) REFERENCES execution_state(id) ON DELETE CASCADE\n );\n CREATE INDEX IF NOT EXISTS idx_recordings_project ON recordings(project);\n CREATE INDEX IF NOT EXISTS idx_recordings_execution ON recordings(execution_id);\n\n CREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n entity_id TEXT NOT NULL,\n entity_type TEXT NOT NULL,\n action TEXT NOT NULL,\n prev_hash TEXT NOT NULL,\n hash TEXT NOT NULL,\n details_json TEXT,\n timestamp TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_events_project ON events(project);\n CREATE INDEX IF NOT EXISTS idx_events_entity ON events(project, entity_id);\n CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);\n\n CREATE TABLE IF NOT EXISTS snapshots (\n id TEXT PRIMARY KEY,\n project TEXT NOT NULL,\n name TEXT NOT NULL,\n description TEXT,\n data_json TEXT NOT NULL,\n created_at TEXT NOT NULL,\n UNIQUE(project, name)\n );\n CREATE INDEX IF NOT EXISTS idx_snapshots_project ON snapshots(project);\n\n INSERT INTO schema_meta (key, value) VALUES ('version', '1')\n ON CONFLICT(key) DO UPDATE SET value = '1';\n `);\n }\n}\n"],"mappings":";AAAA,OAAO,cAAc;AACrB,YAAYA,WAAU;AACtB,YAAYC,SAAQ;AACpB,YAAYC,SAAQ;;;ACDb,IAAM,aAAuC;AAAA,EAClD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEO,SAAS,cAAsB;AACpC,QAAM,YACJ,QAAQ,IAAI,sBAAsB,QAAQ,IAAI,iCAC7C,YAAY;AACf,MAAI,YAAY,YAAY,YAAY;AACtC,WAAO,WAAW,QAAQ;AAAA,EAC5B;AACA,SAAO,WAAW;AACpB;AAEO,IAAM,SAAS;AAAA,EACpB,OAAO,CAAC,YAAoB,SAAoB;AAC9C,QAAI,YAAY,KAAK,WAAW,OAAO;AACrC,cAAQ,MAAM,WAAW,OAAO,IAAI,GAAG,IAAI;AAAA,IAC7C;AAAA,EACF;AAAA,EACA,MAAM,CAAC,YAAoB,SAAoB;AAC7C,QAAI,YAAY,KAAK,WAAW,MAAM;AACpC,cAAQ,MAAM,UAAU,OAAO,IAAI,GAAG,IAAI;AAAA,IAC5C;AAAA,EACF;AAAA,EACA,MAAM,CAAC,YAAoB,SAAoB;AAC7C,QAAI,YAAY,KAAK,WAAW,MAAM;AACpC,cAAQ,MAAM,UAAU,OAAO,IAAI,GAAG,IAAI;AAAA,IAC5C;AAAA,EACF;AAAA,EACA,OAAO,CAAC,YAAoB,SAAoB;AAC9C,QAAI,YAAY,KAAK,WAAW,OAAO;AACrC,cAAQ,MAAM,WAAW,OAAO,IAAI,GAAG,IAAI;AAAA,IAC7C;AAAA,EACF;AACF;;;ACxCO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9B;AAAA,EACA;AAAA,EAEhB,YAAY,SAAiB,OAAO,kBAAkB,SAAmB;AACvE,UAAM,OAAO;AACb,SAAK,OAAO,KAAK,YAAY;AAC7B,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAEO,IAAM,gBAAN,cAA4B,qBAAqB;AAAA,EACtD,YAAY,SAAiB,SAAmB;AAC9C,UAAM,SAAS,kBAAkB,OAAO;AAAA,EAC1C;AACF;AAEO,IAAM,kBAAN,cAA8B,qBAAqB;AAAA,EACxD,YAAY,SAAiB,SAAmB;AAC9C,UAAM,SAAS,oBAAoB,OAAO;AAAA,EAC5C;AACF;AAEO,IAAM,gBAAN,cAA4B,qBAAqB;AAAA,EACtD,YAAY,SAAiB,SAAmB;AAC9C,UAAM,SAAS,mBAAmB,OAAO;AAAA,EAC3C;AACF;AAEO,IAAM,iBAAN,cAA6B,qBAAqB;AAAA,EACvD,YAAY,SAAiB,SAAmB;AAC9C,UAAM,SAAS,mBAAmB,OAAO;AAAA,EAC3C;AACF;AAEO,IAAM,cAAN,cAA0B,qBAAqB;AAAA,EACpD,YAAY,SAAiB,SAAmB;AAC9C,UAAM,SAAS,gBAAgB,OAAO;AAAA,EACxC;AACF;;;ACzCA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,SAAS;AAGX,IAAM,sBAAsB,EAChC,OAAO;AAAA,EACN,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,mBAAmB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAChD,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACpD,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACpD,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACrD,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,kBAAkB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC/C,YAAY,EAAE,KAAK,CAAC,UAAU,WAAW,CAAC,EAAE,SAAS;AACvD,CAAC,EACA,YAAY;AAIf,IAAM,gBAAgB,oBAAI,IAA0D;AACpF,IAAM,gBAAgB;AAEf,SAAS,kBAAkB,aAAoC;AACpE,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,cAAc,IAAI,WAAW;AAC5C,MAAI,UAAU,MAAM,OAAO,YAAY,eAAe;AACpD,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,gBAAgB,QAAQ,IAAI,2BAA2B;AAC7D,QAAM,aAAkB,UAAK,eAAe,oBAAoB;AAChE,QAAM,mBAAwB,UAAK,eAAe,4BAA4B;AAC9E,MAAI,SAAwB,CAAC;AAE7B,QAAM,aAAgB,cAAW,UAAU,IACvC,aACG,cAAW,gBAAgB,IAC5B,mBACA;AAEN,MAAI,YAAY;AACd,QAAI;AACF,YAAM,MAAS,gBAAa,YAAY,OAAO;AAC/C,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,YAAM,YAAY,oBAAoB,UAAU,MAAM;AACtD,UAAI,UAAU,SAAS;AACrB,iBAAS,UAAU;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,WAAgB,cAAS,UAAU,CAAC,YAAY,UAAU,MAAM,OAAO,EAAE;AACrF,iBAAS;AAAA,MACX;AAAA,IACF,SAAS,KAAU;AACjB,aAAO,KAAK,mBAAwB,cAAS,UAAU,CAAC,KAAK,IAAI,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,MAAI,QAAQ,IAAI,4BAA4B,CAAC,OAAO,aAAa;AAC/D,WAAO,cAAc,QAAQ,IAAI;AAAA,EACnC;AAEA,gBAAc,IAAI,aAAa,EAAE,QAAQ,WAAW,IAAI,CAAC;AACzD,SAAO;AACT;;;AClEA,YAAYC,WAAU;AACtB,YAAY,QAAQ;AAQb,SAAS,sBAAsB,aAA+B;AACnE,QAAM,eAAoB,cAAQ,WAAW;AAC7C,QAAM,cAAmB,WAAQ,WAAQ,GAAG,iBAAiB,SAAS;AACtE,SAAO,CAAC,cAAc,WAAW;AACnC;AAEO,SAAS,eAAe,aAAiC;AAC9D,SAAO;AAAA,IACL,aAAkB,cAAQ,WAAW;AAAA,IACrC,mBAAmB,sBAAsB,WAAW;AAAA,EACtD;AACF;AAEO,SAAS,aAAa,UAAkB,QAA4B;AACzE,MAAI,CAAC,YAAY,OAAO,aAAa,UAAU;AAC7C,UAAM,IAAI,gBAAgB,uCAAuC;AAAA,EACnE;AAEA,MAAI;AACJ,MAAS,iBAAW,QAAQ,GAAG;AAC7B,eAAgB,cAAQ,QAAQ;AAAA,EAClC,OAAO;AACL,eAAgB,cAAQ,OAAO,aAAa,QAAQ;AAAA,EACtD;AAEA,QAAM,WAAW,OAAO,qBAAqB,CAAC,OAAO,WAAW,GAAG,IAAI,CAAC,MAAW,cAAQ,CAAC,CAAC;AAC7F,QAAM,YAAY,QAAQ,KAAK,CAAC,QAAQ;AACtC,WAAO,aAAa,OAAO,SAAS,WAAW,MAAW,SAAG;AAAA,EAC/D,CAAC;AAED,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,wBAAwB,QAAQ;AAAA,IAClC;AAAA,EACF;AAEA,SAAO;AACT;;;AC3CO,SAAS,cAAc,IAA6B;AACzD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,GAKP;AAED,QAAM,oBAAoB,GACvB,QAAQ,qDAAqD,EAC7D,IAAI;AACP,QAAM,iBAAiB,oBAAoB,SAAS,kBAAkB,OAAO,EAAE,IAAI;AAEnF,MAAI,iBAAiB,GAAG;AACtB,WAAO,KAAK,2CAA2C;AACvD,OAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KA8HP;AAAA,EACH;AACF;;;ALxIO,SAASC,cAAa,UAAkB,SAA0B;AACvE,QAAM,cAAc,mBAAmB,OAAO;AAC9C,QAAM,aAAa,eAAe,WAAW;AAC7C,SAAO,aAAiB,UAAU,UAAU;AAC9C;AAEA,IAAM,wBAA6B,WAAQ,YAAQ,GAAG,iBAAiB,eAAe;AACtF,IAAM,uBAA4B,WAAQ,YAAQ,GAAG,yBAAyB,eAAe;AAEtF,SAAS,kBAA0B;AACxC,MAAI,QAAQ,IAAI,uBAAwB,QAAO,QAAQ,IAAI;AAC3D,MAAI,CAAI,eAAW,qBAAqB,KAAQ,eAAW,oBAAoB,GAAG;AAChF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,IAAI,gBAAgF;AACpF,IAAM,kBAAkB;AAEjB,SAAS,cAAsC;AACpD,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,iBAAiB,MAAM,cAAc,YAAY,iBAAiB;AACpE,WAAO,cAAc;AAAA,EACvB;AAEA,QAAM,UAAU,gBAAgB;AAChC,MAAI;AACF,QAAO,eAAW,OAAO,GAAG;AAC1B,YAAM,MAAS,iBAAa,SAAS,OAAO;AAC5C,YAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,sBAAgB,EAAE,UAAU,WAAW,IAAI;AAC3C,aAAO;AAAA,IACT;AAAA,EACF,SAAS,KAAU;AACjB,WAAO,KAAK,4BAA4B,IAAI,OAAO,EAAE;AAAA,EACvD;AACA,SAAO,CAAC;AACV;AAEO,SAAS,gBAAgB,aAAqB,aAA2B;AAC9E,QAAM,UAAU,gBAAgB;AAChC,QAAM,MAAW,cAAQ,OAAO;AAChC,MAAI,CAAI,eAAW,GAAG,GAAG;AACvB,IAAG,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACvC;AAEA,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,aAAa,WAAW;AACrC,WAAS,IAAI,IAAS,cAAQ,WAAW;AAEzC,QAAM,UAAU,GAAG,OAAO,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAC5E,EAAG,kBAAc,SAAS,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;AACnF,EAAG,eAAW,SAAS,OAAO;AAE9B,kBAAgB,EAAE,UAAU,WAAW,KAAK,IAAI,EAAE;AAClD,SAAO,MAAM,uBAAuB,IAAI,OAAO,WAAW,EAAE;AAC9D;AAEO,SAAS,kBAAkB,aAA2B;AAC3D,QAAM,UAAU,gBAAgB;AAChC,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,aAAa,WAAW;AACrC,MAAI,SAAS,IAAI,GAAG;AAClB,WAAO,SAAS,IAAI;AACpB,UAAM,UAAU,GAAG,OAAO,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAC5E,IAAG,kBAAc,SAAS,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;AACnF,IAAG,eAAW,SAAS,OAAO;AAC9B,oBAAgB,EAAE,UAAU,WAAW,KAAK,IAAI,EAAE;AAAA,EACpD;AACF;AAEO,SAAS,aAAa,KAAqB;AAChD,SAAO,IACJ,KAAK,EACL,YAAY,EACZ,QAAQ,gBAAgB,GAAG,EAC3B,QAAQ,OAAO,GAAG,EAClB,QAAQ,YAAY,EAAE;AAC3B;AAEO,SAAS,mBAAmB,SAAkB,MAAM,QAAQ,IAAI,GAAW;AAChF,QAAM,aAAkB,cAAQ,GAAG;AAGnC,MAAI,SAAS;AACX,UAAMC,YAAW,YAAY;AAC7B,UAAM,OAAO,aAAa,OAAO;AACjC,QAAIA,UAAS,IAAI,KAAQ,eAAWA,UAAS,IAAI,CAAC,GAAG;AACnD,aAAOA,UAAS,IAAI;AAAA,IACtB;AACA,QAAIA,UAAS,OAAO,KAAQ,eAAWA,UAAS,OAAO,CAAC,GAAG;AACzD,aAAOA,UAAS,OAAO;AAAA,IACzB;AAAA,EACF;AAGA,QAAM,WAAW,YAAY;AAC7B,aAAW,CAAC,EAAE,WAAW,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACtD,UAAM,eAAoB,cAAQ,WAAW;AAC7C,QAAI,iBAAiB,cAAiB,eAAW,YAAY,GAAG;AAC9D,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MACK,eAAgB,WAAK,YAAY,eAAe,CAAC,KACjD,eAAgB,WAAK,YAAY,uBAAuB,CAAC,KACzD,eAAgB,WAAK,YAAY,mBAAmB,CAAC,KACrD,eAAgB,WAAK,YAAY,kBAAkB,CAAC,KACpD,eAAgB,WAAK,YAAY,oBAAoB,CAAC,KACtD,eAAgB,WAAK,YAAY,cAAc,CAAC,GACnD;AACA,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,aAAW,CAAC,EAAE,WAAW,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACtD,UAAM,eAAoB,cAAQ,WAAW;AAC7C,QAAI,iBAAoB,YAAQ,EAAG;AACnC,QAAI,WAAW,WAAW,eAAoB,SAAG,GAAG;AAClD,UAAO,eAAW,YAAY,GAAG;AAC/B,YAAI,CAAC,aAAa,aAAa,SAAS,UAAU,QAAQ;AACxD,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,WAAW;AACb,WAAO;AAAA,EACT;AAGA,MAAI,OAAO;AACX,QAAM,OAAU,YAAQ;AACxB,SAAO,SAAc,cAAQ,IAAI,KAAK,SAAS,MAAM;AACnD,QACK,eAAgB,WAAK,MAAM,eAAe,CAAC,KAC3C,eAAgB,WAAK,MAAM,uBAAuB,CAAC,KACnD,eAAgB,WAAK,MAAM,mBAAmB,CAAC,KAC/C,eAAgB,WAAK,MAAM,kBAAkB,CAAC,KAC9C,eAAgB,WAAK,MAAM,oBAAoB,CAAC,KAChD,eAAgB,WAAK,MAAM,cAAc,CAAC,KAC1C,eAAgB,WAAK,MAAM,MAAM,CAAC,GACrC;AACA,aAAO;AAAA,IACT;AACA,WAAY,cAAQ,IAAI;AAAA,EAC1B;AACA,SAAO;AACT;AAEO,SAAS,eAAe,SAAkB,MAAM,QAAQ,IAAI,GAAW;AAC5E,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACxC,WAAO,aAAa,OAAO;AAAA,EAC7B;AACA,QAAM,OAAO,mBAAmB,SAAS,GAAG;AAC5C,QAAM,SAAS,kBAAkB,IAAI;AACrC,MAAI,OAAO,aAAa;AACtB,WAAO,aAAa,OAAO,WAAW;AAAA,EACxC;AACA,SAAO,aAAkB,eAAS,IAAI,CAAC;AACzC;AAEO,SAAS,WAAW,aAA6B;AACtD,QAAM,SAAS,kBAAkB,WAAW;AAC5C,MAAI,OAAO,aAAa;AACtB,WAAY,cAAQ,aAAa,OAAO,WAAW;AAAA,EACrD;AACA,MAAI,QAAQ,IAAI,oBAAoB,QAAQ,IAAI,0BAA0B;AACxE,WAAY;AAAA,MACV;AAAA,MACC,QAAQ,IAAI,oBAAoB,QAAQ,IAAI;AAAA,IAC/C;AAAA,EACF;AACA,MAAO,eAAgB,WAAK,aAAa,eAAe,CAAC,GAAG;AAC1D,WAAY,WAAK,aAAa,eAAe;AAAA,EAC/C;AACA,MAAO,eAAgB,WAAK,aAAa,uBAAuB,CAAC,GAAG;AAClE,WAAY,WAAK,aAAa,uBAAuB;AAAA,EACvD;AACA,SAAY,WAAK,aAAa,eAAe;AAC/C;AAEO,SAAS,gBAAgB,SAAkB,MAAM,QAAQ,IAAI,GAAW;AAC7E,QAAM,OAAO,mBAAmB,SAAS,GAAG;AAC5C,QAAM,OAAO,eAAe,SAAS,GAAG;AACxC,QAAM,UAAU,WAAW,IAAI;AAC/B,SAAY,WAAK,SAAS,IAAI;AAChC;AAEO,SAAS,UAAU,SAAkB,MAAM,QAAQ,IAAI,GAAW;AACvE,QAAM,QAAQ,gBAAgB,SAAS,GAAG;AAC1C,SAAY,WAAK,OAAO,aAAa;AACvC;AAEA,IAAM,UAAU,oBAAI,IAA+B;AACnD,IAAM,kBAAkB,oBAAI,IAA+B;AAC3D,IAAM,iBAAiB;AAEhB,SAAS,MAAM,SAAkB,MAAM,QAAQ,IAAI,GAAsB;AAC9E,QAAM,SAAS,UAAU,SAAS,GAAG;AACrC,MAAI,QAAQ,IAAI,MAAM,GAAG;AACvB,WAAO,QAAQ,IAAI,MAAM;AAAA,EAC3B;AAEA,QAAM,QAAa,cAAQ,MAAM;AACjC,MAAI,CAAI,eAAW,KAAK,GAAG;AACzB,IAAG,cAAU,OAAO,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACtD;AAEA,QAAM,OAAO,mBAAmB,SAAS,GAAG;AAC5C,QAAM,SAAS,kBAAkB,IAAI;AAErC,MAAI;AACF,UAAM,KAAK,IAAI,SAAS,QAAQ;AAAA,MAC9B,SAAS,OAAO,iBAAiB;AAAA,IACnC,CAAC;AAED,OAAG,OAAO,oBAAoB;AAC9B,OAAG,OAAO,sBAAsB;AAChC,OAAG,OAAO,kBAAkB,OAAO,iBAAiB,GAAI,EAAE;AAC1D,OAAG,OAAO,mBAAmB;AAC7B,OAAG,OAAO,qBAAqB;AAC/B,OAAG,OAAO,eAAe,OAAO,iBAAiB,SAAS,EAAE;AAC5D,OAAG,OAAO,sBAAsB;AAEhC,kBAAc,EAAE;AAEhB,QAAI,QAAQ,QAAQ,gBAAgB;AAClC,YAAM,WAAW,QAAQ,KAAK,EAAE,KAAK,EAAE;AACvC,UAAI,UAAU;AACZ,YAAI;AACF,kBAAQ,IAAI,QAAQ,GAAG,MAAM;AAAA,QAC/B,QAAQ;AAAA,QAAC;AACT,gBAAQ,OAAO,QAAQ;AAAA,MACzB;AAAA,IACF;AAEA,YAAQ,IAAI,QAAQ,EAAE;AACtB,WAAO;AAAA,EACT,SAAS,KAAU;AACjB,UAAM,IAAI;AAAA,MACR,+CAA+C,MAAM,KAAK,IAAI,OAAO;AAAA,IACvE;AAAA,EACF;AACF;AAEO,SAAS,cAAc,SAAkB,MAAM,QAAQ,IAAI,GAAsB;AACtF,QAAM,SAAS,UAAU,SAAS,GAAG;AACrC,MAAI,gBAAgB,IAAI,MAAM,GAAG;AAC/B,WAAO,gBAAgB,IAAI,MAAM;AAAA,EACnC;AAEA,MAAI,CAAI,eAAW,MAAM,GAAG;AAC1B,UAAM,SAAS,GAAG;AAAA,EACpB;AAEA,QAAM,OAAO,mBAAmB,SAAS,GAAG;AAC5C,QAAM,SAAS,kBAAkB,IAAI;AAErC,MAAI;AACF,UAAM,KAAK,IAAI,SAAS,QAAQ;AAAA,MAC9B,UAAU;AAAA,MACV,SAAS,OAAO,iBAAiB;AAAA,IACnC,CAAC;AAED,OAAG,OAAO,iBAAiB;AAC3B,OAAG,OAAO,kBAAkB,OAAO,iBAAiB,GAAI,EAAE;AAC1D,OAAG,OAAO,mBAAmB;AAC7B,OAAG,OAAO,2BAA2B;AACrC,OAAG,OAAO,qBAAqB;AAC/B,OAAG,OAAO,eAAe,OAAO,iBAAiB,SAAS,EAAE;AAC5D,OAAG,OAAO,sBAAsB;AAEhC,QAAI,gBAAgB,QAAQ,gBAAgB;AAC1C,YAAM,WAAW,gBAAgB,KAAK,EAAE,KAAK,EAAE;AAC/C,UAAI,UAAU;AACZ,YAAI;AACF,0BAAgB,IAAI,QAAQ,GAAG,MAAM;AAAA,QACvC,QAAQ;AAAA,QAAC;AACT,wBAAgB,OAAO,QAAQ;AAAA,MACjC;AAAA,IACF;AAEA,oBAAgB,IAAI,QAAQ,EAAE;AAC9B,WAAO;AAAA,EACT,SAAS,KAAU;AACjB,UAAM,IAAI;AAAA,MACR,iDAAiD,MAAM,KAAK,IAAI,OAAO;AAAA,IACzE;AAAA,EACF;AACF;AAEO,SAAS,QAAQ,SAAkB,MAAM,QAAQ,IAAI,GAAS;AACnE,QAAM,SAAS,UAAU,SAAS,GAAG;AACrC,MAAI,QAAQ,IAAI,MAAM,GAAG;AACvB,QAAI;AACF,cAAQ,IAAI,MAAM,GAAG,MAAM;AAAA,IAC7B,QAAQ;AAAA,IAAC;AACT,YAAQ,OAAO,MAAM;AAAA,EACvB;AACA,MAAI,gBAAgB,IAAI,MAAM,GAAG;AAC/B,QAAI;AACF,sBAAgB,IAAI,MAAM,GAAG,MAAM;AAAA,IACrC,QAAQ;AAAA,IAAC;AACT,oBAAgB,OAAO,MAAM;AAAA,EAC/B;AACF;AAEO,SAAS,cAAoB;AAClC,aAAW,CAAC,KAAK,EAAE,KAAK,QAAQ,QAAQ,GAAG;AACzC,QAAI;AACF,SAAG,MAAM;AAAA,IACX,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,UAAQ,MAAM;AAEd,aAAW,CAAC,KAAK,EAAE,KAAK,gBAAgB,QAAQ,GAAG;AACjD,QAAI;AACF,SAAG,MAAM;AAAA,IACX,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,kBAAgB,MAAM;AACxB;","names":["path","fs","os","path","validatePath","registry"]}
|