@frostbridge/imdl 0.1.7 → 0.1.9

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 +149 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2688,7 +2688,7 @@ function resumeCommand() {
2688
2688
  import pc6 from "picocolors";
2689
2689
 
2690
2690
  // src/adapters/index.ts
2691
- import { existsSync as existsSync18, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
2691
+ import { existsSync as existsSync18, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
2692
2692
  import { join as join17 } from "path";
2693
2693
 
2694
2694
  // src/transport/buffer.ts
@@ -3197,7 +3197,7 @@ var ClaudeCodeAdapter = class {
3197
3197
  };
3198
3198
 
3199
3199
  // src/adapters/cursor.ts
3200
- import { existsSync as existsSync17, readdirSync as readdirSync4, statSync as statSync9 } from "fs";
3200
+ import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
3201
3201
  import { join as join16 } from "path";
3202
3202
  import { homedir as homedir13 } from "os";
3203
3203
  import { createRequire } from "module";
@@ -3579,6 +3579,97 @@ var CursorAdapter = class _CursorAdapter {
3579
3579
  }
3580
3580
  return `cursor-${parts[parts.length - 2] || "unknown"}`;
3581
3581
  }
3582
+ /**
3583
+ * Scan Cursor's agent-transcripts JSONL files (real-time, like Claude Code).
3584
+ * Path: ~/.cursor/projects/{project}/agent-transcripts/{session}/{session}.jsonl
3585
+ * Uses offsets to only read new lines since last scan.
3586
+ */
3587
+ extractFromTranscripts(since, offsets) {
3588
+ const projectsDir = join16(homedir13(), ".cursor", "projects");
3589
+ if (!existsSync17(projectsDir)) return { events: [], newOffsets: offsets };
3590
+ const events = [];
3591
+ const newOffsets = { ...offsets };
3592
+ try {
3593
+ const projects = readdirSync4(projectsDir);
3594
+ for (const proj of projects) {
3595
+ const transcriptsDir = join16(projectsDir, proj, "agent-transcripts");
3596
+ if (!existsSync17(transcriptsDir)) continue;
3597
+ let sessionDirs;
3598
+ try {
3599
+ sessionDirs = readdirSync4(transcriptsDir);
3600
+ } catch {
3601
+ continue;
3602
+ }
3603
+ for (const sessionDir of sessionDirs) {
3604
+ const jsonlPath = join16(transcriptsDir, sessionDir, `${sessionDir}.jsonl`);
3605
+ if (!existsSync17(jsonlPath)) continue;
3606
+ if (since) {
3607
+ try {
3608
+ const stat = statSync9(jsonlPath);
3609
+ if (stat.mtimeMs < since.getTime()) continue;
3610
+ } catch {
3611
+ continue;
3612
+ }
3613
+ }
3614
+ try {
3615
+ const content = readFileSync12(jsonlPath, "utf-8");
3616
+ const lines = content.split("\n").filter((l) => l.trim());
3617
+ const lastOffset = offsets[jsonlPath] || 0;
3618
+ if (lines.length <= lastOffset) continue;
3619
+ const newLines = lines.slice(lastOffset);
3620
+ newOffsets[jsonlPath] = lines.length;
3621
+ const projectPath = "/" + proj.replace(/-/g, "/").replace(/^\//, "");
3622
+ const sid = `cursor-${sessionDir.slice(0, 8)}`;
3623
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3624
+ for (const line of newLines) {
3625
+ let entry;
3626
+ try {
3627
+ entry = JSON.parse(line);
3628
+ } catch {
3629
+ continue;
3630
+ }
3631
+ const content2 = entry.message?.content;
3632
+ if (!Array.isArray(content2)) continue;
3633
+ if (entry.role === "user") {
3634
+ let text = "";
3635
+ for (const block of content2) {
3636
+ if (block?.type === "text" && block.text) {
3637
+ text += block.text.replace(/<user_query>\n?/g, "").replace(/\n?<\/user_query>/g, "").trim();
3638
+ }
3639
+ }
3640
+ if (!text || this.isNoisePrompt(text)) continue;
3641
+ events.push({
3642
+ sessionId: sid,
3643
+ type: "user_prompt",
3644
+ prompt: text.slice(0, 1e4),
3645
+ timestamp: now,
3646
+ cwd: projectPath,
3647
+ agentType: "cursor"
3648
+ });
3649
+ } else if (entry.role === "assistant") {
3650
+ for (const block of content2) {
3651
+ if (block?.type === "tool_use" && block.name) {
3652
+ events.push({
3653
+ sessionId: sid,
3654
+ type: "pre_tool_use",
3655
+ toolName: block.name,
3656
+ toolInput: block.input || void 0,
3657
+ timestamp: now,
3658
+ cwd: projectPath,
3659
+ agentType: "cursor"
3660
+ });
3661
+ }
3662
+ }
3663
+ }
3664
+ }
3665
+ } catch {
3666
+ }
3667
+ }
3668
+ }
3669
+ } catch {
3670
+ }
3671
+ return { events, newOffsets };
3672
+ }
3582
3673
  /**
3583
3674
  * Returns the workspace storage path for Cursor, or null if not found.
3584
3675
  */
@@ -3610,6 +3701,13 @@ var CursorAdapter = class _CursorAdapter {
3610
3701
  return [];
3611
3702
  }
3612
3703
  }
3704
+ /**
3705
+ * Returns the Cursor projects directory for transcript scanning.
3706
+ */
3707
+ static getProjectsDir() {
3708
+ const dir = join16(homedir13(), ".cursor", "projects");
3709
+ return existsSync17(dir) ? dir : null;
3710
+ }
3613
3711
  };
3614
3712
 
3615
3713
  // src/adapters/index.ts
@@ -3617,7 +3715,7 @@ var STATE_FILE = join17(getImdlDir(), "adapter-state.json");
3617
3715
  function loadState() {
3618
3716
  try {
3619
3717
  if (existsSync18(STATE_FILE)) {
3620
- return JSON.parse(readFileSync12(STATE_FILE, "utf-8"));
3718
+ return JSON.parse(readFileSync13(STATE_FILE, "utf-8"));
3621
3719
  }
3622
3720
  } catch {
3623
3721
  }
@@ -3698,9 +3796,38 @@ async function collectPrompts() {
3698
3796
  }
3699
3797
  }
3700
3798
  }
