@frostbridge/imdl 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +166 -59
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1351,6 +1351,7 @@ async function initCommand(options) {
1351
1351
  installShellWrapper(agent.name, agent.dir);
1352
1352
  }
1353
1353
  }
1354
+ installDaemon();
1354
1355
  let apiConnected = false;
1355
1356
  try {
1356
1357
  const healthRes = await fetch(`${config.apiUrl}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -1471,6 +1472,60 @@ function installShellWrapper(agentName, agentDir) {
1471
1472
  console.log(pc2.dim(` \u2022 Shell wrapper: couldn't configure for ${agentName} (configure manually)`));
1472
1473
  }
1473
1474
  }
1475
+ function installDaemon() {
1476
+ if (process.platform !== "darwin") {
1477
+ console.log(pc2.dim(" \u2022 Background daemon: only auto-installed on macOS (run `imdl daemon` manually)"));
1478
+ return;
1479
+ }
1480
+ const plistName = "com.frostbridge.imdl-daemon.plist";
1481
+ const plistDir = join6(homedir4(), "Library", "LaunchAgents");
1482
+ const plistPath = join6(plistDir, plistName);
1483
+ if (existsSync7(plistPath)) {
1484
+ console.log(pc2.dim(" \u2022 Background daemon already installed"));
1485
+ return;
1486
+ }
1487
+ const { execSync: execSync2 } = __require("child_process");
1488
+ let imdlBin = "";
1489
+ try {
1490
+ imdlBin = execSync2("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
+ execSync2(`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, "..");
@@ -1508,7 +1563,8 @@ import pc3 from "picocolors";
1508
1563
  import { createServer } from "http";
1509
1564
  import { URL as URL2 } from "url";
1510
1565
  import { randomBytes } from "crypto";
1511
- var DASHBOARD_URL = process.env.IMDL_DASHBOARD_URL || "http://localhost:5173";
1566
+ var DEFAULT_DASHBOARD_URL = "https://imdl-dashboard-1028078600113.us-central1.run.app";
1567
+ var DASHBOARD_URL = process.env.IMDL_DASHBOARD_URL || DEFAULT_DASHBOARD_URL;
1512
1568
  async function loginCommand() {
1513
1569
  console.log("");
1514
1570
  console.log(pc3.bold(pc3.cyan("IMDL Login")));
@@ -1542,6 +1598,7 @@ async function loginCommand() {
1542
1598
  config.authToken = result.token;
1543
1599
  config.teamId = result.teamId;
1544
1600
  if (result.teamName) config.teamName = result.teamName;
1601
+ if (result.developerId) config.developerId = result.developerId;
1545
1602
  saveConfig(config);
1546
1603
  console.log(pc3.green(" \u2713 Authenticated successfully!"));
1547
1604
  console.log("");
@@ -1573,6 +1630,7 @@ function waitForCallback(port, expectedState) {
1573
1630
  const token = url.searchParams.get("token");
1574
1631
  const teamId = url.searchParams.get("teamId");
1575
1632
  const teamName = url.searchParams.get("teamName");
1633
+ const developerId = url.searchParams.get("developerId");
1576
1634
  const returnedState = url.searchParams.get("state");
1577
1635
  const error = url.searchParams.get("error");
1578
1636
  res.setHeader("Access-Control-Allow-Origin", DASHBOARD_URL);
@@ -1598,7 +1656,7 @@ function waitForCallback(port, expectedState) {
1598
1656
  res.end("<html><body><h2>Authenticated!</h2><p>You can close this tab and return to your terminal.</p></body></html>");
1599
1657
  clearTimeout(timeout);
1600
1658
  server.close();
1601
- resolve4({ token, teamId, teamName: teamName || void 0 });
1659
+ resolve4({ token, teamId, teamName: teamName || void 0, developerId: developerId || void 0 });
1602
1660
  });
1603
1661
  server.listen(port);
1604
1662
  });
@@ -3574,6 +3632,8 @@ async function collectCommand() {
3574
3632
 
3575
3633
  // src/commands/daemon.ts
3576
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";
3577
3637
  var INTERVAL_MS = 3e4;
3578
3638
  async function daemonCommand() {
3579
3639
  console.log(pc7.cyan("imdl daemon started \u2014 collecting every 30s"));
@@ -3581,9 +3641,12 @@ async function daemonCommand() {
3581
3641
  const tick = async () => {
3582
3642
  try {
3583
3643
  const count = await collectPrompts();
3584
- if (count > 0) {
3585
- console.log(pc7.green(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Collected ${count} new event${count !== 1 ? "s" : ""}`));
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})`));
3586
3648
  }
3649
+ await tryFlush();
3587
3650
  } catch (err) {
3588
3651
  console.error(pc7.red(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Error: ${err.message}`));
