mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
feat(mitm): add detection modules for 8 agents (F3)
Filesystem-only installation probes for antigravity, kiro, copilot,
codex, cursor, zed, claude-code, and open-code. detectAgent(id)
dispatcher returns {installed, path?} without ever spawning a shell
or interpolating runtime paths (Hard Rule #13).
Trae has no detector — its entry in the dispatch table returns
{installed: false} until upstream viability is confirmed.
This commit is contained in:
33
src/mitm/detection/antigravity.ts
Normal file
33
src/mitm/detection/antigravity.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Antigravity IDE installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
// macOS
|
||||
"/Applications/Antigravity.app",
|
||||
path.join(HOME, "Applications", "Antigravity.app"),
|
||||
// Linux (AppImage / system install)
|
||||
"/usr/bin/antigravity",
|
||||
"/usr/local/bin/antigravity",
|
||||
path.join(HOME, ".local", "bin", "antigravity"),
|
||||
// Windows
|
||||
path.join(
|
||||
process.env.LOCALAPPDATA ?? path.join(HOME, "AppData", "Local"),
|
||||
"Programs",
|
||||
"Antigravity",
|
||||
"Antigravity.exe"
|
||||
),
|
||||
];
|
||||
|
||||
export function detectAntigravity(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
29
src/mitm/detection/claudeCode.ts
Normal file
29
src/mitm/detection/claudeCode.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Claude Code (Anthropic CLI) installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
"/usr/local/bin/claude",
|
||||
"/usr/bin/claude",
|
||||
path.join(HOME, ".local", "bin", "claude"),
|
||||
path.join(HOME, ".npm-global", "bin", "claude"),
|
||||
path.join(HOME, ".claude"),
|
||||
path.join(
|
||||
process.env.APPDATA ?? path.join(HOME, "AppData", "Roaming"),
|
||||
"npm",
|
||||
"claude.cmd"
|
||||
),
|
||||
];
|
||||
|
||||
export function detectClaudeCode(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
29
src/mitm/detection/codex.ts
Normal file
29
src/mitm/detection/codex.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* OpenAI Codex CLI installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
"/usr/local/bin/codex",
|
||||
"/usr/bin/codex",
|
||||
path.join(HOME, ".local", "bin", "codex"),
|
||||
path.join(HOME, ".npm-global", "bin", "codex"),
|
||||
path.join(HOME, "node_modules", ".bin", "codex"),
|
||||
path.join(
|
||||
process.env.APPDATA ?? path.join(HOME, "AppData", "Roaming"),
|
||||
"npm",
|
||||
"codex.cmd"
|
||||
),
|
||||
];
|
||||
|
||||
export function detectCodex(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
39
src/mitm/detection/copilot.ts
Normal file
39
src/mitm/detection/copilot.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* GitHub Copilot installation detection.
|
||||
*
|
||||
* Detection strategy: look for the Copilot extension folder inside the user's
|
||||
* VS Code (or fork) extensions directory. Purely filesystem-based — no shell
|
||||
* interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
|
||||
const EXTENSIONS_DIRS = [
|
||||
path.join(HOME, ".vscode", "extensions"),
|
||||
path.join(HOME, ".vscode-insiders", "extensions"),
|
||||
path.join(HOME, ".cursor", "extensions"),
|
||||
];
|
||||
|
||||
export function detectCopilot(): DetectionResult {
|
||||
for (const dir of EXTENSIONS_DIRS) {
|
||||
try {
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
const entries = fs.readdirSync(dir);
|
||||
for (const name of entries) {
|
||||
// Copilot extensions are named like `github.copilot-1.x.x`,
|
||||
// `github.copilot-chat-...`. Match by prefix only.
|
||||
const lower = name.toLowerCase();
|
||||
if (lower.startsWith("github.copilot")) {
|
||||
return { installed: true, path: path.join(dir, name) };
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Permission or transient fs error — skip this directory.
|
||||
}
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
31
src/mitm/detection/cursor.ts
Normal file
31
src/mitm/detection/cursor.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Cursor IDE installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
"/Applications/Cursor.app",
|
||||
path.join(HOME, "Applications", "Cursor.app"),
|
||||
"/usr/bin/cursor",
|
||||
"/usr/local/bin/cursor",
|
||||
path.join(HOME, ".local", "bin", "cursor"),
|
||||
path.join(HOME, ".cursor"),
|
||||
path.join(
|
||||
process.env.LOCALAPPDATA ?? path.join(HOME, "AppData", "Local"),
|
||||
"Programs",
|
||||
"cursor",
|
||||
"Cursor.exe"
|
||||
),
|
||||
];
|
||||
|
||||
export function detectCursor(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
53
src/mitm/detection/index.ts
Normal file
53
src/mitm/detection/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Detection dispatcher.
|
||||
*
|
||||
* `detectAgent(id)` returns whether the given AgentBridge target is installed
|
||||
* on the current machine. All detection probes are filesystem-only — they
|
||||
* never spawn shells or interpolate runtime paths (Hard Rule #13).
|
||||
*
|
||||
* Trae is intentionally absent from the dispatch table: its viability is still
|
||||
* under investigation, so callers receive `{ installed: false }` until the
|
||||
* upstream surface is confirmed (see `targets/trae.ts`).
|
||||
*/
|
||||
import type { AgentId, DetectionResult } from "../types";
|
||||
import { detectAntigravity } from "./antigravity";
|
||||
import { detectKiro } from "./kiro";
|
||||
import { detectCopilot } from "./copilot";
|
||||
import { detectCodex } from "./codex";
|
||||
import { detectCursor } from "./cursor";
|
||||
import { detectZed } from "./zed";
|
||||
import { detectClaudeCode } from "./claudeCode";
|
||||
import { detectOpenCode } from "./openCode";
|
||||
|
||||
export const DETECTORS: Record<AgentId, () => DetectionResult> = {
|
||||
antigravity: detectAntigravity,
|
||||
kiro: detectKiro,
|
||||
copilot: detectCopilot,
|
||||
codex: detectCodex,
|
||||
cursor: detectCursor,
|
||||
zed: detectZed,
|
||||
"claude-code": detectClaudeCode,
|
||||
"open-code": detectOpenCode,
|
||||
trae: () => ({ installed: false }),
|
||||
};
|
||||
|
||||
export function detectAgent(id: AgentId): DetectionResult {
|
||||
const fn = DETECTORS[id];
|
||||
if (!fn) return { installed: false };
|
||||
try {
|
||||
return fn();
|
||||
} catch {
|
||||
return { installed: false };
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
detectAntigravity,
|
||||
detectKiro,
|
||||
detectCopilot,
|
||||
detectCodex,
|
||||
detectCursor,
|
||||
detectZed,
|
||||
detectClaudeCode,
|
||||
detectOpenCode,
|
||||
};
|
||||
30
src/mitm/detection/kiro.ts
Normal file
30
src/mitm/detection/kiro.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Kiro IDE installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
"/Applications/Kiro.app",
|
||||
path.join(HOME, "Applications", "Kiro.app"),
|
||||
"/usr/bin/kiro",
|
||||
"/usr/local/bin/kiro",
|
||||
path.join(HOME, ".local", "bin", "kiro"),
|
||||
path.join(
|
||||
process.env.LOCALAPPDATA ?? path.join(HOME, "AppData", "Local"),
|
||||
"Programs",
|
||||
"Kiro",
|
||||
"Kiro.exe"
|
||||
),
|
||||
];
|
||||
|
||||
export function detectKiro(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
32
src/mitm/detection/openCode.ts
Normal file
32
src/mitm/detection/openCode.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* OpenCode installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
"/Applications/OpenCode.app",
|
||||
path.join(HOME, "Applications", "OpenCode.app"),
|
||||
"/usr/bin/opencode",
|
||||
"/usr/local/bin/opencode",
|
||||
path.join(HOME, ".local", "bin", "opencode"),
|
||||
path.join(HOME, ".opencode"),
|
||||
path.join(HOME, ".config", "opencode"),
|
||||
path.join(
|
||||
process.env.LOCALAPPDATA ?? path.join(HOME, "AppData", "Local"),
|
||||
"Programs",
|
||||
"OpenCode",
|
||||
"OpenCode.exe"
|
||||
),
|
||||
];
|
||||
|
||||
export function detectOpenCode(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
26
src/mitm/detection/zed.ts
Normal file
26
src/mitm/detection/zed.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Zed editor installation detection.
|
||||
* Purely filesystem-based — no shell interpolation (Hard Rule #13).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DetectionResult } from "../types";
|
||||
|
||||
const HOME = os.homedir();
|
||||
const PATHS = [
|
||||
"/Applications/Zed.app",
|
||||
path.join(HOME, "Applications", "Zed.app"),
|
||||
"/usr/bin/zed",
|
||||
"/usr/local/bin/zed",
|
||||
path.join(HOME, ".local", "bin", "zed"),
|
||||
path.join(HOME, ".local", "share", "zed"),
|
||||
path.join(HOME, ".config", "zed"),
|
||||
];
|
||||
|
||||
export function detectZed(): DetectionResult {
|
||||
for (const p of PATHS) {
|
||||
if (fs.existsSync(p)) return { installed: true, path: p };
|
||||
}
|
||||
return { installed: false };
|
||||
}
|
||||
Reference in New Issue
Block a user