3799
+ const cursorAdapter = new CursorAdapter();
3800
+ if (CursorAdapter.getProjectsDir()) {
3801
+ try {
3802
+ const { events: transcriptEvents, newOffsets } = cursorAdapter.extractFromTranscripts(new Date(state.lastRun), state.offsets);
3803
+ state.offsets = newOffsets;
3804
+ for (const event of transcriptEvents) {
3805
+ let violation;
3806
+ if (event.prompt) {
3807
+ const scan = scanPromptForSecrets(event.prompt);
3808
+ if (scan.hasSecrets) {
3809
+ violation = createPromptViolation(scan.findings);
3810
+ }
3811
+ }
3812
+ const buffered = {
3813
+ sessionId: event.sessionId,
3814
+ type: event.type,
3815
+ toolName: event.toolName,
3816
+ toolInput: event.toolInput ? redactObject(event.toolInput) : void 0,
3817
+ prompt: event.prompt ? redactObject(event.prompt) : void 0,
3818
+ timestamp: event.timestamp,
3819
+ cwd: event.cwd,
3820
+ agentType: "cursor",
3821
+ violation
3822
+ };
3823
+ appendEvent(buffered);
3824
+ collected++;
3825
+ }
3826
+ } catch {
3827
+ }
3828
+ }
3701
3829
  const cursorWorkspaces = CursorAdapter.getAllWorkspaces();
