@frostbridge/imdl 0.1.12 → 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 +439 -76
- 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"));
|
|
@@ -3926,9 +4148,9 @@ async function daemonCommand() {
|
|
|
3926
4148
|
setInterval(tick, INTERVAL_MS);
|
|
3927
4149
|
}
|
|
3928
4150
|
async function ingestAgentEvents() {
|
|
3929
|
-
const agentLog =
|
|
3930
|
-
if (!
|
|
3931
|
-
const content =
|
|
4151
|
+
const agentLog = join19(getBufferDir(), "agent-events.ndjson");
|
|
4152
|
+
if (!existsSync20(agentLog)) return 0;
|
|
4153
|
+
const content = readFileSync15(agentLog, "utf-8").trim();
|
|
3932
4154
|
if (!content) return 0;
|
|
3933
4155
|
const config = loadConfig();
|
|
3934
4156
|
const lines = content.split("\n");
|
|
@@ -3981,9 +4203,9 @@ async function ingestAgentEvents() {
|
|
|
3981
4203
|
return sent;
|
|
3982
4204
|
}
|
|
3983
4205
|
async function ingestShellEvents() {
|
|
3984
|
-
const shellLog =
|
|
3985
|
-
if (!
|
|
3986
|
-
const content =
|
|
4206
|
+
const shellLog = join19(getBufferDir(), "shell-events.ndjson");
|
|
4207
|
+
if (!existsSync20(shellLog)) return 0;
|
|
4208
|
+
const content = readFileSync15(shellLog, "utf-8").trim();
|
|
3987
4209
|
if (!content) return 0;
|
|
3988
4210
|
const config = loadConfig();
|
|
3989
4211
|
const lines = content.split("\n");
|
|
@@ -4088,8 +4310,8 @@ async function requestCommand(options) {
|
|
|
4088
4310
|
// src/commands/lock.ts
|
|
4089
4311
|
import pc9 from "picocolors";
|
|
4090
4312
|
import { createHash as createHash2 } from "crypto";
|
|
4091
|
-
import { readFileSync as
|
|
4092
|
-
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";
|
|
4093
4315
|
var LOCKFILE_NAME = "mcp-lock.json";
|
|
4094
4316
|
function computeIntegrity(server, identity) {
|
|
4095
4317
|
const payload = JSON.stringify({
|
|
@@ -4104,7 +4326,7 @@ function computeIntegrity(server, identity) {
|
|
|
4104
4326
|
}
|
|
4105
4327
|
function getLockfilePath(customPath) {
|
|
4106
4328
|
if (customPath) return resolve3(customPath);
|
|
4107
|
-
return
|
|
4329
|
+
return join20(process.cwd(), LOCKFILE_NAME);
|
|
4108
4330
|
}
|
|
4109
4331
|
async function lockCommand(options) {
|
|
4110
4332
|
const detection = await detectMCPConfigs(options.path);
|
|
@@ -4174,7 +4396,7 @@ async function lockCommand(options) {
|
|
|
4174
4396
|
}
|
|
4175
4397
|
async function verifyCommand(options) {
|
|
4176
4398
|
const lockPath = getLockfilePath(options.path);
|
|
4177
|
-
if (!
|
|
4399
|
+
if (!existsSync21(lockPath)) {
|
|
4178
4400
|
if (options.json) {
|
|
4179
4401
|
console.log(JSON.stringify({ error: "No lockfile found. Run `imdl lock` first.", path: lockPath }));
|
|
4180
4402
|
} else {
|
|
@@ -4186,7 +4408,7 @@ async function verifyCommand(options) {
|
|
|
4186
4408
|
}
|
|
4187
4409
|
let lockfile;
|
|
4188
4410
|
try {
|
|
4189
|
-
lockfile = JSON.parse(
|
|
4411
|
+
lockfile = JSON.parse(readFileSync16(lockPath, "utf-8"));
|
|
4190
4412
|
} catch {
|
|
4191
4413
|
console.log(pc9.red("Failed to parse lockfile."));
|
|
4192
4414
|
process.exit(1);
|
|
@@ -4595,12 +4817,12 @@ async function checkCompliance(developerId) {
|
|
|
4595
4817
|
}
|
|
4596
4818
|
|
|
4597
4819
|
// src/permissions/fixer.ts
|
|
4598
|
-
import { existsSync as
|
|
4599
|
-
import { join as
|
|
4600
|
-
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";
|
|
4601
4823
|
function buildFixesFromChanges(changes) {
|
|
4602
4824
|
const fixes = [];
|
|
4603
|
-
const home =
|
|
4825
|
+
const home = homedir15();
|
|
4604
4826
|
for (const change of changes) {
|
|
4605
4827
|
const fix = buildFixForAgent(change, home);
|
|
4606
4828
|
if (fix) fixes.push(fix);
|
|
@@ -4610,10 +4832,10 @@ function buildFixesFromChanges(changes) {
|
|
|
4610
4832
|
function buildFixForAgent(change, home) {
|
|
4611
4833
|
const { agentType, category, action, targetGrant } = change;
|
|
4612
4834
|
if (agentType === "claude-code") {
|
|
4613
|
-
const configPath =
|
|
4614
|
-
if (!
|
|
4835
|
+
const configPath = join21(home, ".claude", "settings.json");
|
|
4836
|
+
if (!existsSync22(configPath)) return null;
|
|
4615
4837
|
try {
|
|
4616
|
-
const settings = JSON.parse(
|
|
4838
|
+
const settings = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4617
4839
|
const tools = settings.allowedTools || [];
|
|
4618
4840
|
if (category === "shell") {
|
|
4619
4841
|
if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
|
|
@@ -4641,10 +4863,10 @@ function buildFixForAgent(change, home) {
|
|
|
4641
4863
|
}
|
|
4642
4864
|
}
|
|
4643
4865
|
if (agentType === "cursor") {
|
|
4644
|
-
const configPath =
|
|
4645
|
-
if (!
|
|
4866
|
+
const configPath = join21(home, ".cursor", "settings.json");
|
|
4867
|
+
if (!existsSync22(configPath)) return null;
|
|
4646
4868
|
try {
|
|
4647
|
-
const settings = JSON.parse(
|
|
4869
|
+
const settings = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4648
4870
|
if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
|
|
4649
4871
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
|
|
4650
4872
|
}
|
|
@@ -4653,10 +4875,10 @@ function buildFixForAgent(change, home) {
|
|
|
4653
4875
|
}
|
|
4654
4876
|
}
|
|
4655
4877
|
if (agentType === "codex") {
|
|
4656
|
-
const configPath =
|
|
4657
|
-
if (!
|
|
4878
|
+
const configPath = join21(home, ".codex", "config.json");
|
|
4879
|
+
if (!existsSync22(configPath)) return null;
|
|
4658
4880
|
try {
|
|
4659
|
-
const config = JSON.parse(
|
|
4881
|
+
const config = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4660
4882
|
if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
|
|
4661
4883
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
|
|
4662
4884
|
}
|
|
@@ -4665,10 +4887,10 @@ function buildFixForAgent(change, home) {
|
|
|
4665
4887
|
}
|
|
4666
4888
|
}
|
|
4667
4889
|
if (agentType === "copilot") {
|
|
4668
|
-
const configPath =
|
|
4669
|
-
if (!
|
|
4890
|
+
const configPath = join21(home, "Library", "Application Support", "Code", "User", "settings.json");
|
|
4891
|
+
if (!existsSync22(configPath)) return null;
|
|
4670
4892
|
try {
|
|
4671
|
-
const settings = JSON.parse(
|
|
4893
|
+
const settings = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4672
4894
|
if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
|
|
4673
4895
|
return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
|
|
4674
4896
|
}
|
|
@@ -4678,10 +4900,10 @@ function buildFixForAgent(change, home) {
|
|
|
4678
4900
|
}
|
|
4679
4901
|
if (agentType === "windsurf") {
|
|
4680
4902
|
if (category === "mcp_tools") {
|
|
4681
|
-
const configPath =
|
|
4682
|
-
if (!
|
|
4903
|
+
const configPath = join21(home, ".windsurf", "mcp.json");
|
|
4904
|
+
if (!existsSync22(configPath)) return null;
|
|
4683
4905
|
try {
|
|
4684
|
-
const config = JSON.parse(
|
|
4906
|
+
const config = JSON.parse(readFileSync17(configPath, "utf-8"));
|
|
4685
4907
|
const servers = Object.keys(config.mcpServers || {});
|
|
4686
4908
|
if (servers.length === 0) return null;
|
|
4687
4909
|
return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
|
|
@@ -4694,7 +4916,7 @@ function buildFixForAgent(change, home) {
|
|
|
4694
4916
|
}
|
|
4695
4917
|
function applyFix(fix) {
|
|
4696
4918
|
try {
|
|
4697
|
-
const content =
|
|
4919
|
+
const content = readFileSync17(fix.configPath, "utf-8");
|
|
4698
4920
|
const config = JSON.parse(content);
|
|
4699
4921
|
let modified = false;
|
|
4700
4922
|
if (fix.agentType === "claude-code") {
|
|
@@ -4985,13 +5207,13 @@ function grantLevel(grant) {
|
|
|
4985
5207
|
|
|
4986
5208
|
// src/commands/gateway.ts
|
|
4987
5209
|
import pc12 from "picocolors";
|
|
4988
|
-
import { existsSync as
|
|
4989
|
-
import { join as
|
|
4990
|
-
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";
|
|
4991
5213
|
import { execSync as execSync2, spawn } from "child_process";
|
|
4992
|
-
var IMDL_DIR3 =
|
|
4993
|
-
var GATEWAY_CONFIG =
|
|
4994
|
-
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");
|
|
4995
5217
|
var DEFAULT_PORT = 9443;
|
|
4996
5218
|
async function gatewayCommand(opts) {
|
|
4997
5219
|
const status = await getGatewayStatus();
|
|
@@ -5066,7 +5288,7 @@ async function gatewayStartCommand(opts) {
|
|
|
5066
5288
|
});
|
|
5067
5289
|
child.unref();
|
|
5068
5290
|
if (child.pid) {
|
|
5069
|
-
if (!
|
|
5291
|
+
if (!existsSync23(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
|
|
5070
5292
|
writeFileSync10(GATEWAY_PID_FILE, String(child.pid));
|
|
5071
5293
|
console.log(pc12.green(`Gateway started (PID ${child.pid}, port ${port})`));
|
|
5072
5294
|
console.log(pc12.dim(` Config: ${GATEWAY_CONFIG}`));
|
|
@@ -5094,8 +5316,8 @@ async function gatewayStopCommand() {
|
|
|
5094
5316
|
console.log(pc12.yellow("Could not stop gateway \u2014 process may have already exited."));
|
|
5095
5317
|
}
|
|
5096
5318
|
try {
|
|
5097
|
-
const { unlinkSync:
|
|
5098
|
-
|
|
5319
|
+
const { unlinkSync: unlinkSync4 } = await import("fs");
|
|
5320
|
+
unlinkSync4(GATEWAY_PID_FILE);
|
|
5099
5321
|
} catch {
|
|
5100
5322
|
}
|
|
5101
5323
|
}
|
|
@@ -5126,12 +5348,12 @@ async function gatewayDisableCommand(opts) {
|
|
|
5126
5348
|
console.log(pc12.dim("Restart gateway for changes to take effect."));
|
|
5127
5349
|
}
|
|
5128
5350
|
async function gatewayAlertsCommand(opts) {
|
|
5129
|
-
const logPath =
|
|
5130
|
-
if (!
|
|
5351
|
+
const logPath = join22(IMDL_DIR3, "gateway-alerts.jsonl");
|
|
5352
|
+
if (!existsSync23(logPath)) {
|
|
5131
5353
|
console.log(pc12.dim("No alerts logged yet."));
|
|
5132
5354
|
return;
|
|
5133
5355
|
}
|
|
5134
|
-
const lines =
|
|
5356
|
+
const lines = readFileSync18(logPath, "utf-8").split("\n").filter(Boolean);
|
|
5135
5357
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
|
|
5136
5358
|
const recent = lines.slice(-limit);
|
|
5137
5359
|
if (opts.json) {
|
|
@@ -5233,9 +5455,9 @@ async function getGatewayStatus() {
|
|
|
5233
5455
|
return { running: true, pid, port, stats, config };
|
|
5234
5456
|
}
|
|
5235
5457
|
function getRunningPid() {
|
|
5236
|
-
if (!
|
|
5458
|
+
if (!existsSync23(GATEWAY_PID_FILE)) return null;
|
|
5237
5459
|
try {
|
|
5238
|
-
const pid = parseInt(
|
|
5460
|
+
const pid = parseInt(readFileSync18(GATEWAY_PID_FILE, "utf-8").trim(), 10);
|
|
5239
5461
|
return isNaN(pid) ? null : pid;
|
|
5240
5462
|
} catch {
|
|
5241
5463
|
return null;
|
|
@@ -5250,9 +5472,9 @@ function isProcessAlive(pid) {
|
|
|
5250
5472
|
}
|
|
5251
5473
|
}
|
|
5252
5474
|
function getConfiguredPort() {
|
|
5253
|
-
if (!
|
|
5475
|
+
if (!existsSync23(GATEWAY_CONFIG)) return DEFAULT_PORT;
|
|
5254
5476
|
try {
|
|
5255
|
-
const content =
|
|
5477
|
+
const content = readFileSync18(GATEWAY_CONFIG, "utf-8");
|
|
5256
5478
|
const match = content.match(/listen_port\s*=\s*(\d+)/);
|
|
5257
5479
|
return match ? parseInt(match[1], 10) : DEFAULT_PORT;
|
|
5258
5480
|
} catch {
|
|
@@ -5261,12 +5483,12 @@ function getConfiguredPort() {
|
|
|
5261
5483
|
}
|
|
5262
5484
|
function findGatewayBinary() {
|
|
5263
5485
|
const candidates = [
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
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")
|
|
5267
5489
|
];
|
|
5268
5490
|
for (const path of candidates) {
|
|
5269
|
-
if (
|
|
5491
|
+
if (existsSync23(path)) return path;
|
|
5270
5492
|
}
|
|
5271
5493
|
try {
|
|
5272
5494
|
const result = execSync2("which imdl-gateway", { encoding: "utf-8" }).trim();
|
|
@@ -5276,8 +5498,8 @@ function findGatewayBinary() {
|
|
|
5276
5498
|
return null;
|
|
5277
5499
|
}
|
|
5278
5500
|
function ensureConfig(port) {
|
|
5279
|
-
if (
|
|
5280
|
-
if (!
|
|
5501
|
+
if (existsSync23(GATEWAY_CONFIG)) return;
|
|
5502
|
+
if (!existsSync23(IMDL_DIR3)) mkdirSync3(IMDL_DIR3, { recursive: true });
|
|
5281
5503
|
const defaultToml = `# IMDL AI Gateway configuration
|
|
5282
5504
|
listen_host = "127.0.0.1"
|
|
5283
5505
|
listen_port = ${port}
|
|
@@ -5321,10 +5543,10 @@ function normalizeModule(input) {
|
|
|
5321
5543
|
return map[input.toLowerCase()] ?? null;
|
|
5322
5544
|
}
|
|
5323
5545
|
function loadGatewayToml() {
|
|
5324
|
-
if (!
|
|
5546
|
+
if (!existsSync23(GATEWAY_CONFIG)) {
|
|
5325
5547
|
ensureConfig(DEFAULT_PORT);
|
|
5326
5548
|
}
|
|
5327
|
-
return
|
|
5549
|
+
return readFileSync18(GATEWAY_CONFIG, "utf-8");
|
|
5328
5550
|
}
|
|
5329
5551
|
function saveGatewayToml(content) {
|
|
5330
5552
|
writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
|
|
@@ -5349,6 +5571,146 @@ function formatUptime(seconds) {
|
|
|
5349
5571
|
return `${h}h ${m}m`;
|
|
5350
5572
|
}
|
|
5351
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
|
+
|
|
5352
5714
|
// src/bifrost/compiler.ts
|
|
5353
5715
|
function globToRegex(glob) {
|
|
5354
5716
|
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -5657,18 +6019,18 @@ function ruleTypeToCategory(type) {
|
|
|
5657
6019
|
}
|
|
5658
6020
|
|
|
5659
6021
|
// src/transport/sync.ts
|
|
5660
|
-
import { readFileSync as
|
|
5661
|
-
import { join as
|
|
6022
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync11, existsSync as existsSync25 } from "fs";
|
|
6023
|
+
import { join as join24 } from "path";
|
|
5662
6024
|
var SYNC_INTERVAL = 6e4;
|
|
5663
6025
|
var SYNC_TIMEOUT = 3e3;
|
|
5664
6026
|
function getSyncStateFile() {
|
|
5665
|
-
return
|
|
6027
|
+
return join24(getImdlDir(), "last_sync.json");
|
|
5666
6028
|
}
|
|
5667
6029
|
function getLastSyncTime() {
|
|
5668
6030
|
try {
|
|
5669
6031
|
const file = getSyncStateFile();
|
|
5670
|
-
if (
|
|
5671
|
-
const data = JSON.parse(
|
|
6032
|
+
if (existsSync25(file)) {
|
|
6033
|
+
const data = JSON.parse(readFileSync20(file, "utf-8"));
|
|
5672
6034
|
return data.lastSync || 0;
|
|
5673
6035
|
}
|
|
5674
6036
|
} catch {
|
|
@@ -6079,7 +6441,7 @@ function getSafeAlternative(toolName, toolInput) {
|
|
|
6079
6441
|
}
|
|
6080
6442
|
|
|
6081
6443
|
// src/index.ts
|
|
6082
|
-
var program = new
|
|
6444
|
+
var program = new Command2();
|
|
6083
6445
|
program.name("imdl").description("IMDL \u2014 Intelligent Mediation & Detection Layer. AI agent security.").version("0.1.0");
|
|
6084
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);
|
|
6085
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 }));
|
|
@@ -6106,5 +6468,6 @@ gwCmd.command("enable <module>").description("Enable a gateway module (secrets,
|
|
|
6106
6468
|
gwCmd.command("disable <module>").description("Disable a gateway module").action((module) => gatewayDisableCommand({ module }));
|
|
6107
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);
|
|
6108
6470
|
gwCmd.command("cost").description("Show AI spend and usage breakdown").option("--json", "Output as JSON").action(gatewayCostCommand);
|
|
6471
|
+
program.addCommand(uninstall);
|
|
6109
6472
|
program.command("hook <type>").description("Handle hook events from AI agents (internal)").action(handleHook);
|
|
6110
6473
|
program.parse();
|