@frostbridge/imdl 0.1.3 → 0.1.5
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/index.js +163 -59
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1085,6 +1085,7 @@ function capitalize(s) {
|
|
|
1085
1085
|
// src/commands/init.ts
|
|
1086
1086
|
import pc2 from "picocolors";
|
|
1087
1087
|
import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
1088
|
+
import { execSync } from "child_process";
|
|
1088
1089
|
import { join as join6 } from "path";
|
|
1089
1090
|
import { homedir as homedir4 } from "os";
|
|
1090
1091
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -1351,6 +1352,7 @@ async function initCommand(options) {
|
|
|
1351
1352
|
installShellWrapper(agent.name, agent.dir);
|
|
1352
1353
|
}
|
|
1353
1354
|
}
|
|
1355
|
+
installDaemon();
|
|
1354
1356
|
let apiConnected = false;
|
|
1355
1357
|
try {
|
|
1356
1358
|
const healthRes = await fetch(`${config.apiUrl}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -1471,6 +1473,59 @@ function installShellWrapper(agentName, agentDir) {
|
|
|
1471
1473
|
console.log(pc2.dim(` \u2022 Shell wrapper: couldn't configure for ${agentName} (configure manually)`));
|
|
1472
1474
|
}
|
|
1473
1475
|
}
|
|
1476
|
+
function installDaemon() {
|
|
1477
|
+
if (process.platform !== "darwin") {
|
|
1478
|
+
console.log(pc2.dim(" \u2022 Background daemon: only auto-installed on macOS (run `imdl daemon` manually)"));
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
const plistName = "com.frostbridge.imdl-daemon.plist";
|
|
1482
|
+
const plistDir = join6(homedir4(), "Library", "LaunchAgents");
|
|
1483
|
+
const plistPath = join6(plistDir, plistName);
|
|
1484
|
+
if (existsSync7(plistPath)) {
|
|
1485
|
+
console.log(pc2.dim(" \u2022 Background daemon already installed"));
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
let imdlBin = "";
|
|
1489
|
+
try {
|
|
1490
|
+
imdlBin = execSync("which imdl", { encoding: "utf-8" }).trim();
|
|
1491
|
+
} catch {
|
|
1492
|
+
imdlBin = "/usr/local/bin/imdl";
|
|
1493
|
+
}
|
|
1494
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1495
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1496
|
+
<plist version="1.0">
|
|
1497
|
+
<dict>
|
|
1498
|
+
<key>Label</key>
|
|
1499
|
+
<string>com.frostbridge.imdl-daemon</string>
|
|
1500
|
+
<key>ProgramArguments</key>
|
|
1501
|
+
<array>
|
|
1502
|
+
<string>${imdlBin}</string>
|
|
1503
|
+
<string>daemon</string>
|
|
1504
|
+
</array>
|
|
1505
|
+
<key>RunAtLoad</key>
|
|
1506
|
+
<true/>
|
|
1507
|
+
<key>KeepAlive</key>
|
|
1508
|
+
<true/>
|
|
1509
|
+
<key>StandardOutPath</key>
|
|
1510
|
+
<string>${join6(homedir4(), ".imdl", "daemon.log")}</string>
|
|
1511
|
+
<key>StandardErrorPath</key>
|
|
1512
|
+
<string>${join6(homedir4(), ".imdl", "daemon.log")}</string>
|
|
1513
|
+
<key>EnvironmentVariables</key>
|
|
1514
|
+
<dict>
|
|
1515
|
+
<key>PATH</key>
|
|
1516
|
+
<string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
|
|
1517
|
+
</dict>
|
|
1518
|
+
</dict>
|
|
1519
|
+
</plist>`;
|
|
1520
|
+
try {
|
|
1521
|
+
if (!existsSync7(plistDir)) mkdirSync2(plistDir, { recursive: true });
|
|
1522
|
+
writeFileSync4(plistPath, plist);
|
|
1523
|
+
execSync(`launchctl load ${plistPath}`, { stdio: "ignore" });
|
|
1524
|
+
console.log(pc2.green(" \u2713 Background daemon installed (auto-starts on login)"));
|
|
1525
|
+
} catch {
|
|
1526
|
+
console.log(pc2.yellow(" \u26A0 Could not install daemon service (run `imdl daemon` manually)"));
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1474
1529
|
function installMCPProxy(mcpConfigPath, agentName) {
|
|
1475
1530
|
let existing = {};
|
|
1476
1531
|
const dir = join6(mcpConfigPath, "..");
|
|
@@ -3577,6 +3632,8 @@ async function collectCommand() {
|
|
|
3577
3632
|
|
|
3578
3633
|
// src/commands/daemon.ts
|
|
3579
3634
|
import pc7 from "picocolors";
|
|
3635
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
3636
|
+
import { join as join18 } from "path";
|
|
3580
3637
|
var INTERVAL_MS = 3e4;
|
|
3581
3638
|
async function daemonCommand() {
|
|
3582
3639
|
console.log(pc7.cyan("imdl daemon started \u2014 collecting every 30s"));
|
|
@@ -3584,9 +3641,12 @@ async function daemonCommand() {
|
|
|
3584
3641
|
const tick = async () => {
|
|
3585
3642
|
try {
|
|
3586
3643
|
const count = await collectPrompts();
|
|
3587
|
-
|
|
3588
|
-
|
|
3644
|
+
const shellCount = await ingestShellEvents();
|
|
3645
|
+
const total = count + shellCount;
|
|
3646
|
+
if (total > 0) {
|
|
3647
|
+
console.log(pc7.green(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Collected ${total} event${total !== 1 ? "s" : ""} (prompts: ${count}, shell: ${shellCount})`));
|
|
3589
3648
|
}
|
|
3649
|
+
await tryFlush();
|
|
3590
3650
|
} catch (err) {
|
|
3591
3651
|
console.error(pc7.red(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Error: ${err.message}`));
|
|
3592
3652
|
}
|
|
@@ -3594,6 +3654,50 @@ async function daemonCommand() {
|
|
|
3594
3654
|
await tick();
|
|
3595
3655
|
setInterval(tick, INTERVAL_MS);
|
|
3596
3656
|
}
|
|
3657
|
+
async function ingestShellEvents() {
|
|
3658
|
+
const shellLog = join18(getBufferDir(), "shell-events.ndjson");
|
|
3659
|
+
if (!existsSync19(shellLog)) return 0;
|
|
3660
|
+
const content = readFileSync13(shellLog, "utf-8").trim();
|
|
3661
|
+
if (!content) return 0;
|
|
3662
|
+
const config = loadConfig();
|
|
3663
|
+
const lines = content.split("\n");
|
|
3664
|
+
const events = lines.map((line) => {
|
|
3665
|
+
try {
|
|
3666
|
+
return JSON.parse(line);
|
|
3667
|
+
} catch {
|
|
3668
|
+
return null;
|
|
3669
|
+
}
|
|
3670
|
+
}).filter(Boolean);
|
|
3671
|
+
if (events.length === 0) return 0;
|
|
3672
|
+
const headers = { "Content-Type": "application/json" };
|
|
3673
|
+
if (config.authToken) headers["X-IMDL-Key"] = config.authToken;
|
|
3674
|
+
const sessionId = `shell_${config.developerId}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
3675
|
+
const apiEvents = events.map((e) => ({
|
|
3676
|
+
type: "pre_tool_use",
|
|
3677
|
+
toolName: "shell_command",
|
|
3678
|
+
toolInput: { command: e.command, agent: e.agent, decision: e.decision, reason: e.reason },
|
|
3679
|
+
timestamp: e.timestamp,
|
|
3680
|
+
violation: e.decision === "block" ? { action: "block", reason: e.reason, policyName: e.policyName } : void 0
|
|
3681
|
+
}));
|
|
3682
|
+
let sent = 0;
|
|
3683
|
+
for (let i = 0; i < apiEvents.length; i += 50) {
|
|
3684
|
+
const batch = apiEvents.slice(i, i + 50);
|
|
3685
|
+
try {
|
|
3686
|
+
const res = await fetch(`${config.apiUrl}/api/sessions/${sessionId}/events`, {
|
|
3687
|
+
method: "POST",
|
|
3688
|
+
headers,
|
|
3689
|
+
body: JSON.stringify({ events: batch, developerId: config.developerId }),
|
|
3690
|
+
signal: AbortSignal.timeout(1e4)
|
|
3691
|
+
});
|
|
3692
|
+
if (res.ok) sent += batch.length;
|
|
3693
|
+
} catch {
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
if (sent === apiEvents.length) {
|
|
3697
|
+
writeFileSync7(shellLog, "", { mode: 384 });
|
|
3698
|
+
}
|
|
3699
|
+
return sent;
|
|
3700
|
+
}
|
|
3597
3701
|
|
|
3598
3702
|
// src/commands/request.ts
|
|
3599
3703
|
import pc8 from "picocolors";
|
|
@@ -3658,8 +3762,8 @@ async function requestCommand(options) {
|
|
|
3658
3762
|
// src/commands/lock.ts
|
|
3659
3763
|
import pc9 from "picocolors";
|
|
3660
3764
|
import { createHash as createHash2 } from "crypto";
|
|
3661
|
-
import { readFileSync as
|
|
3662
|
-
import { join as
|
|
3765
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
|
|
3766
|
+
import { join as join19, resolve as resolve3 } from "path";
|
|
3663
3767
|
var LOCKFILE_NAME = "mcp-lock.json";
|
|
3664
3768
|
function computeIntegrity(server, identity) {
|
|
3665
3769
|
const payload = JSON.stringify({
|
|
@@ -3674,7 +3778,7 @@ function computeIntegrity(server, identity) {
|
|
|
3674
3778
|
}
|
|
3675
3779
|
function getLockfilePath(customPath) {
|
|
3676
3780
|
if (customPath) return resolve3(customPath);
|
|
3677
|
-
return
|
|
3781
|
+
return join19(process.cwd(), LOCKFILE_NAME);
|
|
3678
3782
|
}
|
|
3679
3783
|
async function lockCommand(options) {
|
|
3680
3784
|
const detection = await detectMCPConfigs(options.path);
|
|
@@ -3721,7 +3825,7 @@ async function lockCommand(options) {
|
|
|
3721
3825
|
servers: entries
|
|
3722
3826
|
};
|
|
3723
3827
|
const lockPath = getLockfilePath(options.output);
|
|
3724
|
-
|
|
3828
|
+
writeFileSync8(lockPath, JSON.stringify(lockfile, null, 2), { mode: 420 });
|
|
3725
3829
|
if (options.json) {
|
|
3726
3830
|
console.log(JSON.stringify(lockfile, null, 2));
|
|
3727
3831
|
} else {
|
|
@@ -3744,7 +3848,7 @@ async function lockCommand(options) {
|
|
|
3744
3848
|
}
|
|
3745
3849
|
async function verifyCommand(options) {
|
|
3746
3850
|
const lockPath = getLockfilePath(options.path);
|
|
3747
|
-
if (!
|
|
3851
|
+
if (!existsSync20(lockPath)) {
|
|
3748
3852
|
if (options.json) {
|
|
3749
3853
|
console.log(JSON.stringify({ error: "No lockfile found. Run `imdl lock` first.", path: lockPath }));
|
|
3750
3854
|
} else {
|
|
@@ -3756,7 +3860,7 @@ async function verifyCommand(options) {
|
|
|
3756
3860
|
}
|
|
3757
3861
|
let lockfile;
|
|
3758
3862
|
try {
|
|
3759
|
-
lockfile = JSON.parse(
|
|
3863
|
+
lockfile = JSON.parse(readFileSync14(lockPath, "utf-8"));
|
|
3760
3864
|
} catch {
|
|
3761
3865
|
console.log(pc9.red("Failed to parse lockfile."));
|
|
3762
3866
|
process.exit(1);
|
|
@@ -4165,8 +4269,8 @@ async function checkCompliance(developerId) {
|
|
|
4165
4269
|
}
|
|
4166
4270
|
|
|
4167
4271
|
// src/permissions/fixer.ts
|
|
4168
|
-
import { existsSync as
|
|
4169
|
-
import { join as
|
|
4272
|
+
import { existsSync as existsSync21, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
|
|
4273
|
+
import { join as join20 } from "path";
|
|
4170
4274
|
import { homedir as homedir14 } from "os";
|
|
4171
4275
|
function buildFixesFromChanges(changes) {
|
|
4172
4276
|
const fixes = [];
|
|
@@ -4180,10 +4284,10 @@ function buildFixesFromChanges(changes) {
|
|
|
4180
4284
|
function buildFixForAgent(change, home) {
|
|
4181
4285
|
const { agentType, category, action, targetGrant } = change;
|
|
4182
4286
|
if (agentType === "claude-code") {
|
|
4183
|
-
const configPath =
|
|
4184
|
-
if (!
|
|
4287
|
+
const configPath = join20(home, ".claude", "settings.json");
|
|
4288
|
+
if (!existsSync21(configPath)) return null;
|
|
4185
4289
|
try {
|
|
4186
|
-
const settings = JSON.parse(
|
|
4290
|
+
const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
4187
4291
|
const tools = settings.allowedTools || [];
|
|
4188
4292
|
if (category === "shell") {
|
|
4189
4293
|
if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
|
|
@@ -4211,10 +4315,10 @@ function buildFixForAgent(change, home) {
|
|
|
4211
4315
|
}
|
|
4212
4316
|
}
|
|
4213
4317
|
if (agentType === "cursor") {
|
|
4214
|
-
const configPath =
|
|
4215
|
-
if (!
|
|
4318
|
+
const configPath = join20(home, ".cursor", "settings.json");
|
|
4319
|
+
if (!existsSync21(configPath)) return null;
|
|
4216
4320
|
try {
|
|
4217
|
-
const settings = JSON.parse(
|
|
4321
|
+
const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
4218
4322
|
if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
|
|
4219
4323
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
|
|
4220
4324
|
}
|
|
@@ -4223,10 +4327,10 @@ function buildFixForAgent(change, home) {
|
|
|
4223
4327
|
}
|
|
4224
4328
|
}
|
|
4225
4329
|
if (agentType === "codex") {
|
|
4226
|
-
const configPath =
|
|
4227
|
-
if (!
|
|
4330
|
+
const configPath = join20(home, ".codex", "config.json");
|
|
4331
|
+
if (!existsSync21(configPath)) return null;
|
|
4228
4332
|
try {
|
|
4229
|
-
const config = JSON.parse(
|
|
4333
|
+
const config = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
4230
4334
|
if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
|
|
4231
4335
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
|
|
4232
4336
|
}
|
|
@@ -4235,10 +4339,10 @@ function buildFixForAgent(change, home) {
|
|
|
4235
4339
|
}
|
|
4236
4340
|
}
|
|
4237
4341
|
if (agentType === "copilot") {
|
|
4238
|
-
const configPath =
|
|
4239
|
-
if (!
|
|
4342
|
+
const configPath = join20(home, "Library", "Application Support", "Code", "User", "settings.json");
|
|
4343
|
+
if (!existsSync21(configPath)) return null;
|
|
4240
4344
|
try {
|
|
4241
|
-
const settings = JSON.parse(
|
|
4345
|
+
const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
4242
4346
|
if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
|
|
4243
4347
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
|
|
4244
4348
|
}
|
|
@@ -4248,10 +4352,10 @@ function buildFixForAgent(change, home) {
|
|
|
4248
4352
|
}
|
|
4249
4353
|
if (agentType === "windsurf") {
|
|
4250
4354
|
if (category === "mcp_tools") {
|
|
4251
|
-
const configPath =
|
|
4252
|
-
if (!
|
|
4355
|
+
const configPath = join20(home, ".windsurf", "mcp.json");
|
|
4356
|
+
if (!existsSync21(configPath)) return null;
|
|
4253
4357
|
try {
|
|
4254
|
-
const config = JSON.parse(
|
|
4358
|
+
const config = JSON.parse(readFileSync15(configPath, "utf-8"));
|
|
4255
4359
|
const servers = Object.keys(config.mcpServers || {});
|
|
4256
4360
|
if (servers.length === 0) return null;
|
|
4257
4361
|
return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
|
|
@@ -4264,7 +4368,7 @@ function buildFixForAgent(change, home) {
|
|
|
4264
4368
|
}
|
|
4265
4369
|
function applyFix(fix) {
|
|
4266
4370
|
try {
|
|
4267
|
-
const content =
|
|
4371
|
+
const content = readFileSync15(fix.configPath, "utf-8");
|
|
4268
4372
|
const config = JSON.parse(content);
|
|
4269
4373
|
let modified = false;
|
|
4270
4374
|
if (fix.agentType === "claude-code") {
|
|
@@ -4318,7 +4422,7 @@ function applyFix(fix) {
|
|
|
4318
4422
|
}
|
|
4319
4423
|
}
|
|
4320
4424
|
if (modified) {
|
|
4321
|
-
|
|
4425
|
+
writeFileSync9(fix.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
4322
4426
|
return { fix, applied: true };
|
|
4323
4427
|
}
|
|
4324
4428
|
return { fix, applied: false, error: "No changes needed" };
|
|
@@ -4555,13 +4659,13 @@ function grantLevel(grant) {
|
|
|
4555
4659
|
|
|
4556
4660
|
// src/commands/gateway.ts
|
|
4557
4661
|
import pc12 from "picocolors";
|
|
4558
|
-
import { existsSync as
|
|
4559
|
-
import { join as
|
|
4662
|
+
import { existsSync as existsSync22, readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
|
|
4663
|
+
import { join as join21 } from "path";
|
|
4560
4664
|
import { homedir as homedir15 } from "os";
|
|
4561
|
-
import { execSync, spawn } from "child_process";
|
|
4562
|
-
var IMDL_DIR3 =
|
|
4563
|
-
var GATEWAY_CONFIG =
|
|
4564
|
-
var GATEWAY_PID_FILE =
|
|
4665
|
+
import { execSync as execSync2, spawn } from "child_process";
|
|
4666
|
+
var IMDL_DIR3 = join21(homedir15(), ".imdl");
|
|
4667
|
+
var GATEWAY_CONFIG = join21(IMDL_DIR3, "gateway.toml");
|
|
4668
|
+
var GATEWAY_PID_FILE = join21(IMDL_DIR3, "gateway.pid");
|
|
4565
4669
|
var DEFAULT_PORT = 9443;
|
|
4566
4670
|
async function gatewayCommand(opts) {
|
|
4567
4671
|
const status = await getGatewayStatus();
|
|
@@ -4636,8 +4740,8 @@ async function gatewayStartCommand(opts) {
|
|
|
4636
4740
|
});
|
|
4637
4741
|
child.unref();
|
|
4638
4742
|
if (child.pid) {
|
|
4639
|
-
if (!
|
|
4640
|
-
|
|
4743
|
+
if (!existsSync22(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
|
|
4744
|
+
writeFileSync10(GATEWAY_PID_FILE, String(child.pid));
|
|
4641
4745
|
console.log(pc12.green(`Gateway started (PID ${child.pid}, port ${port})`));
|
|
4642
4746
|
console.log(pc12.dim(` Config: ${GATEWAY_CONFIG}`));
|
|
4643
4747
|
console.log(pc12.dim(` Set ANTHROPIC_BASE_URL=http://127.0.0.1:${port} to intercept traffic`));
|
|
@@ -4696,12 +4800,12 @@ async function gatewayDisableCommand(opts) {
|
|
|
4696
4800
|
console.log(pc12.dim("Restart gateway for changes to take effect."));
|
|
4697
4801
|
}
|
|
4698
4802
|
async function gatewayAlertsCommand(opts) {
|
|
4699
|
-
const logPath =
|
|
4700
|
-
if (!
|
|
4803
|
+
const logPath = join21(IMDL_DIR3, "gateway-alerts.jsonl");
|
|
4804
|
+
if (!existsSync22(logPath)) {
|
|
4701
4805
|
console.log(pc12.dim("No alerts logged yet."));
|
|
4702
4806
|
return;
|
|
4703
4807
|
}
|
|
4704
|
-
const lines =
|
|
4808
|
+
const lines = readFileSync16(logPath, "utf-8").split("\n").filter(Boolean);
|
|
4705
4809
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
|
|
4706
4810
|
const recent = lines.slice(-limit);
|
|
4707
4811
|
if (opts.json) {
|
|
@@ -4803,9 +4907,9 @@ async function getGatewayStatus() {
|
|
|
4803
4907
|
return { running: true, pid, port, stats, config };
|
|
4804
4908
|
}
|
|
4805
4909
|
function getRunningPid() {
|
|
4806
|
-
if (!
|
|
4910
|
+
if (!existsSync22(GATEWAY_PID_FILE)) return null;
|
|
4807
4911
|
try {
|
|
4808
|
-
const pid = parseInt(
|
|
4912
|
+
const pid = parseInt(readFileSync16(GATEWAY_PID_FILE, "utf-8").trim(), 10);
|
|
4809
4913
|
return isNaN(pid) ? null : pid;
|
|
4810
4914
|
} catch {
|
|
4811
4915
|
return null;
|
|
@@ -4820,9 +4924,9 @@ function isProcessAlive(pid) {
|
|
|
4820
4924
|
}
|
|
4821
4925
|
}
|
|
4822
4926
|
function getConfiguredPort() {
|
|
4823
|
-
if (!
|
|
4927
|
+
if (!existsSync22(GATEWAY_CONFIG)) return DEFAULT_PORT;
|
|
4824
4928
|
try {
|
|
4825
|
-
const content =
|
|
4929
|
+
const content = readFileSync16(GATEWAY_CONFIG, "utf-8");
|
|
4826
4930
|
const match = content.match(/listen_port\s*=\s*(\d+)/);
|
|
4827
4931
|
return match ? parseInt(match[1], 10) : DEFAULT_PORT;
|
|
4828
4932
|
} catch {
|
|
@@ -4831,23 +4935,23 @@ function getConfiguredPort() {
|
|
|
4831
4935
|
}
|
|
4832
4936
|
function findGatewayBinary() {
|
|
4833
4937
|
const candidates = [
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4938
|
+
join21(process.cwd(), "target", "release", "imdl-gateway"),
|
|
4939
|
+
join21(process.cwd(), "..", "ai-gateway", "target", "release", "imdl-gateway"),
|
|
4940
|
+
join21(homedir15(), ".imdl", "bin", "imdl-gateway")
|
|
4837
4941
|
];
|
|
4838
4942
|
for (const path of candidates) {
|
|
4839
|
-
if (
|
|
4943
|
+
if (existsSync22(path)) return path;
|
|
4840
4944
|
}
|
|
4841
4945
|
try {
|
|
4842
|
-
const result =
|
|
4946
|
+
const result = execSync2("which imdl-gateway", { encoding: "utf-8" }).trim();
|
|
4843
4947
|
if (result) return result;
|
|
4844
4948
|
} catch {
|
|
4845
4949
|
}
|
|
4846
4950
|
return null;
|
|
4847
4951
|
}
|
|
4848
4952
|
function ensureConfig(port) {
|
|
4849
|
-
if (
|
|
4850
|
-
if (!
|
|
4953
|
+
if (existsSync22(GATEWAY_CONFIG)) return;
|
|
4954
|
+
if (!existsSync22(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
|
|
4851
4955
|
const defaultToml = `# IMDL AI Gateway configuration
|
|
4852
4956
|
listen_host = "127.0.0.1"
|
|
4853
4957
|
listen_port = ${port}
|
|
@@ -4874,7 +4978,7 @@ openai_url = "https://api.openai.com"
|
|
|
4874
4978
|
[audit]
|
|
4875
4979
|
enabled = true
|
|
4876
4980
|
`;
|
|
4877
|
-
|
|
4981
|
+
writeFileSync10(GATEWAY_CONFIG, defaultToml, { mode: 384 });
|
|
4878
4982
|
}
|
|
4879
4983
|
function normalizeModule(input) {
|
|
4880
4984
|
const map = {
|
|
@@ -4891,13 +4995,13 @@ function normalizeModule(input) {
|
|
|
4891
4995
|
return map[input.toLowerCase()] ?? null;
|
|
4892
4996
|
}
|
|
4893
4997
|
function loadGatewayToml() {
|
|
4894
|
-
if (!
|
|
4998
|
+
if (!existsSync22(GATEWAY_CONFIG)) {
|
|
4895
4999
|
ensureConfig(DEFAULT_PORT);
|
|
4896
5000
|
}
|
|
4897
|
-
return
|
|
5001
|
+
return readFileSync16(GATEWAY_CONFIG, "utf-8");
|
|
4898
5002
|
}
|
|
4899
5003
|
function saveGatewayToml(content) {
|
|
4900
|
-
|
|
5004
|
+
writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
|
|
4901
5005
|
}
|
|
4902
5006
|
function setModuleEnabled(content, module, enabled) {
|
|
4903
5007
|
const sectionRegex = new RegExp(`(\\[${module}\\][^\\[]*?)enabled\\s*=\\s*(true|false)`, "s");
|
|
@@ -5227,18 +5331,18 @@ function ruleTypeToCategory(type) {
|
|
|
5227
5331
|
}
|
|
5228
5332
|
|
|
5229
5333
|
// src/transport/sync.ts
|
|
5230
|
-
import { readFileSync as
|
|
5231
|
-
import { join as
|
|
5334
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
|
|
5335
|
+
import { join as join22 } from "path";
|
|
5232
5336
|
var SYNC_INTERVAL = 6e4;
|
|
5233
5337
|
var SYNC_TIMEOUT = 3e3;
|
|
5234
5338
|
function getSyncStateFile() {
|
|
5235
|
-
return
|
|
5339
|
+
return join22(getImdlDir(), "last_sync.json");
|
|
5236
5340
|
}
|
|
5237
5341
|
function getLastSyncTime() {
|
|
5238
5342
|
try {
|
|
5239
5343
|
const file = getSyncStateFile();
|
|
5240
|
-
if (
|
|
5241
|
-
const data = JSON.parse(
|
|
5344
|
+
if (existsSync23(file)) {
|
|
5345
|
+
const data = JSON.parse(readFileSync17(file, "utf-8"));
|
|
5242
5346
|
return data.lastSync || 0;
|
|
5243
5347
|
}
|
|
5244
5348
|
} catch {
|
|
@@ -5247,7 +5351,7 @@ function getLastSyncTime() {
|
|
|
5247
5351
|
}
|
|
5248
5352
|
function setLastSyncTime() {
|
|
5249
5353
|
try {
|
|
5250
|
-
|
|
5354
|
+
writeFileSync11(getSyncStateFile(), JSON.stringify({ lastSync: Date.now() }), { mode: 384 });
|
|
5251
5355
|
} catch {
|
|
5252
5356
|
}
|
|
5253
5357
|
}
|