3589
3652
  }
@@ -3591,6 +3654,50 @@ async function daemonCommand() {
3591
3654
  await tick();
3592
3655
  setInterval(tick, INTERVAL_MS);
3593
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
+ }
3594
3701
 
3595
3702
  // src/commands/request.ts
3596
3703
  import pc8 from "picocolors";
@@ -3655,8 +3762,8 @@ async function requestCommand(options) {
3655
3762
  // src/commands/lock.ts
3656
3763
  import pc9 from "picocolors";
3657
3764
  import { createHash as createHash2 } from "crypto";
3658
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync19 } from "fs";
3659
- import { join as join18, resolve as resolve3 } from "path";
3765
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
3766
+ import { join as join19, resolve as resolve3 } from "path";
3660
3767
  var LOCKFILE_NAME = "mcp-lock.json";
3661
3768
  function computeIntegrity(server, identity) {
3662
3769
  const payload = JSON.stringify({
@@ -3671,7 +3778,7 @@ function computeIntegrity(server, identity) {
3671
3778
  }
3672
3779
  function getLockfilePath(customPath) {
3673
3780
  if (customPath) return resolve3(customPath);
3674
- return join18(process.cwd(), LOCKFILE_NAME);
3781
+ return join19(process.cwd(), LOCKFILE_NAME);
3675
3782
  }
3676
3783
  async function lockCommand(options) {
3677
3784
  const detection = await detectMCPConfigs(options.path);
@@ -3718,7 +3825,7 @@ async function lockCommand(options) {
3718
3825
  servers: entries
3719
3826
  };
3720
3827
  const lockPath = getLockfilePath(options.output);
3721
- writeFileSync7(lockPath, JSON.stringify(lockfile, null, 2), { mode: 420 });
3828
+ writeFileSync8(lockPath, JSON.stringify(lockfile, null, 2), { mode: 420 });
3722
3829
  if (options.json) {
3723
3830
  console.log(JSON.stringify(lockfile, null, 2));
3724
3831
  } else {
@@ -3741,7 +3848,7 @@ async function lockCommand(options) {
3741
3848
  }
3742
3849
  async function verifyCommand(options) {
3743
3850
  const lockPath = getLockfilePath(options.path);
3744
- if (!existsSync19(lockPath)) {
3851
+ if (!existsSync20(lockPath)) {
3745
3852
  if (options.json) {
3746
3853
  console.log(JSON.stringify({ error: "No lockfile found. Run `imdl lock` first.", path: lockPath }));
3747
3854
  } else {
@@ -3753,7 +3860,7 @@ async function verifyCommand(options) {
3753
3860
  }
3754
3861
  let lockfile;
3755
3862
  try {
3756
- lockfile = JSON.parse(readFileSync13(lockPath, "utf-8"));
3863
+ lockfile = JSON.parse(readFileSync14(lockPath, "utf-8"));
3757
3864
  } catch {
3758
3865
  console.log(pc9.red("Failed to parse lockfile."));
3759
3866
  process.exit(1);
@@ -4162,8 +4269,8 @@ async function checkCompliance(developerId) {
4162
4269
  }
4163
4270
 
4164
4271
  // src/permissions/fixer.ts
4165
- import { existsSync as existsSync20, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
4166
- import { join as join19 } from "path";
4272
+ import { existsSync as existsSync21, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
4273
+ import { join as join20 } from "path";
4167
4274
  import { homedir as homedir14 } from "os";
4168
4275
  function buildFixesFromChanges(changes) {
4169
4276
  const fixes = [];
@@ -4177,10 +4284,10 @@ function buildFixesFromChanges(changes) {
4177
4284
  function buildFixForAgent(change, home) {
4178
4285
  const { agentType, category, action, targetGrant } = change;
4179
4286
  if (agentType === "claude-code") {
4180
- const configPath = join19(home, ".claude", "settings.json");
4181
- if (!existsSync20(configPath)) return null;
4287
+ const configPath = join20(home, ".claude", "settings.json");
4288
+ if (!existsSync21(configPath)) return null;
4182
4289
  try {
4183
- const settings = JSON.parse(readFileSync14(configPath, "utf-8"));
4290
+ const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4184
4291
  const tools = settings.allowedTools || [];
4185
4292
  if (category === "shell") {
4186
4293
  if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
@@ -4208,10 +4315,10 @@ function buildFixForAgent(change, home) {
4208
4315
  }
4209
4316
  }
4210
4317
  if (agentType === "cursor") {
4211
- const configPath = join19(home, ".cursor", "settings.json");
4212
- if (!existsSync20(configPath)) return null;
4318
+ const configPath = join20(home, ".cursor", "settings.json");
4319
+ if (!existsSync21(configPath)) return null;
4213
4320
  try {
4214
- const settings = JSON.parse(readFileSync14(configPath, "utf-8"));
4321
+ const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4215
4322
  if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
4216
4323
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
4217
4324
  }
@@ -4220,10 +4327,10 @@ function buildFixForAgent(change, home) {
4220
4327
  }
4221
4328
  }
4222
4329
  if (agentType === "codex") {
4223
- const configPath = join19(home, ".codex", "config.json");
4224
- if (!existsSync20(configPath)) return null;
4330
+ const configPath = join20(home, ".codex", "config.json");
4331
+ if (!existsSync21(configPath)) return null;
4225
4332
  try {
4226
- const config = JSON.parse(readFileSync14(configPath, "utf-8"));
4333
+ const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4227
4334
  if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
4228
4335
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
4229
4336
  }
@@ -4232,10 +4339,10 @@ function buildFixForAgent(change, home) {
4232
4339
  }
4233
4340
  }
4234
4341
  if (agentType === "copilot") {
4235
- const configPath = join19(home, "Library", "Application Support", "Code", "User", "settings.json");
4236
- if (!existsSync20(configPath)) return null;
4342
+ const configPath = join20(home, "Library", "Application Support", "Code", "User", "settings.json");
4343
+ if (!existsSync21(configPath)) return null;
4237
4344
  try {
4238
- const settings = JSON.parse(readFileSync14(configPath, "utf-8"));
4345
+ const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4239
4346
  if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
4240
4347
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
4241
4348
  }
@@ -4245,10 +4352,10 @@ function buildFixForAgent(change, home) {
4245
4352
  }
4246
4353
  if (agentType === "windsurf") {
4247
4354
  if (category === "mcp_tools") {
4248
- const configPath = join19(home, ".windsurf", "mcp.json");
4249
- if (!existsSync20(configPath)) return null;
4355
+ const configPath = join20(home, ".windsurf", "mcp.json");
4356
+ if (!existsSync21(configPath)) return null;
4250
4357
  try {
4251
- const config = JSON.parse(readFileSync14(configPath, "utf-8"));
4358
+ const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4252
4359
  const servers = Object.keys(config.mcpServers || {});
4253
4360
  if (servers.length === 0) return null;
4254
4361
  return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
@@ -4261,7 +4368,7 @@ function buildFixForAgent(change, home) {
4261
4368
  }
4262
4369
  function applyFix(fix) {
4263
4370
  try {
4264
- const content = readFileSync14(fix.configPath, "utf-8");
4371
+ const content = readFileSync15(fix.configPath, "utf-8");
4265
4372
  const config = JSON.parse(content);
4266
4373
  let modified = false;
4267
4374
  if (fix.agentType === "claude-code") {
@@ -4315,7 +4422,7 @@ function applyFix(fix) {
4315
4422
  }
4316
4423
  }
4317
4424
  if (modified) {
4318
- writeFileSync8(fix.configPath, JSON.stringify(config, null, 2) + "\n");
4425
+ writeFileSync9(fix.configPath, JSON.stringify(config, null, 2) + "\n");
4319
4426
  return { fix, applied: true };
4320
4427
  }
4321
4428
  return { fix, applied: false, error: "No changes needed" };
@@ -4552,13 +4659,13 @@ function grantLevel(grant) {
4552
4659
 
4553
4660
  // src/commands/gateway.ts
4554
4661
  import pc12 from "picocolors";
4555
- import { existsSync as existsSync21, readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync3 } from "fs";
4556
- import { join as join20 } from "path";
4662
+ import { existsSync as existsSync22, readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4663
+ import { join as join21 } from "path";
4557
4664
  import { homedir as homedir15 } from "os";
4558
4665
  import { execSync, spawn } from "child_process";
4559
- var IMDL_DIR3 = join20(homedir15(), ".imdl");
4560
- var GATEWAY_CONFIG = join20(IMDL_DIR3, "gateway.toml");
4561
- var GATEWAY_PID_FILE = join20(IMDL_DIR3, "gateway.pid");
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");
4562
4669
  var DEFAULT_PORT = 9443;
4563
4670
  async function gatewayCommand(opts) {
4564
4671
  const status = await getGatewayStatus();
@@ -4633,8 +4740,8 @@ async function gatewayStartCommand(opts) {
4633
4740
  });
4634
4741
  child.unref();
4635
4742
  if (child.pid) {
4636
- if (!existsSync21(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
4637
- writeFileSync9(GATEWAY_PID_FILE, String(child.pid));
4743
+ if (!existsSync22(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
4744
+ writeFileSync10(GATEWAY_PID_FILE, String(child.pid));
4638
4745
  console.log(pc12.green(`Gateway started (PID ${child.pid}, port ${port})`));
4639
4746
  console.log(pc12.dim(` Config: ${GATEWAY_CONFIG}`));
4640
4747
  console.log(pc12.dim(` Set ANTHROPIC_BASE_URL=http://127.0.0.1:${port} to intercept traffic`));
@@ -4693,12 +4800,12 @@ async function gatewayDisableCommand(opts) {
4693
4800
  console.log(pc12.dim("Restart gateway for changes to take effect."));
4694
4801
  }
4695
4802
  async function gatewayAlertsCommand(opts) {
4696
- const logPath = join20(IMDL_DIR3, "gateway-alerts.jsonl");
4697
- if (!existsSync21(logPath)) {
4803
+ const logPath = join21(IMDL_DIR3, "gateway-alerts.jsonl");
4804
+ if (!existsSync22(logPath)) {
4698
4805
  console.log(pc12.dim("No alerts logged yet."));
4699
4806
  return;
4700
4807
  }
4701
- const lines = readFileSync15(logPath, "utf-8").split("\n").filter(Boolean);
4808
+ const lines = readFileSync16(logPath, "utf-8").split("\n").filter(Boolean);
4702
4809
  const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
4703
4810
  const recent = lines.slice(-limit);
4704
4811
  if (opts.json) {
@@ -4800,9 +4907,9 @@ async function getGatewayStatus() {
4800
4907
  return { running: true, pid, port, stats, config };
4801
4908
  }
4802
4909
  function getRunningPid() {
4803
- if (!existsSync21(GATEWAY_PID_FILE)) return null;
4910
+ if (!existsSync22(GATEWAY_PID_FILE)) return null;
4804
4911
  try {
4805
- const pid = parseInt(readFileSync15(GATEWAY_PID_FILE, "utf-8").trim(), 10);
4912
+ const pid = parseInt(readFileSync16(GATEWAY_PID_FILE, "utf-8").trim(), 10);
4806
4913
  return isNaN(pid) ? null : pid;
4807
4914
  } catch {
4808
4915
  return null;
@@ -4817,9 +4924,9 @@ function isProcessAlive(pid) {
4817
4924
  }
4818
4925
  }
4819
4926
  function getConfiguredPort() {
4820
- if (!existsSync21(GATEWAY_CONFIG)) return DEFAULT_PORT;
4927
+ if (!existsSync22(GATEWAY_CONFIG)) return DEFAULT_PORT;
4821
4928
  try {
4822
- const content = readFileSync15(GATEWAY_CONFIG, "utf-8");
4929
+ const content = readFileSync16(GATEWAY_CONFIG, "utf-8");
4823
4930
  const match = content.match(/listen_port\s*=\s*(\d+)/);
4824
4931
  return match ? parseInt(match[1], 10) : DEFAULT_PORT;
4825
4932
  } catch {
@@ -4828,12 +4935,12 @@ function getConfiguredPort() {
4828
4935
  }
4829
4936
  function findGatewayBinary() {
4830
4937
  const candidates = [
4831
- join20(process.cwd(), "target", "release", "imdl-gateway"),
4832
- join20(process.cwd(), "..", "ai-gateway", "target", "release", "imdl-gateway"),
4833
- join20(homedir15(), ".imdl", "bin", "imdl-gateway")
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")
4834
4941
  ];
4835
4942
  for (const path of candidates) {
4836
- if (existsSync21(path)) return path;
4943
+ if (existsSync22(path)) return path;
4837
4944
  }
4838
4945
  try {
4839
4946
  const result = execSync("which imdl-gateway", { encoding: "utf-8" }).trim();
@@ -4843,8 +4950,8 @@ function findGatewayBinary() {
4843
4950
  return null;
4844
4951
  }
4845
4952
  function ensureConfig(port) {
4846
- if (existsSync21(GATEWAY_CONFIG)) return;
4847
- if (!existsSync21(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
4953
+ if (existsSync22(GATEWAY_CONFIG)) return;
4954
+ if (!existsSync22(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
4848
4955
  const defaultToml = `# IMDL AI Gateway configuration
4849
4956
  listen_host = "127.0.0.1"
4850
4957
  listen_port = ${port}
@@ -4871,7 +4978,7 @@ openai_url = "https://api.openai.com"
4871
4978
  [audit]
4872
4979
  enabled = true
4873
4980
  `;
4874
- writeFileSync9(GATEWAY_CONFIG, defaultToml, { mode: 384 });
4981
+ writeFileSync10(GATEWAY_CONFIG, defaultToml, { mode: 384 });
4875
4982
  }
4876
4983
  function normalizeModule(input) {
4877
4984
  const map = {
@@ -4888,13 +4995,13 @@ function normalizeModule(input) {
4888
4995
  return map[input.toLowerCase()] ?? null;
4889
4996
  }
4890
4997
  function loadGatewayToml() {
4891
- if (!existsSync21(GATEWAY_CONFIG)) {
4998
+ if (!existsSync22(GATEWAY_CONFIG)) {
4892
4999
  ensureConfig(DEFAULT_PORT);
4893
5000
  }
4894
- return readFileSync15(GATEWAY_CONFIG, "utf-8");
5001
+ return readFileSync16(GATEWAY_CONFIG, "utf-8");
4895
5002
  }
4896
5003
  function saveGatewayToml(content) {
4897
- writeFileSync9(GATEWAY_CONFIG, content, { mode: 384 });
5004
+ writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
4898
5005
  }
4899
5006
  function setModuleEnabled(content, module, enabled) {
4900
5007
  const sectionRegex = new RegExp(`(\\[${module}\\][^\\[]*?)enabled\\s*=\\s*(true|false)`, "s");
@@ -5224,18 +5331,18 @@ function ruleTypeToCategory(type) {
5224
5331
  }
5225
5332
 
5226
5333
  // src/transport/sync.ts
5227
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync22 } from "fs";
5228
- import { join as join21 } from "path";
5334
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5335
+ import { join as join22 } from "path";
5229
5336
  var SYNC_INTERVAL = 6e4;
5230
5337
  var SYNC_TIMEOUT = 3e3;
5231
5338
  function getSyncStateFile() {
5232
- return join21(getImdlDir(), "last_sync.json");
5339
+ return join22(getImdlDir(), "last_sync.json");
5233
5340
  }
5234
5341
  function getLastSyncTime() {
5235
5342
  try {
5236
5343
  const file = getSyncStateFile();
5237
- if (existsSync22(file)) {
5238
- const data = JSON.parse(readFileSync16(file, "utf-8"));
5344
+ if (existsSync23(file)) {
5345
+ const data = JSON.parse(readFileSync17(file, "utf-8"));
5239
5346
  return data.lastSync || 0;
5240
5347
  }
5241
5348
  } catch {
@@ -5244,7 +5351,7 @@ function getLastSyncTime() {
5244
5351
  }
5245
5352
  function setLastSyncTime() {
5246
5353
  try {
5247
- writeFileSync10(getSyncStateFile(), JSON.stringify({ lastSync: Date.now() }), { mode: 384 });
5354
+ writeFileSync11(getSyncStateFile(), JSON.stringify({ lastSync: Date.now() }), { mode: 384 });
5248
5355
  } catch {
5249
5356
  }
5250
5357
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "restricted"
5
5
  },
6
- "version": "0.1.2",
6
+ "version": "0.1.4",
7
7
  "description": "IMDL — Intelligent Mediation & Detection Layer. AI agent security CLI.",
8
8
  "files": [
9
9
  "dist"