fix(codex): install lsp from local cache

This commit is contained in:
YeonGyu-Kim
2026-05-29 12:41:54 +09:00
parent 8c6e8e4986
commit 8896db9806
5 changed files with 99 additions and 15 deletions
+12 -3
View File
@@ -68,9 +68,9 @@ describe("install-codex", () => {
const configContent = await readFile(join(codexHome, "config.toml"), "utf8")
expect(configContent).toContain("[features]")
expect(configContent).toContain("[marketplaces.sisyphuslabs]")
expect(configContent).toContain('source_type = "git"')
expect(configContent).toContain('source = "https://github.com/code-yeongyu/lazycodex.git"')
expect(configContent).toContain('ref = "main"')
expect(configContent).toContain('source_type = "local"')
expect(configContent).toContain(`source = "${join(codexHome, "plugins", "cache", "sisyphuslabs")}"`)
expect(configContent).not.toContain('ref = "main"')
expect(configContent).toContain("[plugins.\"omo@sisyphuslabs\"]")
expect(configContent).toContain("[hooks.state.")
expect(configContent).toContain("[agents.explorer]")
@@ -87,9 +87,18 @@ describe("install-codex", () => {
expect(pluginPath).toContain(join("plugins", "cache", "sisyphuslabs", "omo"))
const stats = await stat(pluginPath ?? "")
expect(stats.isDirectory()).toBe(true)
const mcpManifest = JSON.parse(await readFile(join(pluginPath ?? "", ".mcp.json"), "utf8")) as {
mcpServers: { lsp: { args: string[] } }
}
expect(mcpManifest.mcpServers.lsp.args[0]).toBe(join(pluginPath ?? "", "components", "lsp", "dist", "cli.js"))
expect((await stat(mcpManifest.mcpServers.lsp.args[0] ?? "")).isFile()).toBe(true)
expect((await stat(join(codexHome, "agents", "explorer.toml"))).isFile()).toBe(true)
expect((await stat(join(codexHome, "agents", "librarian.toml"))).isFile()).toBe(true)
expect((await stat(join(codexHome, "agents", "plan.toml"))).isFile()).toBe(true)
const marketplace = JSON.parse(
await readFile(join(codexHome, "plugins", "cache", "sisyphuslabs", ".agents", "plugins", "marketplace.json"), "utf8"),
) as { plugins: Array<{ name: string; source: { source: string; path: string } }> }
expect(marketplace.plugins).toEqual([{ name: "omo", source: { source: "local", path: "./omo/0.1.0" } }])
await expect(stat(join(codexHome, "plugins", "cache", "code-yeongyu-codex-plugins", "omo"))).rejects.toThrow()
})
})
+32 -6
View File
@@ -1,6 +1,7 @@
import { homedir } from "node:os"
import { join, resolve } from "node:path"
import { existsSync } from "node:fs"
import { mkdir, writeFile } from "node:fs/promises"
import { installCachedPlugin, linkCachedPluginBins, pruneMarketplaceCache, pruneMarketplacePluginCaches } from "./codex-cache"
import { updateCodexConfig } from "./codex-config-toml"
import { trustedHookStatesForPlugin } from "./codex-hook-trust"
@@ -9,11 +10,6 @@ import { readMarketplace, readPluginManifest, resolvePluginSource, validatePathS
import { defaultRunCommand } from "./codex-process"
import type { CodexInstallOptions, CodexInstallResult, InstalledPlugin } from "./types"
const LAZYCODEX_MARKETPLACE_SOURCE = {
sourceType: "git",
source: "https://github.com/code-yeongyu/lazycodex.git",
ref: "main",
} as const
const SISYPHUS_LEGACY_CACHE_MARKETPLACES = ["lazycodex", "code-yeongyu-codex-plugins"] as const
export async function runCodexInstaller(options: CodexInstallOptions = {}): Promise<CodexInstallResult> {
@@ -90,12 +86,19 @@ export async function runCodexInstaller(options: CodexInstallOptions = {}): Prom
})
}
const marketplaceRoot = join(codexHome, "plugins", "cache", marketplace.name)
await writeCachedMarketplaceManifest({
marketplaceName: marketplace.name,
marketplaceRoot,
plugins: installed,
})
const configPath = join(codexHome, "config.toml")
await updateCodexConfig({
configPath,
repoRoot: codexPackageRoot,
marketplaceName: marketplace.name,
marketplaceSource: LAZYCODEX_MARKETPLACE_SOURCE,
marketplaceSource: { sourceType: "local", source: marketplaceRoot },
pluginNames: marketplace.plugins.map((plugin) => plugin.name),
trustedHookStates,
agentConfigs: [...agentConfigs.values()].sort((left, right) => left.name.localeCompare(right.name)),
@@ -131,6 +134,29 @@ function agentNameFromToml(fileName: string): string {
return fileName.endsWith(".toml") ? fileName.slice(0, -".toml".length) : fileName
}
async function writeCachedMarketplaceManifest(input: {
readonly marketplaceName: string
readonly marketplaceRoot: string
readonly plugins: readonly InstalledPlugin[]
}): Promise<void> {
const marketplaceDir = join(input.marketplaceRoot, ".agents", "plugins")
await mkdir(marketplaceDir, { recursive: true })
await writeFile(
join(marketplaceDir, "marketplace.json"),
`${JSON.stringify(
{
name: input.marketplaceName,
plugins: input.plugins.map((plugin) => ({
name: plugin.name,
source: { source: "local", path: `./${plugin.name}/${plugin.version}` },
})),
},
null,
"\t",
)}\n`,
)
}
function legacyCacheMarketplaces(marketplaceName: string): readonly string[] {
return marketplaceName === "sisyphuslabs" ? SISYPHUS_LEGACY_CACHE_MARKETPLACES : []
}