fix: pass wrapper package root to codex installer
This commit is contained in:
@@ -6,6 +6,7 @@ import { spawnSync } from "node:child_process";
|
|||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import { basename } from "node:path";
|
import { basename } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
import {
|
import {
|
||||||
getPlatformPackageCandidates,
|
getPlatformPackageCandidates,
|
||||||
getBinaryPath,
|
getBinaryPath,
|
||||||
@@ -90,6 +91,10 @@ function getWrapperPackageName() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWrapperPackageRoot() {
|
||||||
|
return fileURLToPath(new URL("..", import.meta.url));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine which bin name the user invoked us with (oh-my-opencode, oh-my-openagent, omo, lazycodex).
|
* Determine which bin name the user invoked us with (oh-my-opencode, oh-my-openagent, omo, lazycodex).
|
||||||
* Propagated to the compiled CLI binary via OMO_INVOCATION_NAME so it can route accordingly
|
* Propagated to the compiled CLI binary via OMO_INVOCATION_NAME so it can route accordingly
|
||||||
@@ -155,6 +160,7 @@ function main() {
|
|||||||
const childEnv = {
|
const childEnv = {
|
||||||
...process.env,
|
...process.env,
|
||||||
OMO_INVOCATION_NAME: invocationName,
|
OMO_INVOCATION_NAME: invocationName,
|
||||||
|
OMO_WRAPPER_PACKAGE_ROOT: getWrapperPackageRoot(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let index = 0; index < resolvedBinaries.length; index += 1) {
|
for (let index = 0; index < resolvedBinaries.length; index += 1) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { afterEach, describe, expect, test } from "bun:test";
|
import { afterEach, describe, expect, test } from "bun:test";
|
||||||
import { spawnSync } from "node:child_process";
|
import { spawnSync } from "node:child_process";
|
||||||
import { chmod, cp, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
import { chmod, cp, mkdir, mkdtemp, readFile, realpath, rm, symlink, writeFile } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
@@ -33,6 +33,7 @@ describe("lazycodex bin wrapper", () => {
|
|||||||
// #then
|
// #then
|
||||||
expect(result.status).toBe(23);
|
expect(result.status).toBe(23);
|
||||||
expect((await readFile(join(fixture.captureDir, "env"), "utf8")).trim()).toBe("lazycodex");
|
expect((await readFile(join(fixture.captureDir, "env"), "utf8")).trim()).toBe("lazycodex");
|
||||||
|
expect(await canonicalizePackageRootCapture(fixture)).toBe(await realpath(fixture.root));
|
||||||
expect((await readFile(join(fixture.captureDir, "args"), "utf8")).trim().split("\n")).toEqual([
|
expect((await readFile(join(fixture.captureDir, "args"), "utf8")).trim().split("\n")).toEqual([
|
||||||
"install",
|
"install",
|
||||||
"--no-tui",
|
"--no-tui",
|
||||||
@@ -57,6 +58,7 @@ describe("lazycodex bin wrapper", () => {
|
|||||||
// #then
|
// #then
|
||||||
expect(result.status).toBe(23);
|
expect(result.status).toBe(23);
|
||||||
expect((await readFile(join(fixture.captureDir, "env"), "utf8")).trim()).toBe("lazycodex");
|
expect((await readFile(join(fixture.captureDir, "env"), "utf8")).trim()).toBe("lazycodex");
|
||||||
|
expect(await canonicalizePackageRootCapture(fixture)).toBe(await realpath(fixture.root));
|
||||||
expect((await readFile(join(fixture.captureDir, "args"), "utf8")).trim().split("\n")).toEqual([
|
expect((await readFile(join(fixture.captureDir, "args"), "utf8")).trim().split("\n")).toEqual([
|
||||||
"install",
|
"install",
|
||||||
"--no-tui",
|
"--no-tui",
|
||||||
@@ -81,6 +83,7 @@ describe("lazycodex bin wrapper", () => {
|
|||||||
// #then
|
// #then
|
||||||
expect(result.status).toBe(23);
|
expect(result.status).toBe(23);
|
||||||
expect((await readFile(join(fixture.captureDir, "env"), "utf8")).trim()).toBe("lazycodex");
|
expect((await readFile(join(fixture.captureDir, "env"), "utf8")).trim()).toBe("lazycodex");
|
||||||
|
expect(await canonicalizePackageRootCapture(fixture)).toBe(await realpath(fixture.root));
|
||||||
expect((await readFile(join(fixture.captureDir, "args"), "utf8")).trim().split("\n")).toEqual([
|
expect((await readFile(join(fixture.captureDir, "args"), "utf8")).trim().split("\n")).toEqual([
|
||||||
"install",
|
"install",
|
||||||
"--no-tui",
|
"--no-tui",
|
||||||
@@ -130,10 +133,15 @@ async function createLazyCodexFixture(options: { packageName?: string; wrapperFi
|
|||||||
captureDir,
|
captureDir,
|
||||||
fakeBinDir,
|
fakeBinDir,
|
||||||
lazycodexBin: join(binDir, "lazycodex"),
|
lazycodexBin: join(binDir, "lazycodex"),
|
||||||
|
root,
|
||||||
wrapperBin,
|
wrapperBin,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function canonicalizePackageRootCapture(fixture: { readonly captureDir: string }): Promise<string> {
|
||||||
|
return realpath((await readFile(join(fixture.captureDir, "wrapper-root"), "utf8")).trim());
|
||||||
|
}
|
||||||
|
|
||||||
async function writePlatformPackages(root: string): Promise<void> {
|
async function writePlatformPackages(root: string): Promise<void> {
|
||||||
const packages = getPlatformPackageCandidates({
|
const packages = getPlatformPackageCandidates({
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
@@ -149,6 +157,7 @@ async function writePlatformPackages(root: string): Promise<void> {
|
|||||||
[
|
[
|
||||||
"#!/bin/sh",
|
"#!/bin/sh",
|
||||||
"printf '%s\\n' \"$OMO_INVOCATION_NAME\" > \"$CAPTURE_DIR/env\"",
|
"printf '%s\\n' \"$OMO_INVOCATION_NAME\" > \"$CAPTURE_DIR/env\"",
|
||||||
|
"printf '%s\\n' \"$OMO_WRAPPER_PACKAGE_ROOT\" > \"$CAPTURE_DIR/wrapper-root\"",
|
||||||
"printf '%s\\n' \"$@\" > \"$CAPTURE_DIR/args\"",
|
"printf '%s\\n' \"$@\" > \"$CAPTURE_DIR/args\"",
|
||||||
"exit 23",
|
"exit 23",
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { describe, expect, test } from "bun:test"
|
|||||||
import { mkdir, mkdtemp, readdir, readFile, readlink, rm, stat, writeFile } from "node:fs/promises"
|
import { mkdir, mkdtemp, readdir, readFile, readlink, rm, stat, 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 { findRepoRootFromImporter, resolveCodexInstallerBinDir, runCodexInstaller } from "./install-codex"
|
import { findRepoRoot, findRepoRootFromImporter, resolveCodexInstallerBinDir, runCodexInstaller } from "./install-codex"
|
||||||
|
|
||||||
const EXPECTED_OMO_COMPONENT_BINS = [
|
const EXPECTED_OMO_COMPONENT_BINS = [
|
||||||
{ name: "omo", target: join("components", "ulw-loop", "dist", "cli.js") },
|
{ name: "omo", target: join("components", "ulw-loop", "dist", "cli.js") },
|
||||||
@@ -42,6 +42,23 @@ describe("install-codex", () => {
|
|||||||
expect(repoRoot).toBe(wrapperRoot)
|
expect(repoRoot).toBe(wrapperRoot)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("#given wrapper root env #when resolving vendored repo root #then prefers wrapper package root", async () => {
|
||||||
|
// given
|
||||||
|
const platformPackageRoot = await mkdtemp(join(tmpdir(), "omo-codex-platform-package-"))
|
||||||
|
const wrapperRoot = await mkdtemp(join(tmpdir(), "omo-codex-wrapper-package-"))
|
||||||
|
await mkdir(join(wrapperRoot, "packages", "omo-codex", "plugin", ".codex-plugin"), { recursive: true })
|
||||||
|
await writeFile(join(wrapperRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"), "{}")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const repoRoot = findRepoRoot({
|
||||||
|
importerDir: join(platformPackageRoot, "bin"),
|
||||||
|
env: { OMO_WRAPPER_PACKAGE_ROOT: wrapperRoot },
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(repoRoot).toBe(wrapperRoot)
|
||||||
|
})
|
||||||
|
|
||||||
test("#given default CODEX_HOME #when resolving installer bin dir without override #then preserves user local bin precedence", () => {
|
test("#given default CODEX_HOME #when resolving installer bin dir without override #then preserves user local bin precedence", () => {
|
||||||
// given
|
// given
|
||||||
const homeDir = join(tmpdir(), "omo-codex-home-default")
|
const homeDir = join(tmpdir(), "omo-codex-home-default")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type { CodexInstallOptions, CodexInstallResult, CodexMarketplaceSource, I
|
|||||||
const SISYPHUS_LEGACY_CACHE_MARKETPLACES = ["lazycodex", "code-yeongyu-codex-plugins"] as const
|
const SISYPHUS_LEGACY_CACHE_MARKETPLACES = ["lazycodex", "code-yeongyu-codex-plugins"] as const
|
||||||
|
|
||||||
export async function runCodexInstaller(options: CodexInstallOptions = {}): Promise<CodexInstallResult> {
|
export async function runCodexInstaller(options: CodexInstallOptions = {}): Promise<CodexInstallResult> {
|
||||||
const repoRoot = resolve(options.repoRoot ?? findRepoRootFromImporter(import.meta.dir))
|
const repoRoot = resolve(options.repoRoot ?? findRepoRoot({ importerDir: import.meta.dir, env: process.env }))
|
||||||
const codexHome = resolve(options.codexHome ?? process.env.CODEX_HOME ?? join(homedir(), ".codex"))
|
const codexHome = resolve(options.codexHome ?? process.env.CODEX_HOME ?? join(homedir(), ".codex"))
|
||||||
const binDir = resolveCodexInstallerBinDir({ binDir: options.binDir, codexHome, env: process.env })
|
const binDir = resolveCodexInstallerBinDir({ binDir: options.binDir, codexHome, env: process.env })
|
||||||
const runCommand = options.runCommand ?? defaultRunCommand
|
const runCommand = options.runCommand ?? defaultRunCommand
|
||||||
@@ -199,11 +199,9 @@ function codexMarketplaceSource(marketplaceRoot: string): CodexMarketplaceSource
|
|||||||
export function findRepoRootFromImporter(importerDir: string): string {
|
export function findRepoRootFromImporter(importerDir: string): string {
|
||||||
let current = importerDir
|
let current = importerDir
|
||||||
for (let depth = 0; depth <= 5; depth += 1) {
|
for (let depth = 0; depth <= 5; depth += 1) {
|
||||||
const pluginManifestPath = join(current, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json")
|
if (isRepoRootWithCodexPlugin(current)) return current
|
||||||
if (existsSyncLike(pluginManifestPath)) return current
|
|
||||||
for (const wrapperPackageRoot of [join(current, "node_modules", "oh-my-openagent"), join(current, "oh-my-openagent")]) {
|
for (const wrapperPackageRoot of [join(current, "node_modules", "oh-my-openagent"), join(current, "oh-my-openagent")]) {
|
||||||
const wrapperPluginManifestPath = join(wrapperPackageRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json")
|
if (isRepoRootWithCodexPlugin(wrapperPackageRoot)) return wrapperPackageRoot
|
||||||
if (existsSyncLike(wrapperPluginManifestPath)) return wrapperPackageRoot
|
|
||||||
}
|
}
|
||||||
current = resolve(current, "..")
|
current = resolve(current, "..")
|
||||||
}
|
}
|
||||||
@@ -212,6 +210,22 @@ export function findRepoRootFromImporter(importerDir: string): string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findRepoRoot(input: {
|
||||||
|
readonly importerDir: string
|
||||||
|
readonly env?: { readonly [key: string]: string | undefined }
|
||||||
|
}): string {
|
||||||
|
const wrapperPackageRoot = input.env?.OMO_WRAPPER_PACKAGE_ROOT
|
||||||
|
if (wrapperPackageRoot !== undefined && wrapperPackageRoot.trim().length > 0) {
|
||||||
|
const resolvedWrapperPackageRoot = resolve(wrapperPackageRoot)
|
||||||
|
if (isRepoRootWithCodexPlugin(resolvedWrapperPackageRoot)) return resolvedWrapperPackageRoot
|
||||||
|
}
|
||||||
|
return findRepoRootFromImporter(input.importerDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRepoRootWithCodexPlugin(repoRoot: string): boolean {
|
||||||
|
return existsSyncLike(join(repoRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"))
|
||||||
|
}
|
||||||
|
|
||||||
function existsSyncLike(path: string): boolean {
|
function existsSyncLike(path: string): boolean {
|
||||||
return existsSync(path)
|
return existsSync(path)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user