feat(omo-codex): embed ast-grep MCP for Codex
This commit is contained in:
@@ -1,8 +1,13 @@
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
|
"ast_grep": {
|
||||||
|
"command": "node",
|
||||||
|
"args": ["../../ast-grep-mcp/dist/cli.js", "mcp"],
|
||||||
|
"cwd": "."
|
||||||
|
},
|
||||||
"lsp": {
|
"lsp": {
|
||||||
"command": "node",
|
"command": "node",
|
||||||
"args": ["./components/lsp/packages/lsp-tools-mcp/dist/cli.js", "mcp"],
|
"args": ["../../lsp-tools-mcp/dist/cli.js", "mcp"],
|
||||||
"cwd": "."
|
"cwd": "."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,12 @@
|
|||||||
"components/comment-checker",
|
"components/comment-checker",
|
||||||
"components/rules",
|
"components/rules",
|
||||||
"components/lsp",
|
"components/lsp",
|
||||||
"components/lsp/packages/lsp-tools-mcp",
|
|
||||||
"components/telemetry",
|
"components/telemetry",
|
||||||
"components/ultragoal",
|
"components/ultragoal",
|
||||||
"components/ultrawork"
|
"components/ultrawork"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && npm run build --workspaces --if-present",
|
"build": "bun run --cwd ../../ast-grep-mcp build && node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && npm run build --workspaces --if-present",
|
||||||
"check": "npm run build && npm test",
|
"check": "npm run build && npm test",
|
||||||
"sync:skills": "node scripts/sync-skills.mjs",
|
"sync:skills": "node scripts/sync-skills.mjs",
|
||||||
"test": "node --test test/*.test.mjs"
|
"test": "node --test test/*.test.mjs"
|
||||||
|
|||||||
@@ -71,17 +71,29 @@ test("#given aggregate OMO plugin is enabled #when hooks are inspected #then ult
|
|||||||
assert.deepEqual(preToolUseGroups.map((group) => group.matcher), ["^create_goal$"]);
|
assert.deepEqual(preToolUseGroups.map((group) => group.matcher), ["^create_goal$"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("#given aggregate MCP config #when inspected #then lsp server stays component isolated", async () => {
|
test("#given aggregate MCP config #when inspected #then code MCP servers reuse root MCP packages", async () => {
|
||||||
// given
|
// given
|
||||||
|
const packageJson = await readJson("package.json");
|
||||||
const mcp = await readJson(".mcp.json");
|
const mcp = await readJson(".mcp.json");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const server = mcp.mcpServers.lsp;
|
const lspServer = mcp.mcpServers.lsp;
|
||||||
|
const astGrepServer = mcp.mcpServers.ast_grep;
|
||||||
|
const codeMcpNames = Object.keys(mcp.mcpServers)
|
||||||
|
.filter((name) => name === "lsp" || name === "ast_grep")
|
||||||
|
.sort();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert.equal(server.command, "node");
|
assert.deepEqual(codeMcpNames, ["ast_grep", "lsp"]);
|
||||||
assert.deepEqual(server.args, ["./components/lsp/packages/lsp-tools-mcp/dist/cli.js", "mcp"]);
|
assert.equal(packageJson.workspaces.includes("components/lsp/packages/lsp-tools-mcp"), false);
|
||||||
assert.equal(server.cwd, ".");
|
assert.equal(packageJson.workspaces.includes("components/ast-grep/packages/ast-grep-mcp"), false);
|
||||||
|
assert.match(packageJson.scripts.build, /ast-grep-mcp/);
|
||||||
|
assert.equal(lspServer.command, "node");
|
||||||
|
assert.deepEqual(lspServer.args, ["../../lsp-tools-mcp/dist/cli.js", "mcp"]);
|
||||||
|
assert.equal(lspServer.cwd, ".");
|
||||||
|
assert.equal(astGrepServer.command, "node");
|
||||||
|
assert.deepEqual(astGrepServer.args, ["../../ast-grep-mcp/dist/cli.js", "mcp"]);
|
||||||
|
assert.equal(astGrepServer.cwd, ".");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("#given aggregate plugin build script #when inspected #then telemetry sync runs before workspace builds", async () => {
|
test("#given aggregate plugin build script #when inspected #then telemetry sync runs before workspace builds", async () => {
|
||||||
@@ -93,7 +105,10 @@ test("#given aggregate plugin build script #when inspected #then telemetry sync
|
|||||||
const buildScript = packageJson.scripts.build;
|
const buildScript = packageJson.scripts.build;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert.equal(buildScript, "node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && npm run build --workspaces --if-present");
|
assert.equal(
|
||||||
|
buildScript,
|
||||||
|
"bun run --cwd ../../ast-grep-mcp build && node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && npm run build --workspaces --if-present",
|
||||||
|
);
|
||||||
assert.match(telemetrySyncScript, /syncTelemetryComponent/);
|
assert.match(telemetrySyncScript, /syncTelemetryComponent/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { basename, dirname, join, sep } from "node:path";
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||||
import { cp, lstat, mkdir, readFile, readdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises";
|
import { cp, lstat, mkdir, readFile, readdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises";
|
||||||
|
|
||||||
import { exists, isRecord } from "./utils.mjs";
|
import { exists, isRecord } from "./utils.mjs";
|
||||||
@@ -11,8 +11,9 @@ export async function installCachedPlugin({ codexHome, marketplaceName, name, ru
|
|||||||
|
|
||||||
const targetPath = join(codexHome, "plugins", "cache", marketplaceName, name, version);
|
const targetPath = join(codexHome, "plugins", "cache", marketplaceName, name, version);
|
||||||
await replaceDirectory(sourcePath, targetPath, shouldCopyPluginPath);
|
await replaceDirectory(sourcePath, targetPath, shouldCopyPluginPath);
|
||||||
|
await rewriteCachedPackageLocalFileDependencies(targetPath, sourcePath);
|
||||||
await maybeRunNpmInstall(targetPath, runCommand, ["install", "--omit=dev"]);
|
await maybeRunNpmInstall(targetPath, runCommand, ["install", "--omit=dev"]);
|
||||||
await rewriteCachedMcpManifest(targetPath);
|
await rewriteCachedMcpManifest(targetPath, sourcePath);
|
||||||
return { name, version, path: targetPath };
|
return { name, version, path: targetPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ function shouldCopyPluginPath(path, root) {
|
|||||||
return !parts.some((part) => part === ".git" || part === "node_modules");
|
return !parts.some((part) => part === ".git" || part === "node_modules");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rewriteCachedMcpManifest(pluginRoot) {
|
async function rewriteCachedMcpManifest(pluginRoot, sourceRoot = pluginRoot) {
|
||||||
const manifestPath = join(pluginRoot, ".mcp.json");
|
const manifestPath = join(pluginRoot, ".mcp.json");
|
||||||
if (!(await exists(manifestPath))) return;
|
if (!(await exists(manifestPath))) return;
|
||||||
const raw = await readFile(manifestPath, "utf8");
|
const raw = await readFile(manifestPath, "utf8");
|
||||||
@@ -173,7 +174,7 @@ async function rewriteCachedMcpManifest(pluginRoot) {
|
|||||||
if (!Array.isArray(server.args)) continue;
|
if (!Array.isArray(server.args)) continue;
|
||||||
const nextArgs = server.args.map((arg) => {
|
const nextArgs = server.args.map((arg) => {
|
||||||
if (typeof arg !== "string") return arg;
|
if (typeof arg !== "string") return arg;
|
||||||
if (arg.startsWith("./") || arg.startsWith("../")) return join(pluginRoot, arg);
|
if (arg.startsWith("./") || arg.startsWith("../")) return resolveCachedRuntimePath(pluginRoot, sourceRoot, arg);
|
||||||
return arg;
|
return arg;
|
||||||
});
|
});
|
||||||
if (nextArgs.some((value, index) => value !== server.args[index])) {
|
if (nextArgs.some((value, index) => value !== server.args[index])) {
|
||||||
@@ -183,3 +184,55 @@ async function rewriteCachedMcpManifest(pluginRoot) {
|
|||||||
}
|
}
|
||||||
if (changed) await writeFile(manifestPath, `${JSON.stringify(parsed, null, "\t")}\n`);
|
if (changed) await writeFile(manifestPath, `${JSON.stringify(parsed, null, "\t")}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function rewriteCachedPackageLocalFileDependencies(pluginRoot, sourceRoot) {
|
||||||
|
const packageJsonPaths = [];
|
||||||
|
await collectPackageJsonPaths(pluginRoot, pluginRoot, packageJsonPaths);
|
||||||
|
for (const packageJsonPath of packageJsonPaths) {
|
||||||
|
const raw = await readFile(packageJsonPath, "utf8");
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
if (!isRecord(parsed)) continue;
|
||||||
|
const packageDir = dirname(packageJsonPath);
|
||||||
|
const sourcePackageDir = join(sourceRoot, relative(pluginRoot, packageDir));
|
||||||
|
let changed = false;
|
||||||
|
for (const field of ["dependencies", "optionalDependencies", "peerDependencies"]) {
|
||||||
|
const dependencies = parsed[field];
|
||||||
|
if (!isRecord(dependencies)) continue;
|
||||||
|
for (const [name, specifier] of Object.entries(dependencies)) {
|
||||||
|
if (typeof specifier !== "string" || !specifier.startsWith("file:")) continue;
|
||||||
|
const filePath = specifier.slice("file:".length);
|
||||||
|
if (filePath.length === 0 || isAbsolute(filePath)) continue;
|
||||||
|
const targetPath = resolve(packageDir, filePath);
|
||||||
|
if (isPathInside(targetPath, pluginRoot)) continue;
|
||||||
|
dependencies[name] = `file:${resolve(sourcePackageDir, filePath)}`;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) await writeFile(packageJsonPath, `${JSON.stringify(parsed, null, "\t")}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectPackageJsonPaths(directory, root, paths) {
|
||||||
|
const entries = await readdir(directory, { withFileTypes: true });
|
||||||
|
if (entries.some((entry) => entry.isFile() && entry.name === "package.json")) {
|
||||||
|
paths.push(join(directory, "package.json"));
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.isDirectory()) continue;
|
||||||
|
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue;
|
||||||
|
const childPath = join(directory, entry.name);
|
||||||
|
if (!childPath.startsWith(root)) continue;
|
||||||
|
await collectPackageJsonPaths(childPath, root, paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveCachedRuntimePath(pluginRoot, sourceRoot, runtimePath) {
|
||||||
|
const targetPath = resolve(pluginRoot, runtimePath);
|
||||||
|
if (isPathInside(targetPath, pluginRoot)) return targetPath;
|
||||||
|
return resolve(sourceRoot, runtimePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPathInside(candidatePath, rootPath) {
|
||||||
|
const pathFromRoot = relative(rootPath, candidatePath);
|
||||||
|
return pathFromRoot === "" || (!pathFromRoot.startsWith("..") && !isAbsolute(pathFromRoot));
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { readFile, readdir, writeFile } from "node:fs/promises"
|
||||||
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
|
||||||
|
import { isPathInside } from "./codex-cache-paths"
|
||||||
|
|
||||||
|
export async function rewriteCachedPackageLocalFileDependencies(pluginRoot: string, sourceRoot: string): Promise<void> {
|
||||||
|
const packageJsonPaths: string[] = []
|
||||||
|
await collectPackageJsonPaths(pluginRoot, pluginRoot, packageJsonPaths)
|
||||||
|
for (const packageJsonPath of packageJsonPaths) {
|
||||||
|
const raw = await readFile(packageJsonPath, "utf8")
|
||||||
|
const parsed: unknown = JSON.parse(raw)
|
||||||
|
if (!isRecord(parsed)) continue
|
||||||
|
const packageDir = dirname(packageJsonPath)
|
||||||
|
const sourcePackageDir = join(sourceRoot, relative(pluginRoot, packageDir))
|
||||||
|
let changed = false
|
||||||
|
for (const field of ["dependencies", "optionalDependencies", "peerDependencies"] as const) {
|
||||||
|
const dependencies = parsed[field]
|
||||||
|
if (!isRecord(dependencies)) continue
|
||||||
|
for (const [name, specifier] of Object.entries(dependencies)) {
|
||||||
|
if (typeof specifier !== "string" || !specifier.startsWith("file:")) continue
|
||||||
|
const filePath = specifier.slice("file:".length)
|
||||||
|
if (filePath.length === 0 || isAbsolute(filePath)) continue
|
||||||
|
const targetPath = resolve(packageDir, filePath)
|
||||||
|
if (isPathInside(targetPath, pluginRoot)) continue
|
||||||
|
dependencies[name] = `file:${resolve(sourcePackageDir, filePath)}`
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) await writeFile(packageJsonPath, `${JSON.stringify(parsed, null, "\t")}\n`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectPackageJsonPaths(directory: string, root: string, paths: string[]): Promise<void> {
|
||||||
|
const entries = await readdir(directory, { withFileTypes: true })
|
||||||
|
if (entries.some((entry) => entry.isFile() && entry.name === "package.json")) {
|
||||||
|
paths.push(join(directory, "package.json"))
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.isDirectory()) continue
|
||||||
|
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue
|
||||||
|
const childPath = join(directory, entry.name)
|
||||||
|
if (!isPathInside(childPath, root)) continue
|
||||||
|
await collectPackageJsonPaths(childPath, root, paths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { isAbsolute, relative, resolve } from "node:path"
|
||||||
|
|
||||||
|
export function resolveCachedRuntimePath(pluginRoot: string, sourceRoot: string, runtimePath: string): string {
|
||||||
|
const targetPath = resolve(pluginRoot, runtimePath)
|
||||||
|
if (isPathInside(targetPath, pluginRoot)) return targetPath
|
||||||
|
return resolve(sourceRoot, runtimePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPathInside(candidatePath: string, rootPath: string): boolean {
|
||||||
|
const pathFromRoot = relative(rootPath, candidatePath)
|
||||||
|
return pathFromRoot === "" || (!pathFromRoot.startsWith("..") && !isAbsolute(pathFromRoot))
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { describe, expect, test } from "bun:test"
|
|||||||
import { mkdir, mkdtemp, readFile, readlink, writeFile } from "node:fs/promises"
|
import { mkdir, mkdtemp, readFile, readlink, writeFile } from "node:fs/promises"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { linkCachedPluginBins, rewriteCachedMcpManifest } from "./codex-cache"
|
import { installCachedPlugin, linkCachedPluginBins, rewriteCachedMcpManifest } from "./codex-cache"
|
||||||
|
|
||||||
describe("codex-cache", () => {
|
describe("codex-cache", () => {
|
||||||
test("rewrites cached mcp manifest relative args and cwd", async () => {
|
test("rewrites cached mcp manifest relative args and cwd", async () => {
|
||||||
@@ -27,6 +27,74 @@ describe("codex-cache", () => {
|
|||||||
expect(rewritten.mcpServers.lsp.args[0]).toBe(join(root, "./components/lsp/dist/cli.js"))
|
expect(rewritten.mcpServers.lsp.args[0]).toBe(join(root, "./components/lsp/dist/cli.js"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("rewrites cached mcp manifest args that point outside the plugin cache back to the source package", async () => {
|
||||||
|
// given
|
||||||
|
const root = await mkdtemp(join(tmpdir(), "omo-codex-cache-"))
|
||||||
|
const sourceRoot = join(root, "packages", "omo-codex", "plugin")
|
||||||
|
const cacheRoot = join(root, "cache", "omo")
|
||||||
|
await mkdir(cacheRoot, { recursive: true })
|
||||||
|
await writeFile(
|
||||||
|
join(cacheRoot, ".mcp.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
mcpServers: {
|
||||||
|
ast_grep: { cwd: ".", args: ["../../ast-grep-mcp/dist/cli.js", "mcp"] },
|
||||||
|
custom: { args: ["/usr/local/bin/custom-mcp", "--stdio"] },
|
||||||
|
lsp: { cwd: ".", args: ["../../lsp-tools-mcp/dist/cli.js", "mcp"] },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// when
|
||||||
|
await rewriteCachedMcpManifest(cacheRoot, sourceRoot)
|
||||||
|
|
||||||
|
// then
|
||||||
|
const rewritten = JSON.parse(await readFile(join(cacheRoot, ".mcp.json"), "utf8")) as {
|
||||||
|
mcpServers: {
|
||||||
|
ast_grep: { cwd?: string; args: string[] }
|
||||||
|
custom: { args: string[] }
|
||||||
|
lsp: { cwd?: string; args: string[] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(Object.keys(rewritten.mcpServers).sort()).toEqual(["ast_grep", "custom", "lsp"])
|
||||||
|
expect(rewritten.mcpServers.ast_grep.cwd).toBeUndefined()
|
||||||
|
expect(rewritten.mcpServers.ast_grep.args[0]).toBe(join(root, "packages", "ast-grep-mcp", "dist", "cli.js"))
|
||||||
|
expect(rewritten.mcpServers.custom.args).toEqual(["/usr/local/bin/custom-mcp", "--stdio"])
|
||||||
|
expect(rewritten.mcpServers.lsp.cwd).toBeUndefined()
|
||||||
|
expect(rewritten.mcpServers.lsp.args[0]).toBe(join(root, "packages", "lsp-tools-mcp", "dist", "cli.js"))
|
||||||
|
})
|
||||||
|
|
||||||
|
test("rewrites cached package file dependencies that point outside the plugin cache back to the source package", async () => {
|
||||||
|
// given
|
||||||
|
const root = await mkdtemp(join(tmpdir(), "omo-codex-cache-"))
|
||||||
|
const codexHome = join(root, "codex-home")
|
||||||
|
const sourceRoot = join(root, "packages", "omo-codex", "plugin")
|
||||||
|
await mkdir(sourceRoot, { recursive: true })
|
||||||
|
await writeFile(
|
||||||
|
join(sourceRoot, "package.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
name: "@scope/omo",
|
||||||
|
version: "0.1.0",
|
||||||
|
dependencies: { "@scope/lsp-tools": "file:../lsp-tools-mcp" },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const installed = await installCachedPlugin({
|
||||||
|
codexHome,
|
||||||
|
marketplaceName: "debug",
|
||||||
|
name: "omo",
|
||||||
|
sourcePath: sourceRoot,
|
||||||
|
version: "0.1.0",
|
||||||
|
runCommand: async () => undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
const cachedPackageJson = JSON.parse(await readFile(join(installed.path, "package.json"), "utf8")) as {
|
||||||
|
dependencies: Record<string, string>
|
||||||
|
}
|
||||||
|
expect(cachedPackageJson.dependencies["@scope/lsp-tools"]).toBe(`file:${join(root, "packages", "omo-codex", "lsp-tools-mcp")}`)
|
||||||
|
})
|
||||||
|
|
||||||
test("links cached plugin bins and stays idempotent", async () => {
|
test("links cached plugin bins and stays idempotent", async () => {
|
||||||
// given
|
// given
|
||||||
const root = await mkdtemp(join(tmpdir(), "omo-codex-cache-"))
|
const root = await mkdtemp(join(tmpdir(), "omo-codex-cache-"))
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { cp, lstat, mkdir, readFile, readdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises"
|
import { cp, lstat, mkdir, readFile, readdir, readlink, rename, rm, symlink, writeFile } from "node:fs/promises"
|
||||||
import { basename, dirname, join, sep } from "node:path"
|
import { basename, dirname, join, sep } from "node:path"
|
||||||
|
import { rewriteCachedPackageLocalFileDependencies } from "./codex-cache-local-dependencies"
|
||||||
|
import { resolveCachedRuntimePath } from "./codex-cache-paths"
|
||||||
import type { InstalledPlugin, RunCommand } from "./types"
|
import type { InstalledPlugin, RunCommand } from "./types"
|
||||||
|
|
||||||
type LinkPlatform = NodeJS.Platform
|
type LinkPlatform = NodeJS.Platform
|
||||||
@@ -19,8 +21,9 @@ export async function installCachedPlugin(input: {
|
|||||||
|
|
||||||
const targetPath = join(input.codexHome, "plugins", "cache", input.marketplaceName, input.name, input.version)
|
const targetPath = join(input.codexHome, "plugins", "cache", input.marketplaceName, input.name, input.version)
|
||||||
await replaceDirectory(input.sourcePath, targetPath)
|
await replaceDirectory(input.sourcePath, targetPath)
|
||||||
|
await rewriteCachedPackageLocalFileDependencies(targetPath, input.sourcePath)
|
||||||
await maybeRunNpmInstall(targetPath, input.runCommand, ["install", "--omit=dev"])
|
await maybeRunNpmInstall(targetPath, input.runCommand, ["install", "--omit=dev"])
|
||||||
await rewriteCachedMcpManifest(targetPath)
|
await rewriteCachedMcpManifest(targetPath, input.sourcePath)
|
||||||
return { name: input.name, version: input.version, path: targetPath }
|
return { name: input.name, version: input.version, path: targetPath }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ async function linkCachedPluginBin(
|
|||||||
return linkPath
|
return linkPath
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rewriteCachedMcpManifest(pluginRoot: string): Promise<void> {
|
export async function rewriteCachedMcpManifest(pluginRoot: string, sourceRoot = pluginRoot): Promise<void> {
|
||||||
const manifestPath = join(pluginRoot, ".mcp.json")
|
const manifestPath = join(pluginRoot, ".mcp.json")
|
||||||
if (!(await exists(manifestPath))) return
|
if (!(await exists(manifestPath))) return
|
||||||
const raw = await readFile(manifestPath, "utf8")
|
const raw = await readFile(manifestPath, "utf8")
|
||||||
@@ -87,7 +90,7 @@ export async function rewriteCachedMcpManifest(pluginRoot: string): Promise<void
|
|||||||
if (!Array.isArray(currentArgs)) continue
|
if (!Array.isArray(currentArgs)) continue
|
||||||
const nextArgs = currentArgs.map((arg) => {
|
const nextArgs = currentArgs.map((arg) => {
|
||||||
if (typeof arg !== "string") return arg
|
if (typeof arg !== "string") return arg
|
||||||
if (arg.startsWith("./") || arg.startsWith("../")) return join(pluginRoot, arg)
|
if (arg.startsWith("./") || arg.startsWith("../")) return resolveCachedRuntimePath(pluginRoot, sourceRoot, arg)
|
||||||
return arg
|
return arg
|
||||||
})
|
})
|
||||||
if (nextArgs.some((value, index) => value !== currentArgs[index])) {
|
if (nextArgs.some((value, index) => value !== currentArgs[index])) {
|
||||||
|
|||||||
Reference in New Issue
Block a user