@frostbridge/imdl 0.1.11 → 0.1.13
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 +494 -75
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
|
-
import { Command } from "commander";
|
|
10
|
+
import { Command as Command2 } from "commander";
|
|
11
11
|
|
|
12
12
|
// src/commands/scan.ts
|
|
13
13
|
import pc from "picocolors";
|
|
@@ -633,7 +633,7 @@ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync
|
|
|
633
633
|
import { join as join4 } from "path";
|
|
634
634
|
|
|
635
635
|
// src/config/store.ts
|
|
636
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync4, unlinkSync } from "fs";
|
|
636
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync as existsSync4, unlinkSync, chmodSync } from "fs";
|
|
637
637
|
import { join as join3 } from "path";
|
|
638
638
|
import { homedir as homedir2 } from "os";
|
|
639
639
|
import { randomUUID } from "crypto";
|
|
@@ -647,8 +647,10 @@ function getBufferDir() {
|
|
|
647
647
|
return BUFFER_DIR;
|
|
648
648
|
}
|
|
649
649
|
function ensureImdlDir() {
|
|
650
|
-
|
|
651
|
-
|
|
650
|
+
mkdirSync(IMDL_DIR, { recursive: true, mode: 448 });
|
|
651
|
+
chmodSync(IMDL_DIR, 448);
|
|
652
|
+
mkdirSync(BUFFER_DIR, { recursive: true, mode: 448 });
|
|
653
|
+
chmodSync(BUFFER_DIR, 448);
|
|
652
654
|
}
|
|
653
655
|
function isLocalUrl(url) {
|
|
654
656
|
try {
|
|
@@ -1231,7 +1233,8 @@ var AGENTS = [
|
|
|
1231
1233
|
hooksFile: join6(homedir4(), ".claude", "settings.json"),
|
|
1232
1234
|
hooks: {
|
|
1233
1235
|
PreToolUse: [{ matcher: "*", hooks: [{ type: "command", command: "imdl hook pre-tool", timeout: 5 }] }],
|
|
1234
|
-
PostToolUse: [{ matcher: "*", hooks: [{ type: "command", command: "imdl hook post-tool", timeout: 5 }] }]
|
|
1236
|
+
PostToolUse: [{ matcher: "*", hooks: [{ type: "command", command: "imdl hook post-tool", timeout: 5 }] }],
|
|
1237
|
+
UserPromptSubmit: [{ matcher: "*", hooks: [{ type: "command", command: "imdl hook prompt", timeout: 5 }] }]
|
|
1235
1238
|
}
|
|
1236
1239
|
},
|
|
1237
1240
|
{
|
|
@@ -2705,8 +2708,8 @@ function resumeCommand() {
|
|
|
2705
2708
|
import pc6 from "picocolors";
|
|
2706
2709
|
|
|
2707
2710
|
// src/adapters/index.ts
|
|
2708
|
-
import { existsSync as
|
|
2709
|
-
import { join as
|
|
2711
|
+
import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
|
|
2712
|
+
import { join as join18 } from "path";
|
|
2710
2713
|
|
|
2711
2714
|
// src/transport/buffer.ts
|
|
2712
2715
|
import { appendFileSync, readFileSync as readFileSync11, writeFileSync as writeFileSync5, existsSync as existsSync15, statSync as statSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
@@ -3149,6 +3152,21 @@ var ClaudeCodeAdapter = class {
|
|
|
3149
3152
|
cwd: entry.cwd,
|
|
3150
3153
|
agentType: "claude-code"
|
|
3151
3154
|
});
|
|
3155
|
+
} else if (Array.isArray(content)) {
|
|
3156
|
+
const textBlock = content.find(
|
|
3157
|
+
(block) => block && block.type === "text" && typeof block.text === "string" && block.text.length > 0
|
|
3158
|
+
);
|
|
3159
|
+
if (textBlock) {
|
|
3160
|
+
if (this.isNoisePrompt(textBlock.text)) continue;
|
|
3161
|
+
events.push({
|
|
3162
|
+
sessionId: entry.sessionId || this.sessionIdFromPath(path),
|
|
3163
|
+
type: "user_prompt",
|
|
3164
|
+
prompt: textBlock.text.slice(0, 1e4),
|
|
3165
|
+
timestamp: entry.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
3166
|
+
cwd: entry.cwd,
|
|
3167
|
+
agentType: "claude-code"
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3152
3170
|
}
|
|
3153
3171
|
}
|
|
3154
3172
|
if (entry.type === "assistant" && entry.message?.usage) {
|
|
@@ -3677,6 +3695,23 @@ var CursorAdapter = class _CursorAdapter {
|
|
|
3677
3695
|
});
|
|
3678
3696
|
}
|
|
3679
3697
|
}
|
|
3698
|
+
const usage = entry.message?.usage || entry.usage;
|
|
3699
|
+
if (usage && (usage.input_tokens || usage.output_tokens)) {
|
|
3700
|
+
events.push({
|
|
3701
|
+
sessionId: sid,
|
|
3702
|
+
type: "notification",
|
|
3703
|
+
timestamp: now,
|
|
3704
|
+
cwd: projectPath,
|
|
3705
|
+
agentType: "cursor",
|
|
3706
|
+
usage: {
|
|
3707
|
+
model: entry.message?.model || entry.model || "unknown",
|
|
3708
|
+
inputTokens: usage.input_tokens || 0,
|
|
3709
|
+
outputTokens: usage.output_tokens || 0,
|
|
3710
|
+
cacheReadTokens: usage.cache_read_input_tokens || 0,
|
|
3711
|
+
cacheWriteTokens: usage.cache_creation_input_tokens || 0
|
|
3712
|
+
}
|
|
3713
|
+
});
|
|
3714
|
+
}
|
|
3680
3715
|
}
|
|
3681
3716
|
}
|
|
3682
3717
|
} catch {
|
|
@@ -3727,12 +3762,141 @@ var CursorAdapter = class _CursorAdapter {
|
|
|
3727
3762
|
}
|
|
3728
3763
|
};
|
|
3729
3764
|
|
|
3765
|
+
// src/adapters/codex.ts
|
|
3766
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync13, statSync as statSync10 } from "fs";
|
|
3767
|
+
import { join as join17 } from "path";
|
|
3768
|
+
import { homedir as homedir14 } from "os";
|
|
3769
|
+
var CodexAdapter = class {
|
|
3770
|
+
agentType = "codex";
|
|
3771
|
+
async extractEvents(source) {
|
|
3772
|
+
if (source.kind === "directory") {
|
|
3773
|
+
return this.readSessionsDir(source.path, source.since);
|
|
3774
|
+
}
|
|
3775
|
+
return [];
|
|
3776
|
+
}
|
|
3777
|
+
readSessionsDir(basePath, since) {
|
|
3778
|
+
if (!existsSync18(basePath)) return [];
|
|
3779
|
+
const events = [];
|
|
3780
|
+
const files = this.findSessionFiles(basePath, since);
|
|
3781
|
+
for (const filePath of files) {
|
|
3782
|
+
const fileEvents = this.readSessionFile(filePath, since);
|
|
3783
|
+
events.push(...fileEvents);
|
|
3784
|
+
}
|
|
3785
|
+
return events;
|
|
3786
|
+
}
|
|
3787
|
+
findSessionFiles(basePath, since) {
|
|
3788
|
+
const files = [];
|
|
3789
|
+
const walk = (dir) => {
|
|
3790
|
+
try {
|
|
3791
|
+
for (const entry of readdirSync5(dir)) {
|
|
3792
|
+
const full = join17(dir, entry);
|
|
3793
|
+
try {
|
|
3794
|
+
const stat = statSync10(full);
|
|
3795
|
+
if (stat.isDirectory()) {
|
|
3796
|
+
walk(full);
|
|
3797
|
+
} else if (entry.endsWith(".jsonl")) {
|
|
3798
|
+
if (!since || stat.mtimeMs >= since.getTime()) {
|
|
3799
|
+
files.push(full);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
} catch {
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
} catch {
|
|
3806
|
+
}
|
|
3807
|
+
};
|
|
3808
|
+
walk(basePath);
|
|
3809
|
+
return files;
|
|
3810
|
+
}
|
|
3811
|
+
readSessionFile(filePath, since) {
|
|
3812
|
+
const events = [];
|
|
3813
|
+
let content;
|
|
3814
|
+
try {
|
|
3815
|
+
content = readFileSync13(filePath, "utf-8");
|
|
3816
|
+
} catch {
|
|
3817
|
+
return [];
|
|
3818
|
+
}
|
|
3819
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
3820
|
+
const sessionId = this.sessionIdFromPath(filePath);
|
|
3821
|
+
let cwd;
|
|
3822
|
+
for (const line of lines) {
|
|
3823
|
+
let entry;
|
|
3824
|
+
try {
|
|
3825
|
+
entry = JSON.parse(line);
|
|
3826
|
+
} catch {
|
|
3827
|
+
continue;
|
|
3828
|
+
}
|
|
3829
|
+
if (since && entry.timestamp) {
|
|
3830
|
+
const entryTime = new Date(entry.timestamp).getTime();
|
|
3831
|
+
if (entryTime <= since.getTime()) continue;
|
|
3832
|
+
}
|
|
3833
|
+
const ptype = entry.payload?.type;
|
|
3834
|
+
if (ptype === "turn_context" && entry.payload.cwd) {
|
|
3835
|
+
cwd = entry.payload.cwd;
|
|
3836
|
+
}
|
|
3837
|
+
if (ptype === "user_message" && entry.payload.message) {
|
|
3838
|
+
const msg = entry.payload.message.trim();
|
|
3839
|
+
if (msg.length > 0) {
|
|
3840
|
+
events.push({
|
|
3841
|
+
sessionId,
|
|
3842
|
+
type: "user_prompt",
|
|
3843
|
+
prompt: msg.slice(0, 1e4),
|
|
3844
|
+
timestamp: entry.timestamp,
|
|
3845
|
+
cwd,
|
|
3846
|
+
agentType: "codex"
|
|
3847
|
+
});
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
if (ptype === "function_call" && entry.payload.name) {
|
|
3851
|
+
let toolInput;
|
|
3852
|
+
if (entry.payload.arguments) {
|
|
3853
|
+
try {
|
|
3854
|
+
toolInput = JSON.parse(entry.payload.arguments);
|
|
3855
|
+
} catch {
|
|
3856
|
+
toolInput = { raw: entry.payload.arguments };
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
events.push({
|
|
3860
|
+
sessionId,
|
|
3861
|
+
type: "pre_tool_use",
|
|
3862
|
+
toolName: entry.payload.name,
|
|
3863
|
+
toolInput,
|
|
3864
|
+
timestamp: entry.timestamp,
|
|
3865
|
+
cwd,
|
|
3866
|
+
agentType: "codex"
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
3869
|
+
if (ptype === "function_call_output" && entry.payload.output) {
|
|
3870
|
+
events.push({
|
|
3871
|
+
sessionId,
|
|
3872
|
+
type: "post_tool_use",
|
|
3873
|
+
toolName: entry.payload.call_id || "unknown",
|
|
3874
|
+
toolOutput: entry.payload.output.slice(0, 1e4),
|
|
3875
|
+
timestamp: entry.timestamp,
|
|
3876
|
+
cwd,
|
|
3877
|
+
agentType: "codex"
|
|
3878
|
+
});
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
return events;
|
|
3882
|
+
}
|
|
3883
|
+
sessionIdFromPath(filePath) {
|
|
3884
|
+
const filename = filePath.split("/").pop() || "";
|
|
3885
|
+
const match = filename.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/);
|
|
3886
|
+
return match ? `codex-${match[1].slice(0, 8)}` : `codex-${filename.replace(".jsonl", "").slice(-8)}`;
|
|
3887
|
+
}
|
|
3888
|
+
static getSessionsDir() {
|
|
3889
|
+
const dir = join17(homedir14(), ".codex", "sessions");
|
|
3890
|
+
return existsSync18(dir) ? dir : null;
|
|
3891
|
+
}
|
|
3892
|
+
};
|
|
3893
|
+
|
|
3730
3894
|
// src/adapters/index.ts
|
|
3731
|
-
var STATE_FILE =
|
|
3895
|
+
var STATE_FILE = join18(getImdlDir(), "adapter-state.json");
|
|
3732
3896
|
function loadState() {
|
|
3733
3897
|
try {
|
|
3734
|
-
if (
|
|
3735
|
-
return JSON.parse(
|
|
3898
|
+
if (existsSync19(STATE_FILE)) {
|
|
3899
|
+
return JSON.parse(readFileSync14(STATE_FILE, "utf-8"));
|
|
3736
3900
|
}
|
|
3737
3901
|
} catch {
|
|
3738
3902
|
}
|
|
@@ -3744,12 +3908,15 @@ function saveState(state) {
|
|
|
3744
3908
|
}
|
|
3745
3909
|
var adapters = [
|
|
3746
3910
|
new ClaudeCodeAdapter(),
|
|
3747
|
-
new CursorAdapter()
|
|
3911
|
+
new CursorAdapter(),
|
|
3912
|
+
new CodexAdapter()
|
|
3748
3913
|
];
|
|
3749
3914
|
async function collectPrompts() {
|
|
3750
3915
|
const state = loadState();
|
|
3751
3916
|
const config = loadConfig();
|
|
3752
3917
|
let collected = 0;
|
|
3918
|
+
const usageHeaders = { "Content-Type": "application/json" };
|
|
3919
|
+
if (config.authToken) usageHeaders["X-IMDL-Key"] = config.authToken;
|
|
3753
3920
|
const claudeDirs = ClaudeCodeAdapter.getAllProjectDirs();
|
|
3754
3921
|
const claudeAdapter = new ClaudeCodeAdapter();
|
|
3755
3922
|
for (const dir of claudeDirs) {
|
|
@@ -3799,8 +3966,6 @@ async function collectPrompts() {
|
|
|
3799
3966
|
appendEvent(buffered);
|
|
3800
3967
|
collected++;
|
|
3801
3968
|
}
|
|
3802
|
-
const usageHeaders = { "Content-Type": "application/json" };
|
|
3803
|
-
if (config.authToken) usageHeaders["X-IMDL-Key"] = config.authToken;
|
|
3804
3969
|
for (const [sessionId, usage] of usageBySession) {
|
|
3805
3970
|
try {
|
|
3806
3971
|
await fetch(`${config.apiUrl}/api/sessions/${sessionId}/usage`, {
|
|
@@ -3814,11 +3979,22 @@ async function collectPrompts() {
|
|
|
3814
3979
|
}
|
|
3815
3980
|
}
|
|
3816
3981
|
const cursorAdapter = new CursorAdapter();
|
|
3982
|
+
const cursorUsageBySession = /* @__PURE__ */ new Map();
|
|
3817
3983
|
if (CursorAdapter.getProjectsDir()) {
|
|
3818
3984
|
try {
|
|
3819
3985
|
const { events: transcriptEvents, newOffsets } = cursorAdapter.extractFromTranscripts(new Date(state.lastRun), state.offsets);
|
|
3820
3986
|
state.offsets = newOffsets;
|
|
3821
3987
|
for (const event of transcriptEvents) {
|
|
3988
|
+
if (event.usage) {
|
|
3989
|
+
const existing = cursorUsageBySession.get(event.sessionId) || { model: event.usage.model, input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
3990
|
+
existing.model = event.usage.model;
|
|
3991
|
+
existing.input += event.usage.inputTokens;
|
|
3992
|
+
existing.output += event.usage.outputTokens;
|
|
3993
|
+
existing.cacheRead += event.usage.cacheReadTokens;
|
|
3994
|
+
existing.cacheWrite += event.usage.cacheWriteTokens;
|
|
3995
|
+
cursorUsageBySession.set(event.sessionId, existing);
|
|
3996
|
+
continue;
|
|
3997
|
+
}
|
|
3822
3998
|
let violation;
|
|
3823
3999
|
if (event.prompt) {
|
|
3824
4000
|
const scan = scanPromptForSecrets(event.prompt);
|
|
@@ -3878,6 +4054,52 @@ async function collectPrompts() {
|
|
|
3878
4054
|
}
|
|
3879
4055
|
}
|
|
3880
4056
|
}
|
|
4057
|
+
for (const [sessionId, usage] of cursorUsageBySession) {
|
|
4058
|
+
try {
|
|
4059
|
+
await fetch(`${config.apiUrl}/api/sessions/${sessionId}/usage`, {
|
|
4060
|
+
method: "POST",
|
|
4061
|
+
headers: usageHeaders,
|
|
4062
|
+
body: JSON.stringify(usage),
|
|
4063
|
+
signal: AbortSignal.timeout(3e3)
|
|
4064
|
+
});
|
|
4065
|
+
} catch {
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
const codexSessionsDir = CodexAdapter.getSessionsDir();
|
|
4069
|
+
if (codexSessionsDir) {
|
|
4070
|
+
try {
|
|
4071
|
+
const codexAdapter = new CodexAdapter();
|
|
4072
|
+
const codexEvents = await codexAdapter.extractEvents({
|
|
4073
|
+
kind: "directory",
|
|
4074
|
+
path: codexSessionsDir,
|
|
4075
|
+
since: new Date(state.lastRun)
|
|
4076
|
+
});
|
|
4077
|
+
for (const event of codexEvents) {
|
|
4078
|
+
let violation;
|
|
4079
|
+
if (event.prompt) {
|
|
4080
|
+
const scan = scanPromptForSecrets(event.prompt);
|
|
4081
|
+
if (scan.hasSecrets) {
|
|
4082
|
+
violation = createPromptViolation(scan.findings);
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
const buffered = {
|
|
4086
|
+
sessionId: event.sessionId,
|
|
4087
|
+
type: event.type,
|
|
4088
|
+
toolName: event.toolName,
|
|
4089
|
+
toolInput: event.toolInput ? redactObject(event.toolInput) : void 0,
|
|
4090
|
+
toolOutput: event.toolOutput ? redactObject(event.toolOutput) : void 0,
|
|
4091
|
+
prompt: event.prompt ? redactObject(event.prompt) : void 0,
|
|
4092
|
+
timestamp: event.timestamp,
|
|
4093
|
+
cwd: event.cwd,
|
|
4094
|
+
agentType: "codex",
|
|
4095
|
+
violation
|
|
4096
|
+
};
|
|
4097
|
+
appendEvent(buffered);
|
|
4098
|
+
collected++;
|
|
4099
|
+
}
|
|
4100
|
+
} catch {
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
3881
4103
|
if (collected > 0) {
|
|
3882
4104
|
try {
|
|
3883
4105
|
await tryFlush();
|
|
@@ -3902,8 +4124,8 @@ async function collectCommand() {
|
|
|
3902
4124
|
|
|
3903
4125
|
// src/commands/daemon.ts
|
|
3904
4126
|
import pc7 from "picocolors";
|
|
3905
|
-
import { existsSync as
|
|
3906
|
-
import { join as
|
|
4127
|
+
import { existsSync as existsSync20, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
|
|
4128
|
+
import { join as join19 } from "path";
|
|
3907
4129
|
var INTERVAL_MS = 3e4;
|
|
3908
4130
|
async function daemonCommand() {
|
|
3909
4131
|
console.log(pc7.cyan("imdl daemon started \u2014 collecting every 30s"));
|
|
@@ -3912,9 +4134,10 @@ async function daemonCommand() {
|
|
|
3912
4134
|
try {
|
|
3913
4135
|
const count = await collectPrompts();
|
|
3914
4136
|
const shellCount = await ingestShellEvents();
|
|
3915
|
-
const
|
|
4137
|
+
const agentCount = await ingestAgentEvents();
|
|
4138
|
+
const total = count + shellCount + agentCount;
|
|
3916
4139
|
if (total > 0) {
|
|
3917
|
-
console.log(pc7.green(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Collected ${total} event${total !== 1 ? "s" : ""} (prompts: ${count}, shell: ${shellCount})`));
|
|
4140
|
+
console.log(pc7.green(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Collected ${total} event${total !== 1 ? "s" : ""} (prompts: ${count}, shell: ${shellCount}, os-agent: ${agentCount})`));
|
|
3918
4141
|
}
|
|
3919
4142
|
await tryFlush();
|
|
3920
4143
|
} catch (err) {
|
|
@@ -3924,10 +4147,65 @@ async function daemonCommand() {
|
|
|
3924
4147
|
await tick();
|
|
3925
4148
|
setInterval(tick, INTERVAL_MS);
|
|
3926
4149
|
}
|
|
4150
|
+
async function ingestAgentEvents() {
|
|
4151
|
+
const agentLog = join19(getBufferDir(), "agent-events.ndjson");
|
|
4152
|
+
if (!existsSync20(agentLog)) return 0;
|
|
4153
|
+
const content = readFileSync15(agentLog, "utf-8").trim();
|
|
4154
|
+
if (!content) return 0;
|
|
4155
|
+
const config = loadConfig();
|
|
4156
|
+
const lines = content.split("\n");
|
|
4157
|
+
const events = lines.map((line) => {
|
|
4158
|
+
try {
|
|
4159
|
+
return JSON.parse(line);
|
|
4160
|
+
} catch {
|
|
4161
|
+
return null;
|
|
4162
|
+
}
|
|
4163
|
+
}).filter(Boolean);
|
|
4164
|
+
if (events.length === 0) return 0;
|
|
4165
|
+
const headers = { "Content-Type": "application/json" };
|
|
4166
|
+
if (config.authToken) headers["X-IMDL-Key"] = config.authToken;
|
|
4167
|
+
const sessionId = `os_${config.developerId}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
4168
|
+
const apiEvents = events.map((e) => ({
|
|
4169
|
+
type: e.event_type === "exec" ? "pre_tool_use" : "tool_call",
|
|
4170
|
+
toolName: `os_${e.event_type}`,
|
|
4171
|
+
toolInput: {
|
|
4172
|
+
process: e.process,
|
|
4173
|
+
path: e.path,
|
|
4174
|
+
address: e.address,
|
|
4175
|
+
pid: e.pid,
|
|
4176
|
+
agent: e.agent
|
|
4177
|
+
},
|
|
4178
|
+
timestamp: e.timestamp,
|
|
4179
|
+
violation: e.decision === "block" ? {
|
|
4180
|
+
action: "block",
|
|
4181
|
+
reason: e.reason,
|
|
4182
|
+
policyName: e.policy_name
|
|
4183
|
+
} : void 0,
|
|
4184
|
+
source: "os_agent"
|
|
4185
|
+
}));
|
|
4186
|
+
let sent = 0;
|
|
4187
|
+
for (let i = 0; i < apiEvents.length; i += 50) {
|
|
4188
|
+
const batch = apiEvents.slice(i, i + 50);
|
|
4189
|
+
try {
|
|
4190
|
+
const res = await fetch(`${config.apiUrl}/api/sessions/${sessionId}/events`, {
|
|
4191
|
+
method: "POST",
|
|
4192
|
+
headers,
|
|
4193
|
+
body: JSON.stringify({ events: batch, developerId: config.developerId, source: "os_agent" }),
|
|
4194
|
+
signal: AbortSignal.timeout(1e4)
|
|
4195
|
+
});
|
|
4196
|
+
if (res.ok) sent += batch.length;
|
|
4197
|
+
} catch {
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
if (sent === apiEvents.length) {
|
|
4201
|
+
writeFileSync7(agentLog, "", { mode: 384 });
|
|
4202
|
+
}
|
|
4203
|
+
return sent;
|
|
4204
|
+
}
|
|
3927
4205
|
async function ingestShellEvents() {
|
|
3928
|
-
const shellLog =
|
|
3929
|
-
if (!
|
|
3930
|
-
const content =
|
|
4206
|
+
const shellLog = join19(getBufferDir(), "shell-events.ndjson");
|
|
4207
|
+
if (!existsSync20(shellLog)) return 0;
|
|
4208
|
+
const content = readFileSync15(shellLog, "utf-8").trim();
|
|
3931
4209
|
if (!content) return 0;
|
|
3932
4210
|
const config = loadConfig();
|
|
3933
4211
|
const lines = content.split("\n");
|
|
@@ -4032,8 +4310,8 @@ async function requestCommand(options) {
|
|
|
4032
4310
|
// src/commands/lock.ts
|
|
4033
4311
|
import pc9 from "picocolors";
|
|
4034
4312
|
import { createHash as createHash2 } from "crypto";
|
|
4035
|
-
import { readFileSync as
|
|
4036
|
-
import { join as
|
|
4313
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync8, existsSync as existsSync21 } from "fs";
|
|
4314
|
+
import { join as join20, resolve as resolve3 } from "path";
|
|
4037
4315
|
var LOCKFILE_NAME = "mcp-lock.json";
|
|
4038
4316
|
function computeIntegrity(server, identity) {
|
|
4039
4317
|
const payload = JSON.stringify({
|
|
@@ -4048,7 +4326,7 @@ function computeIntegrity(server, identity) {
|
|
|
4048
4326
|
}
|
|
4049
4327
|
function getLockfilePath(customPath) {
|
|
4050
4328
|
if (customPath) return resolve3(customPath);
|
|
4051
|
-
return
|
|
4329
|
+
return join20(process.cwd(), LOCKFILE_NAME);
|
|
4052
4330
|
}
|
|
4053
4331
|
async function lockCommand(options) {
|
|
4054
4332
|
const detection = await detectMCPConfigs(options.path);
|
|
@@ -4118,7 +4396,7 @@ async function lockCommand(options) {
|
|
|
4118
4396
|
}
|
|
4119
4397
|
async function verifyCommand(options) {
|
|
4120
4398
|
const lockPath = getLockfilePath(options.path);
|
|
4121
|
-
if (!
|
|
4399
|
+
if (!existsSync21(lockPath)) {
|
|
4122
4400
|
if (options.json) {
|
|
4123
4401
|
console.log(JSON.stringify({ error: "No lockfile found. Run `imdl lock` first.", path: lockPath }));
|
|
4124
4402
|
} else {
|
|
@@ -4130,7 +4408,7 @@ async function verifyCommand(options) {
|
|
|
4130
4408
|
}
|
|
4131
4409
|
let lockfile;
|
|
4132
4410
|
try {
|
|
4133
|
-
lockfile = JSON.parse(
|
|
4411
|
+
lockfile = JSON.parse(readFileSync16(lockPath, "utf-8"));
|
|
4134
4412
|
} catch {
|
|
4135
4413
|
console.log(pc9.red("Failed to parse lockfile."));
|
|
4136
4414
|
process.exit(1);
|
|
@@ -4539,12 +4817,12 @@ async function checkCompliance(developerId) {
|
|
|
4539
4817
|
}
|
|
4540
4818
|
|
|
4541
4819
|
// src/permissions/fixer.ts
|
|
4542
|
-
import { existsSync as
|
|
4543
|
-
import { join as
|
|
4544
|
-
import { homedir as
|
|
4820
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync9 } from "fs";
|
|
4821
|
+
import { join as join21 } from "path";
|
|
4822
|
+
import { homedir as homedir15 } from "os";
|
|
4545
4823
|
function buildFixesFromChanges(changes) {
|
|
4546
4824
|
const fixes = [];
|
|
4547
|
-
const home =
|
|
4825
|
+
const home = homedir15();
|
|
4548
4826
|
for (const change of changes) {
|
|
4549
4827
|
const fix = buildFixForAgent(change, home);
|
|
4550
4828
|
if (fix) fixes.push(fix);
|
|
@@ -4554,10 +4832,10 @@ function buildFixesFromChanges(changes) {
|
|
|
4554
4832
|
function buildFixForAgent(change, home) {
|
|
4555
4833
|
const { agentType, category, action, targetGrant } = change;
|
|
4556
4834
|
if (agentType === "claude-code") {
|
|
4557
|
-
const configPath =
|
|
4558
|
-
if (!
|
|
4835
|
+
const configPath = join21(home, ".claude", "settings.json");
|
|
4836
|
+
if (!existsSync22(configPath)) return null;
|
|
4559
4837
|
try {
|
|
4560
|
-
const settings = JSON.parse(
|
|
4838
|
+
const settings = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4561
4839
|
const tools = settings.allowedTools || [];
|
|
4562
4840
|
if (category === "shell") {
|
|
4563
4841
|
if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
|
|
@@ -4585,10 +4863,10 @@ function buildFixForAgent(change, home) {
|
|
|
4585
4863
|
}
|
|
4586
4864
|
}
|
|
4587
4865
|
if (agentType === "cursor") {
|
|
4588
|
-
const configPath =
|
|
4589
|
-
if (!
|
|
4866
|
+
const configPath = join21(home, ".cursor", "settings.json");
|
|
4867
|
+
if (!existsSync22(configPath)) return null;
|
|
4590
4868
|
try {
|
|
4591
|
-
const settings = JSON.parse(
|
|
4869
|
+
const settings = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4592
4870
|
if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
|
|
4593
4871
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
|
|
4594
4872
|
}
|
|
@@ -4597,10 +4875,10 @@ function buildFixForAgent(change, home) {
|
|
|
4597
4875
|
}
|
|
4598
4876
|
}
|
|
4599
4877
|
if (agentType === "codex") {
|
|
4600
|
-
const configPath =
|
|
4601
|
-
if (!
|
|
4878
|
+
const configPath = join21(home, ".codex", "config.json");
|
|
4879
|
+
if (!existsSync22(configPath)) return null;
|
|
4602
4880
|
try {
|
|
4603
|
-
const config = JSON.parse(
|
|
4881
|
+
const config = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4604
4882
|
if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
|
|
4605
4883
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
|
|
4606
4884
|
}
|
|
@@ -4609,10 +4887,10 @@ function buildFixForAgent(change, home) {
|
|
|
4609
4887
|
}
|
|
4610
4888
|
}
|
|
4611
4889
|
if (agentType === "copilot") {
|
|
4612
|
-
const configPath =
|
|
4613
|
-
if (!
|
|
4890
|
+
const configPath = join21(home, "Library", "Application Support", "Code", "User", "settings.json");
|
|
4891
|
+
if (!existsSync22(configPath)) return null;
|
|
4614
4892
|
try {
|
|
4615
|
-
const settings = JSON.parse(
|
|
4893
|
+
const settings = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4616
4894
|
if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
|
|
4617
4895
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
|
|
4618
4896
|
}
|
|
@@ -4622,10 +4900,10 @@ function buildFixForAgent(change, home) {
|
|
|
4622
4900
|
}
|
|
4623
4901
|
if (agentType === "windsurf") {
|
|
4624
4902
|
if (category === "mcp_tools") {
|
|
4625
|
-
const configPath =
|
|
4626
|
-
if (!
|
|
4903
|
+
const configPath = join21(home, ".windsurf", "mcp.json");
|
|
4904
|
+
if (!existsSync22(configPath)) return null;
|
|
4627
4905
|
try {
|
|
4628
|
-
const config = JSON.parse(
|
|
4906
|
+
const config = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4629
4907
|
const servers = Object.keys(config.mcpServers || {});
|
|
4630
4908
|
if (servers.length === 0) return null;
|
|
4631
4909
|
return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
|
|
@@ -4638,7 +4916,7 @@ function buildFixForAgent(change, home) {
|
|
|
4638
4916
|
}
|
|
4639
4917
|
function applyFix(fix) {
|
|
4640
4918
|
try {
|
|
4641
|
-
const content =
|
|
4919
|
+
const content = readFileSync17(fix.configPath, "utf-8");
|
|
4642
4920
|
const config = JSON.parse(content);
|
|
4643
4921
|
let modified = false;
|
|
4644
4922
|
if (fix.agentType === "claude-code") {
|
|
@@ -4929,13 +5207,13 @@ function grantLevel(grant) {
|
|
|
4929
5207
|
|
|
4930
5208
|
// src/commands/gateway.ts
|
|
4931
5209
|
import pc12 from "picocolors";
|
|
4932
|
-
import { existsSync as
|
|
4933
|
-
import { join as
|
|
4934
|
-
import { homedir as
|
|
5210
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
|
|
5211
|
+
import { join as join22 } from "path";
|
|
5212
|
+
import { homedir as homedir16 } from "os";
|
|
4935
5213
|
import { execSync as execSync2, spawn } from "child_process";
|
|
4936
|
-
var IMDL_DIR3 =
|
|
4937
|
-
var GATEWAY_CONFIG =
|
|
4938
|
-
var GATEWAY_PID_FILE =
|
|
5214
|
+
var IMDL_DIR3 = join22(homedir16(), ".imdl");
|
|
5215
|
+
var GATEWAY_CONFIG = join22(IMDL_DIR3, "gateway.toml");
|
|
5216
|
+
var GATEWAY_PID_FILE = join22(IMDL_DIR3, "gateway.pid");
|
|
4939
5217
|
var DEFAULT_PORT = 9443;
|
|
4940
5218
|
async function gatewayCommand(opts) {
|
|
4941
5219
|
const status = await getGatewayStatus();
|
|
@@ -5010,7 +5288,7 @@ async function gatewayStartCommand(opts) {
|
|
|
5010
5288
|
});
|
|
5011
5289
|
child.unref();
|
|
5012
5290
|
if (child.pid) {
|
|
5013
|
-
if (!
|
|
5291
|
+
if (!existsSync23(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
|
|
5014
5292
|
writeFileSync10(GATEWAY_PID_FILE, String(child.pid));
|
|
5015
5293
|
console.log(pc12.green(`Gateway started (PID ${child.pid}, port ${port})`));
|
|
5016
5294
|
console.log(pc12.dim(` Config: ${GATEWAY_CONFIG}`));
|
|
@@ -5038,8 +5316,8 @@ async function gatewayStopCommand() {
|
|
|
5038
5316
|
console.log(pc12.yellow("Could not stop gateway \u2014 process may have already exited."));
|
|
5039
5317
|
}
|
|
5040
5318
|
try {
|
|
5041
|
-
const { unlinkSync:
|
|
5042
|
-
|
|
5319
|
+
const { unlinkSync: unlinkSync4 } = await import("fs");
|
|
5320
|
+
unlinkSync4(GATEWAY_PID_FILE);
|
|
5043
5321
|
} catch {
|
|
5044
5322
|
}
|
|
5045
5323
|
}
|
|
@@ -5070,12 +5348,12 @@ async function gatewayDisableCommand(opts) {
|
|
|
5070
5348
|
console.log(pc12.dim("Restart gateway for changes to take effect."));
|
|
5071
5349
|
}
|
|
5072
5350
|
async function gatewayAlertsCommand(opts) {
|
|
5073
|
-
const logPath =
|
|
5074
|
-
if (!
|
|
5351
|
+
const logPath = join22(IMDL_DIR3, "gateway-alerts.jsonl");
|
|
5352
|
+
if (!existsSync23(logPath)) {
|
|
5075
5353
|
console.log(pc12.dim("No alerts logged yet."));
|
|
5076
5354
|
return;
|
|
5077
5355
|
}
|
|
5078
|
-
const lines =
|
|
5356
|
+
const lines = readFileSync18(logPath, "utf-8").split("\n").filter(Boolean);
|
|
5079
5357
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
|
|
5080
5358
|
const recent = lines.slice(-limit);
|
|
5081
5359
|
if (opts.json) {
|
|
@@ -5177,9 +5455,9 @@ async function getGatewayStatus() {
|
|
|
5177
5455
|
return { running: true, pid, port, stats, config };
|
|
5178
5456
|
}
|
|
5179
5457
|
function getRunningPid() {
|
|
5180
|
-
if (!
|
|
5458
|
+
if (!existsSync23(GATEWAY_PID_FILE)) return null;
|
|
5181
5459
|
try {
|
|
5182
|
-
const pid = parseInt(
|
|
5460
|
+
const pid = parseInt(readFileSync18(GATEWAY_PID_FILE, "utf-8").trim(), 10);
|
|
5183
5461
|
return isNaN(pid) ? null : pid;
|
|
5184
5462
|
} catch {
|
|
5185
5463
|
return null;
|
|
@@ -5194,9 +5472,9 @@ function isProcessAlive(pid) {
|
|
|
5194
5472
|
}
|
|
5195
5473
|
}
|
|
5196
5474
|
function getConfiguredPort() {
|
|
5197
|
-
if (!
|
|
5475
|
+
if (!existsSync23(GATEWAY_CONFIG)) return DEFAULT_PORT;
|
|
5198
5476
|
try {
|
|
5199
|
-
const content =
|
|
5477
|
+
const content = readFileSync18(GATEWAY_CONFIG, "utf-8");
|
|
5200
5478
|
const match = content.match(/listen_port\s*=\s*(\d+)/);
|
|
5201
5479
|
return match ? parseInt(match[1], 10) : DEFAULT_PORT;
|
|
5202
5480
|
} catch {
|
|
@@ -5205,12 +5483,12 @@ function getConfiguredPort() {
|
|
|
5205
5483
|
}
|
|
5206
5484
|
function findGatewayBinary() {
|
|
5207
5485
|
const candidates = [
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5486
|
+
join22(process.cwd(), "target", "release", "imdl-gateway"),
|
|
5487
|
+
join22(process.cwd(), "..", "ai-gateway", "target", "release", "imdl-gateway"),
|
|
5488
|
+
join22(homedir16(), ".imdl", "bin", "imdl-gateway")
|
|
5211
5489
|
];
|
|
5212
5490
|
for (const path of candidates) {
|
|
5213
|
-
if (
|
|
5491
|
+
if (existsSync23(path)) return path;
|
|
5214
5492
|
}
|
|
5215
5493
|
try {
|
|
5216
5494
|
const result = execSync2("which imdl-gateway", { encoding: "utf-8" }).trim();
|
|
@@ -5220,8 +5498,8 @@ function findGatewayBinary() {
|
|
|
5220
5498
|
return null;
|
|
5221
5499
|
}
|
|
5222
5500
|
function ensureConfig(port) {
|
|
5223
|
-
if (
|
|
5224
|
-
if (!
|
|
5501
|
+
if (existsSync23(GATEWAY_CONFIG)) return;
|
|
5502
|
+
if (!existsSync23(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
|
|
5225
5503
|
const defaultToml = `# IMDL AI Gateway configuration
|
|
5226
5504
|
listen_host = "127.0.0.1"
|
|
5227
5505
|
listen_port = ${port}
|
|
@@ -5265,10 +5543,10 @@ function normalizeModule(input) {
|
|
|
5265
5543
|
return map[input.toLowerCase()] ?? null;
|
|
5266
5544
|
}
|
|
5267
5545
|
function loadGatewayToml() {
|
|
5268
|
-
if (!
|
|
5546
|
+
if (!existsSync23(GATEWAY_CONFIG)) {
|
|
5269
5547
|
ensureConfig(DEFAULT_PORT);
|
|
5270
5548
|
}
|
|
5271
|
-
return
|
|
5549
|
+
return readFileSync18(GATEWAY_CONFIG, "utf-8");
|
|
5272
5550
|
}
|
|
5273
5551
|
function saveGatewayToml(content) {
|
|
5274
5552
|
writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
|
|
@@ -5293,6 +5571,146 @@ function formatUptime(seconds) {
|
|
|
5293
5571
|
return `${h}h ${m}m`;
|
|
5294
5572
|
}
|
|
5295
5573
|
|
|
5574
|
+
// src/commands/uninstall.ts
|
|
5575
|
+
import { Command } from "commander";
|
|
5576
|
+
import { existsSync as existsSync24, readFileSync as readFileSync19, rmSync, unlinkSync as unlinkSync3 } from "fs";
|
|
5577
|
+
import { homedir as homedir17 } from "os";
|
|
5578
|
+
import { join as join23 } from "path";
|
|
5579
|
+
import pc13 from "picocolors";
|
|
5580
|
+
var uninstall = new Command("uninstall").description("Remove all IMDL components, hooks, and configuration").option("--keep-config", "Keep ~/.imdl config directory").option("--force", "Skip confirmation prompt").action(async (opts) => {
|
|
5581
|
+
const home = homedir17();
|
|
5582
|
+
const imdlDir = join23(home, ".imdl");
|
|
5583
|
+
console.log("");
|
|
5584
|
+
console.log(pc13.bold(" Frostbridge IMDL \u2014 Uninstaller"));
|
|
5585
|
+
console.log("");
|
|
5586
|
+
const removed = [];
|
|
5587
|
+
const skipped = [];
|
|
5588
|
+
const claudeSettings = join23(home, ".claude", "settings.json");
|
|
5589
|
+
if (existsSync24(claudeSettings)) {
|
|
5590
|
+
try {
|
|
5591
|
+
const raw = readFileSync19(claudeSettings, "utf8");
|
|
5592
|
+
const settings = JSON.parse(raw);
|
|
5593
|
+
let modified = false;
|
|
5594
|
+
for (const hookType of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
5595
|
+
if (settings.hooks?.[hookType]) {
|
|
5596
|
+
const before = settings.hooks[hookType].length;
|
|
5597
|
+
settings.hooks[hookType] = settings.hooks[hookType].filter(
|
|
5598
|
+
(h) => !h.hooks?.some((hh) => hh.command?.includes("imdl"))
|
|
5599
|
+
);
|
|
5600
|
+
if (settings.hooks[hookType].length < before) modified = true;
|
|
5601
|
+
if (settings.hooks[hookType].length === 0) delete settings.hooks[hookType];
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
5605
|
+
if (modified) {
|
|
5606
|
+
const { writeFileSync: writeFileSync12 } = await import("fs");
|
|
5607
|
+
writeFileSync12(claudeSettings, JSON.stringify(settings, null, 2));
|
|
5608
|
+
removed.push("Claude Code hooks");
|
|
5609
|
+
}
|
|
5610
|
+
} catch {
|
|
5611
|
+
skipped.push("Claude Code hooks (could not parse settings.json)");
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
const cursorSettings = join23(home, ".cursor", "settings.json");
|
|
5615
|
+
if (existsSync24(cursorSettings)) {
|
|
5616
|
+
try {
|
|
5617
|
+
const raw = readFileSync19(cursorSettings, "utf8");
|
|
5618
|
+
const settings = JSON.parse(raw);
|
|
5619
|
+
let modified = false;
|
|
5620
|
+
if (settings.hooks) {
|
|
5621
|
+
for (const hookType of Object.keys(settings.hooks)) {
|
|
5622
|
+
const before = settings.hooks[hookType]?.length || 0;
|
|
5623
|
+
if (Array.isArray(settings.hooks[hookType])) {
|
|
5624
|
+
settings.hooks[hookType] = settings.hooks[hookType].filter(
|
|
5625
|
+
(h) => !h.hooks?.some((hh) => hh.command?.includes("imdl"))
|
|
5626
|
+
);
|
|
5627
|
+
if (settings.hooks[hookType].length < before) modified = true;
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
if (modified) {
|
|
5632
|
+
const { writeFileSync: writeFileSync12 } = await import("fs");
|
|
5633
|
+
writeFileSync12(cursorSettings, JSON.stringify(settings, null, 2));
|
|
5634
|
+
removed.push("Cursor hooks");
|
|
5635
|
+
}
|
|
5636
|
+
} catch {
|
|
5637
|
+
skipped.push("Cursor hooks (could not parse settings.json)");
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
for (const rc of [".bashrc", ".zshrc", ".profile"]) {
|
|
5641
|
+
const rcPath = join23(home, rc);
|
|
5642
|
+
if (existsSync24(rcPath)) {
|
|
5643
|
+
try {
|
|
5644
|
+
const content = readFileSync19(rcPath, "utf8");
|
|
5645
|
+
if (content.includes("imdl") || content.includes("frostbridge")) {
|
|
5646
|
+
const lines = content.split("\n");
|
|
5647
|
+
const filtered = lines.filter(
|
|
5648
|
+
(l) => !l.includes("imdl") && !l.includes("frostbridge") && !l.includes("# Added by Frostbridge")
|
|
5649
|
+
);
|
|
5650
|
+
if (filtered.length < lines.length) {
|
|
5651
|
+
const { writeFileSync: writeFileSync12 } = await import("fs");
|
|
5652
|
+
writeFileSync12(rcPath, filtered.join("\n"));
|
|
5653
|
+
removed.push(`Shell rc entries (${rc})`);
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
} catch {
|
|
5657
|
+
}
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
try {
|
|
5661
|
+
const pidFile = join23(imdlDir, "daemon.pid");
|
|
5662
|
+
if (existsSync24(pidFile)) {
|
|
5663
|
+
const pid = parseInt(readFileSync19(pidFile, "utf8").trim());
|
|
5664
|
+
if (pid > 0) {
|
|
5665
|
+
try {
|
|
5666
|
+
process.kill(pid, "SIGTERM");
|
|
5667
|
+
} catch {
|
|
5668
|
+
}
|
|
5669
|
+
unlinkSync3(pidFile);
|
|
5670
|
+
removed.push("Background daemon (killed)");
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
} catch {
|
|
5674
|
+
}
|
|
5675
|
+
if (!opts.keepConfig && existsSync24(imdlDir)) {
|
|
5676
|
+
rmSync(imdlDir, { recursive: true, force: true });
|
|
5677
|
+
removed.push("~/.imdl config directory");
|
|
5678
|
+
} else if (opts.keepConfig) {
|
|
5679
|
+
skipped.push("~/.imdl (--keep-config)");
|
|
5680
|
+
}
|
|
5681
|
+
const bufferDir = join23(home, ".imdl", "buffer");
|
|
5682
|
+
if (existsSync24(bufferDir)) {
|
|
5683
|
+
rmSync(bufferDir, { recursive: true, force: true });
|
|
5684
|
+
removed.push("Event buffer");
|
|
5685
|
+
}
|
|
5686
|
+
console.log(pc13.dim(" Uninstalling npm packages..."));
|
|
5687
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
5688
|
+
const packages = ["@frostbridge/imdl", "@frostbridge/imdl-shell-wrapper", "@frostbridge/imdl-mcp-proxy"];
|
|
5689
|
+
for (const pkg of packages) {
|
|
5690
|
+
try {
|
|
5691
|
+
execSync3(`npm uninstall -g ${pkg} 2>/dev/null`, { stdio: "pipe" });
|
|
5692
|
+
removed.push(`npm: ${pkg}`);
|
|
5693
|
+
} catch {
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
console.log("");
|
|
5697
|
+
if (removed.length > 0) {
|
|
5698
|
+
console.log(pc13.green(pc13.bold(" Removed:")));
|
|
5699
|
+
for (const item of removed) {
|
|
5700
|
+
console.log(pc13.green(` \u2713 ${item}`));
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
if (skipped.length > 0) {
|
|
5704
|
+
console.log(pc13.yellow(pc13.bold(" Skipped:")));
|
|
5705
|
+
for (const item of skipped) {
|
|
5706
|
+
console.log(pc13.yellow(` \u25CB ${item}`));
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
console.log("");
|
|
5710
|
+
console.log(pc13.dim(" IMDL has been completely removed from this machine."));
|
|
5711
|
+
console.log("");
|
|
5712
|
+
});
|
|
5713
|
+
|
|
5296
5714
|
// src/bifrost/compiler.ts
|
|
5297
5715
|
function globToRegex(glob) {
|
|
5298
5716
|
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -5601,18 +6019,18 @@ function ruleTypeToCategory(type) {
|
|
|
5601
6019
|
}
|
|
5602
6020
|
|
|
5603
6021
|
// src/transport/sync.ts
|
|
5604
|
-
import { readFileSync as
|
|
5605
|
-
import { join as
|
|
6022
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync11, existsSync as existsSync25 } from "fs";
|
|
6023
|
+
import { join as join24 } from "path";
|
|
5606
6024
|
var SYNC_INTERVAL = 6e4;
|
|
5607
6025
|
var SYNC_TIMEOUT = 3e3;
|
|
5608
6026
|
function getSyncStateFile() {
|
|
5609
|
-
return
|
|
6027
|
+
return join24(getImdlDir(), "last_sync.json");
|
|
5610
6028
|
}
|
|
5611
6029
|
function getLastSyncTime() {
|
|
5612
6030
|
try {
|
|
5613
6031
|
const file = getSyncStateFile();
|
|
5614
|
-
if (
|
|
5615
|
-
const data = JSON.parse(
|
|
6032
|
+
if (existsSync25(file)) {
|
|
6033
|
+
const data = JSON.parse(readFileSync20(file, "utf-8"));
|
|
5616
6034
|
return data.lastSync || 0;
|
|
5617
6035
|
}
|
|
5618
6036
|
} catch {
|
|
@@ -6023,7 +6441,7 @@ function getSafeAlternative(toolName, toolInput) {
|
|
|
6023
6441
|
}
|
|
6024
6442
|
|
|
6025
6443
|
// src/index.ts
|
|
6026
|
-
var program = new
|
|
6444
|
+
var program = new Command2();
|
|
6027
6445
|
program.name("imdl").description("IMDL \u2014 Intelligent Mediation & Detection Layer. AI agent security.").version("0.1.0");
|
|
6028
6446
|
program.command("scan").description("Scan MCP server configs for security risks").option("-p, --path <path>", "Path to MCP config file").option("-u, --url <url>", "GitHub URL or org/repo to scan").option("--json", "Output as JSON").option("--no-color", "Disable colored output").option("-q, --quiet", "Only show warnings and errors").option("-d, --deep", "Deep scan: static code analysis + GitHub issue scanning").action(scanCommand);
|
|
6029
6447
|
program.command("init").description("Detect AI agents and install monitoring hooks").option("-t, --token <token>", "Invite token to join a team").option("--team-token <token>", "Alias for --token").option("--api <url>", "API URL to connect to").option("--non-interactive", "Skip all prompts and use defaults").action((opts) => initCommand({ teamToken: opts.token || opts.teamToken, apiUrl: opts.api, nonInteractive: opts.nonInteractive }));
|
|
@@ -6050,5 +6468,6 @@ gwCmd.command("enable <module>").description("Enable a gateway module (secrets,
|
|
|
6050
6468
|
gwCmd.command("disable <module>").description("Disable a gateway module").action((module) => gatewayDisableCommand({ module }));
|
|
6051
6469
|
gwCmd.command("alerts").description("Show recent gateway security alerts").option("-n, --limit <count>", "Number of alerts to show", "20").option("--json", "Output as JSON").action(gatewayAlertsCommand);
|
|
6052
6470
|
gwCmd.command("cost").description("Show AI spend and usage breakdown").option("--json", "Output as JSON").action(gatewayCostCommand);
|
|
6471
|
+
program.addCommand(uninstall);
|
|
6053
6472
|
program.command("hook <type>").description("Handle hook events from AI agents (internal)").action(handleHook);
|
|
6054
6473
|
program.parse();
|