@frostbridge/imdl 0.1.7 → 0.1.8

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 +132 -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,80 @@ 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
+ for (const line of newLines) {
3623
+ let entry;
3624
+ try {
3625
+ entry = JSON.parse(line);
3626
+ } catch {
3627
+ continue;
3628
+ }
3629
+ if (entry.role !== "user") continue;
3630
+ const msg = entry.message?.content;
3631
+ if (!Array.isArray(msg)) continue;
3632
+ let text = "";
3633
+ for (const block of msg) {
3634
+ if (block?.type === "text" && block.text) {
3635
+ text += block.text.replace(/<user_query>\n?/g, "").replace(/\n?<\/user_query>/g, "").trim();
3636
+ }
3637
+ }
3638
+ if (!text || this.isNoisePrompt(text)) continue;
3639
+ events.push({
3640
+ sessionId: `cursor-${sessionDir.slice(0, 8)}`,
3641
+ type: "user_prompt",
3642
+ prompt: text.slice(0, 1e4),
3643
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3644
+ cwd: projectPath,
3645
+ agentType: "cursor"
3646
+ });
3647
+ }
3648
+ } catch {
3649
+ }
3650
+ }
3651
+ }
3652
+ } catch {
3653
+ }
3654
+ return { events, newOffsets };
3655
+ }
3582
3656
  /**
3583
3657
  * Returns the workspace storage path for Cursor, or null if not found.
3584
3658
  */
@@ -3610,6 +3684,13 @@ var CursorAdapter = class _CursorAdapter {
3610
3684
  return [];
3611
3685
  }
3612
3686
  }
3687
+ /**
3688
+ * Returns the Cursor projects directory for transcript scanning.
3689
+ */
3690
+ static getProjectsDir() {
3691
+ const dir = join16(homedir13(), ".cursor", "projects");
3692
+ return existsSync17(dir) ? dir : null;
3693
+ }
3613
3694
  };
3614
3695
 
3615
3696
  // src/adapters/index.ts
@@ -3617,7 +3698,7 @@ var STATE_FILE = join17(getImdlDir(), "adapter-state.json");
3617
3698
  function loadState() {
3618
3699
  try {
3619
3700
  if (existsSync18(STATE_FILE)) {
3620
- return JSON.parse(readFileSync12(STATE_FILE, "utf-8"));
3701
+ return JSON.parse(readFileSync13(STATE_FILE, "utf-8"));
3621
3702
  }
3622
3703
  } catch {
3623
3704
  }
@@ -3698,9 +3779,38 @@ async function collectPrompts() {
3698
3779
  }
3699
3780
  }
3700
3781
  }
3782
+ const cursorAdapter = new CursorAdapter();
3783
+ if (CursorAdapter.getProjectsDir()) {
3784
+ try {
3785
+ const { events: transcriptEvents, newOffsets } = cursorAdapter.extractFromTranscripts(new Date(state.lastRun), state.offsets);
3786
+ state.offsets = newOffsets;
3787
+ for (const event of transcriptEvents) {
3788
+ let violation;
3789
+ if (event.prompt) {
3790
+ const scan = scanPromptForSecrets(event.prompt);
3791
+ if (scan.hasSecrets) {
3792
+ violation = createPromptViolation(scan.findings);
3793
+ }
3794
+ }
3795
+ const buffered = {
3796
+ sessionId: event.sessionId,
3797
+ type: event.type,
3798
+ toolName: event.toolName,
3799
+ toolInput: event.toolInput ? redactObject(event.toolInput) : void 0,
3800
+ prompt: event.prompt ? redactObject(event.prompt) : void 0,
3801
+ timestamp: event.timestamp,
3802
+ cwd: event.cwd,
3803
+ agentType: "cursor",
3804
+ violation
3805
+ };
3806
+ appendEvent(buffered);
3807
+ collected++;
3808
+ }
3809
+ } catch {
3810
+ }
3811
+ }
3701
3812
  const cursorWorkspaces = CursorAdapter.getAllWorkspaces();