3702
3830
  if (cursorWorkspaces.length > 0) {
3703
- const cursorAdapter = new CursorAdapter();
3704
3831
  for (const ws of cursorWorkspaces) {
3705
3832
  try {
3706
3833
  const events = await cursorAdapter.extractEvents({
@@ -3758,7 +3885,7 @@ async function collectCommand() {
3758
3885
 
3759
3886
  // src/commands/daemon.ts
3760
3887
  import pc7 from "picocolors";
3761
- import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
3888
+ import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
3762
3889
  import { join as join18 } from "path";
3763
3890
  var INTERVAL_MS = 3e4;
3764
3891
  async function daemonCommand() {
@@ -3783,7 +3910,7 @@ async function daemonCommand() {
3783
3910
  async function ingestShellEvents() {
3784
3911
  const shellLog = join18(getBufferDir(), "shell-events.ndjson");
3785
3912
  if (!existsSync19(shellLog)) return 0;
3786
- const content = readFileSync13(shellLog, "utf-8").trim();
3913
+ const content = readFileSync14(shellLog, "utf-8").trim();
3787
3914
  if (!content) return 0;
3788
3915
  const config = loadConfig();
3789
3916
  const lines = content.split("\n");
@@ -3888,7 +4015,7 @@ async function requestCommand(options) {
3888
4015
  // src/commands/lock.ts
3889
4016
  import pc9 from "picocolors";
3890
4017
  import { createHash as createHash2 } from "crypto";
3891
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
4018
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
3892
4019
  import { join as join19, resolve as resolve3 } from "path";
3893
4020
  var LOCKFILE_NAME = "mcp-lock.json";
3894
4021
  function computeIntegrity(server, identity) {
@@ -3986,7 +4113,7 @@ async function verifyCommand(options) {
3986
4113
  }
3987
4114
  let lockfile;
3988
4115
  try {
3989
- lockfile = JSON.parse(readFileSync14(lockPath, "utf-8"));
4116
+ lockfile = JSON.parse(readFileSync15(lockPath, "utf-8"));
3990
4117
  } catch {
3991
4118
  console.log(pc9.red("Failed to parse lockfile."));
3992
4119
  process.exit(1);
@@ -4395,7 +4522,7 @@ async function checkCompliance(developerId) {
4395
4522
  }
4396
4523
 
4397
4524
  // src/permissions/fixer.ts
4398
- import { existsSync as existsSync21, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
4525
+ import { existsSync as existsSync21, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
4399
4526
  import { join as join20 } from "path";
4400
4527
  import { homedir as homedir14 } from "os";
4401
4528
  function buildFixesFromChanges(changes) {
@@ -4413,7 +4540,7 @@ function buildFixForAgent(change, home) {
4413
4540
  const configPath = join20(home, ".claude", "settings.json");
4414
4541
  if (!existsSync21(configPath)) return null;
4415
4542
  try {
4416
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4543
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4417
4544
  const tools = settings.allowedTools || [];
4418
4545
  if (category === "shell") {
4419
4546
  if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
@@ -4444,7 +4571,7 @@ function buildFixForAgent(change, home) {
4444
4571
  const configPath = join20(home, ".cursor", "settings.json");
4445
4572
  if (!existsSync21(configPath)) return null;
4446
4573
  try {
4447
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4574
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4448
4575
  if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
4449
4576
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
4450
4577
  }
@@ -4456,7 +4583,7 @@ function buildFixForAgent(change, home) {
4456
4583
  const configPath = join20(home, ".codex", "config.json");
4457
4584
  if (!existsSync21(configPath)) return null;
4458
4585
  try {
4459
- const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4586
+ const config = JSON.parse(readFileSync16(configPath, "utf-8"));
4460
4587
  if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
4461
4588
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
4462
4589
  }
@@ -4468,7 +4595,7 @@ function buildFixForAgent(change, home) {
4468
4595
  const configPath = join20(home, "Library", "Application Support", "Code", "User", "settings.json");
4469
4596
  if (!existsSync21(configPath)) return null;
4470
4597
  try {
4471
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4598
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4472
4599
  if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
4473
4600
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
4474
4601
  }
@@ -4481,7 +4608,7 @@ function buildFixForAgent(change, home) {
4481
4608
  const configPath = join20(home, ".windsurf", "mcp.json");
4482
4609
  if (!existsSync21(configPath)) return null;
4483
4610
  try {
4484
- const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4611
+ const config = JSON.parse(readFileSync16(configPath, "utf-8"));
4485
4612
  const servers = Object.keys(config.mcpServers || {});
4486
4613
  if (servers.length === 0) return null;
4487
4614
  return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
@@ -4494,7 +4621,7 @@ function buildFixForAgent(change, home) {
4494
4621
  }
4495
4622
  function applyFix(fix) {
4496
4623
  try {
4497
- const content = readFileSync15(fix.configPath, "utf-8");
4624
+ const content = readFileSync16(fix.configPath, "utf-8");
4498
4625
  const config = JSON.parse(content);
4499
4626
  let modified = false;
4500
4627
  if (fix.agentType === "claude-code") {
@@ -4785,7 +4912,7 @@ function grantLevel(grant) {
4785
4912
 
4786
4913
  // src/commands/gateway.ts
4787
4914
  import pc12 from "picocolors";
4788
- import { existsSync as existsSync22, readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4915
+ import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4789
4916
  import { join as join21 } from "path";
4790
4917
  import { homedir as homedir15 } from "os";
4791
4918
  import { execSync as execSync2, spawn } from "child_process";
@@ -4931,7 +5058,7 @@ async function gatewayAlertsCommand(opts) {
4931
5058
  console.log(pc12.dim("No alerts logged yet."));
4932
5059
  return;
4933
5060
  }
4934
- const lines = readFileSync16(logPath, "utf-8").split("\n").filter(Boolean);
5061
+ const lines = readFileSync17(logPath, "utf-8").split("\n").filter(Boolean);
4935
5062
  const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
4936
5063
  const recent = lines.slice(-limit);
4937
5064
  if (opts.json) {
@@ -5035,7 +5162,7 @@ async function getGatewayStatus() {
5035
5162
  function getRunningPid() {
5036
5163
  if (!existsSync22(GATEWAY_PID_FILE)) return null;
5037
5164
  try {
5038
- const pid = parseInt(readFileSync16(GATEWAY_PID_FILE, "utf-8").trim(), 10);
5165
+ const pid = parseInt(readFileSync17(GATEWAY_PID_FILE, "utf-8").trim(), 10);
5039
5166
  return isNaN(pid) ? null : pid;
5040
5167
  } catch {
5041
5168
  return null;
@@ -5052,7 +5179,7 @@ function isProcessAlive(pid) {
5052
5179
  function getConfiguredPort() {
5053
5180
  if (!existsSync22(GATEWAY_CONFIG)) return DEFAULT_PORT;
5054
5181
  try {
5055
- const content = readFileSync16(GATEWAY_CONFIG, "utf-8");
5182
+ const content = readFileSync17(GATEWAY_CONFIG, "utf-8");
5056
5183
  const match = content.match(/listen_port\s*=\s*(\d+)/);
5057
5184
  return match ? parseInt(match[1], 10) : DEFAULT_PORT;
5058
5185
  } catch {
@@ -5124,7 +5251,7 @@ function loadGatewayToml() {
5124
5251
  if (!existsSync22(GATEWAY_CONFIG)) {
5125
5252
  ensureConfig(DEFAULT_PORT);
5126
5253
  }
5127
- return readFileSync16(GATEWAY_CONFIG, "utf-8");
5254
+ return readFileSync17(GATEWAY_CONFIG, "utf-8");
5128
5255
  }
5129
5256
  function saveGatewayToml(content) {
5130
5257
  writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
@@ -5457,7 +5584,7 @@ function ruleTypeToCategory(type) {
5457
5584
  }
5458
5585
 
5459
5586
  // src/transport/sync.ts
5460
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5587
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5461
5588
  import { join as join22 } from "path";
5462
5589
  var SYNC_INTERVAL = 6e4;
5463
5590
  var SYNC_TIMEOUT = 3e3;
@@ -5468,7 +5595,7 @@ function getLastSyncTime() {
5468
5595
  try {
5469
5596
  const file = getSyncStateFile();
5470
5597
  if (existsSync23(file)) {
5471
- const data = JSON.parse(readFileSync17(file, "utf-8"));
5598
+ const data = JSON.parse(readFileSync18(file, "utf-8"));
5472
5599
  return data.lastSync || 0;
5473
5600
  }
5474
5601
  } catch {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "restricted"
5
5
  },
6
- "version": "0.1.7",
6
+ "version": "0.1.9",
7
7
  "description": "IMDL — Intelligent Mediation & Detection Layer. AI agent security CLI.",
8
8
  "files": [
9
9
  "dist"