3702
3813
  if (cursorWorkspaces.length > 0) {
3703
- const cursorAdapter = new CursorAdapter();
3704
3814
  for (const ws of cursorWorkspaces) {
3705
3815
  try {
3706
3816
  const events = await cursorAdapter.extractEvents({
@@ -3758,7 +3868,7 @@ async function collectCommand() {
3758
3868
 
3759
3869
  // src/commands/daemon.ts
3760
3870
  import pc7 from "picocolors";
3761
- import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
3871
+ import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
3762
3872
  import { join as join18 } from "path";
3763
3873
  var INTERVAL_MS = 3e4;
3764
3874
  async function daemonCommand() {
@@ -3783,7 +3893,7 @@ async function daemonCommand() {
3783
3893
  async function ingestShellEvents() {
3784
3894
  const shellLog = join18(getBufferDir(), "shell-events.ndjson");
3785
3895
  if (!existsSync19(shellLog)) return 0;
3786
- const content = readFileSync13(shellLog, "utf-8").trim();
3896
+ const content = readFileSync14(shellLog, "utf-8").trim();
3787
3897
  if (!content) return 0;
3788
3898
  const config = loadConfig();
3789
3899
  const lines = content.split("\n");
@@ -3888,7 +3998,7 @@ async function requestCommand(options) {
3888
3998
  // src/commands/lock.ts
3889
3999
  import pc9 from "picocolors";
3890
4000
  import { createHash as createHash2 } from "crypto";
3891
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
4001
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
3892
4002
  import { join as join19, resolve as resolve3 } from "path";
3893
4003
  var LOCKFILE_NAME = "mcp-lock.json";
3894
4004
  function computeIntegrity(server, identity) {
@@ -3986,7 +4096,7 @@ async function verifyCommand(options) {
3986
4096
  }
3987
4097
  let lockfile;
3988
4098
  try {
3989
- lockfile = JSON.parse(readFileSync14(lockPath, "utf-8"));
4099
+ lockfile = JSON.parse(readFileSync15(lockPath, "utf-8"));
3990
4100
  } catch {
3991
4101
  console.log(pc9.red("Failed to parse lockfile."));
3992
4102
  process.exit(1);
@@ -4395,7 +4505,7 @@ async function checkCompliance(developerId) {
4395
4505
  }
4396
4506
 
4397
4507
  // src/permissions/fixer.ts
4398
- import { existsSync as existsSync21, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
4508
+ import { existsSync as existsSync21, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
4399
4509
  import { join as join20 } from "path";
4400
4510
  import { homedir as homedir14 } from "os";
4401
4511
  function buildFixesFromChanges(changes) {
@@ -4413,7 +4523,7 @@ function buildFixForAgent(change, home) {
4413
4523
  const configPath = join20(home, ".claude", "settings.json");
4414
4524
  if (!existsSync21(configPath)) return null;
4415
4525
  try {
4416
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4526
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4417
4527
  const tools = settings.allowedTools || [];
4418
4528
  if (category === "shell") {
4419
4529
  if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
@@ -4444,7 +4554,7 @@ function buildFixForAgent(change, home) {
4444
4554
  const configPath = join20(home, ".cursor", "settings.json");
4445
4555
  if (!existsSync21(configPath)) return null;
4446
4556
  try {
4447
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4557
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4448
4558
  if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
4449
4559
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
4450
4560
  }
@@ -4456,7 +4566,7 @@ function buildFixForAgent(change, home) {
4456
4566
  const configPath = join20(home, ".codex", "config.json");
4457
4567
  if (!existsSync21(configPath)) return null;
4458
4568
  try {
4459
- const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4569
+ const config = JSON.parse(readFileSync16(configPath, "utf-8"));
4460
4570
  if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
4461
4571
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
4462
4572
  }
@@ -4468,7 +4578,7 @@ function buildFixForAgent(change, home) {
4468
4578
  const configPath = join20(home, "Library", "Application Support", "Code", "User", "settings.json");
4469
4579
  if (!existsSync21(configPath)) return null;
4470
4580
  try {
4471
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4581
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4472
4582
  if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
4473
4583
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
4474
4584
  }
@@ -4481,7 +4591,7 @@ function buildFixForAgent(change, home) {
4481
4591
  const configPath = join20(home, ".windsurf", "mcp.json");
4482
4592
  if (!existsSync21(configPath)) return null;
4483
4593
  try {
4484
- const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4594
+ const config = JSON.parse(readFileSync16(configPath, "utf-8"));
4485
4595
  const servers = Object.keys(config.mcpServers || {});
4486
4596
  if (servers.length === 0) return null;
4487
4597
  return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
@@ -4494,7 +4604,7 @@ function buildFixForAgent(change, home) {
4494
4604
  }
4495
4605
  function applyFix(fix) {
4496
4606
  try {
4497
- const content = readFileSync15(fix.configPath, "utf-8");
4607
+ const content = readFileSync16(fix.configPath, "utf-8");
4498
4608
  const config = JSON.parse(content);
4499
4609
  let modified = false;
4500
4610
  if (fix.agentType === "claude-code") {
@@ -4785,7 +4895,7 @@ function grantLevel(grant) {
4785
4895
 
4786
4896
  // src/commands/gateway.ts
4787
4897
  import pc12 from "picocolors";
4788
- import { existsSync as existsSync22, readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4898
+ import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4789
4899
  import { join as join21 } from "path";
4790
4900
  import { homedir as homedir15 } from "os";
4791
4901
  import { execSync as execSync2, spawn } from "child_process";
@@ -4931,7 +5041,7 @@ async function gatewayAlertsCommand(opts) {
4931
5041
  console.log(pc12.dim("No alerts logged yet."));
4932
5042
  return;
4933
5043
  }
4934
- const lines = readFileSync16(logPath, "utf-8").split("\n").filter(Boolean);
5044
+ const lines = readFileSync17(logPath, "utf-8").split("\n").filter(Boolean);
4935
5045
  const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
4936
5046
  const recent = lines.slice(-limit);
4937
5047
  if (opts.json) {
@@ -5035,7 +5145,7 @@ async function getGatewayStatus() {
5035
5145
  function getRunningPid() {
5036
5146
  if (!existsSync22(GATEWAY_PID_FILE)) return null;
5037
5147
  try {
5038
- const pid = parseInt(readFileSync16(GATEWAY_PID_FILE, "utf-8").trim(), 10);
5148
+ const pid = parseInt(readFileSync17(GATEWAY_PID_FILE, "utf-8").trim(), 10);
5039
5149
  return isNaN(pid) ? null : pid;
5040
5150
  } catch {
5041
5151
  return null;
@@ -5052,7 +5162,7 @@ function isProcessAlive(pid) {
5052
5162
  function getConfiguredPort() {
5053
5163
  if (!existsSync22(GATEWAY_CONFIG)) return DEFAULT_PORT;
5054
5164
  try {
5055
- const content = readFileSync16(GATEWAY_CONFIG, "utf-8");
5165
+ const content = readFileSync17(GATEWAY_CONFIG, "utf-8");
5056
5166
  const match = content.match(/listen_port\s*=\s*(\d+)/);
5057
5167
  return match ? parseInt(match[1], 10) : DEFAULT_PORT;
5058
5168
  } catch {
@@ -5124,7 +5234,7 @@ function loadGatewayToml() {
5124
5234
  if (!existsSync22(GATEWAY_CONFIG)) {
5125
5235
  ensureConfig(DEFAULT_PORT);
5126
5236
  }
5127
- return readFileSync16(GATEWAY_CONFIG, "utf-8");
5237
+ return readFileSync17(GATEWAY_CONFIG, "utf-8");
5128
5238
  }
5129
5239
  function saveGatewayToml(content) {
5130
5240
  writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
@@ -5457,7 +5567,7 @@ function ruleTypeToCategory(type) {
5457
5567
  }
5458
5568
 
5459
5569
  // src/transport/sync.ts
5460
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5570
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5461
5571
  import { join as join22 } from "path";
5462
5572
  var SYNC_INTERVAL = 6e4;
5463
5573
  var SYNC_TIMEOUT = 3e3;
@@ -5468,7 +5578,7 @@ function getLastSyncTime() {
5468
5578
  try {
5469
5579
  const file = getSyncStateFile();
5470
5580
  if (existsSync23(file)) {
5471
- const data = JSON.parse(readFileSync17(file, "utf-8"));
5581
+ const data = JSON.parse(readFileSync18(file, "utf-8"));
5472
5582
  return data.lastSync || 0;
5473
5583
  }
5474
5584
  } 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.8",
7
7
  "description": "IMDL — Intelligent Mediation & Detection Layer. AI agent security CLI.",
8
8
  "files": [
9
9
  "dist"