mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-20 03:24:46 +00:00
Add a post-install seam so the wizard can prompt the user to import their existing Codex CLI state (skills, archived config/hooks, advisory cached plugins) through the existing `openclaw migrate codex` flow once the harness plugin is in place. Fires on both fresh installs and repair runs; the user can decline at any time. Trigger sites, both routing through one helper: - src/plugins/provider-auth-choice.ts: after `ensureCodexRuntimePluginForModelSelection` reports `installed: true`, dynamically import `offerPostInstallMigrations` and call it before the wizard moves on. - src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.ts: same call shape with `nonInteractive: true`, so the helper emits a hint line only and never mutates state. Helper (src/wizard/setup.post-install-migration.ts) is generic, not Codex-hardcoded — it resolves migration providers via the manifest `migrationProviders` contract, filters to providers owned by plugins the caller flags as installed in this onboarding step, runs `provider.detect`, and on TTY hands accepted runs to `migrateDefaultCommand`. All detect, prompt, and migrate failures are swallowed so onboarding never aborts on this optional offer. Also harden the Codex app-server subprocess lifecycle now that `detect()` runs from a hotter onboarding path: isolate the plugin-install `plugin/read` call (extensions/codex/src/migration/apply.ts) and have the isolated request wait for child exit with a SIGKILL fallback (extensions/codex/src/app-server/request.ts) so parents are not held open by an orphaned codex binary. Tests: - src/wizard/setup.post-install-migration.test.ts (new, 10 cases) - src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.test.ts extended with hint-call assertions and a not-required-no-offer case.
74 lines
2.7 KiB
TypeScript
74 lines
2.7 KiB
TypeScript
import type { resolveCodexAppServerAuthProfileIdForAgent } from "./auth-bridge.js";
|
|
import type { CodexAppServerStartOptions } from "./config.js";
|
|
import type {
|
|
CodexAppServerRequestMethod,
|
|
CodexAppServerRequestParams,
|
|
CodexAppServerRequestResult,
|
|
JsonValue,
|
|
} from "./protocol.js";
|
|
import {
|
|
createIsolatedCodexAppServerClient,
|
|
getSharedCodexAppServerClient,
|
|
} from "./shared-client.js";
|
|
import { withTimeout } from "./timeout.js";
|
|
|
|
export async function requestCodexAppServerJson<M extends CodexAppServerRequestMethod>(params: {
|
|
method: M;
|
|
requestParams: CodexAppServerRequestParams<M>;
|
|
timeoutMs?: number;
|
|
startOptions?: CodexAppServerStartOptions;
|
|
authProfileId?: string | null;
|
|
agentDir?: string;
|
|
config?: Parameters<typeof resolveCodexAppServerAuthProfileIdForAgent>[0]["config"];
|
|
isolated?: boolean;
|
|
}): Promise<CodexAppServerRequestResult<M>>;
|
|
export async function requestCodexAppServerJson<T = JsonValue | undefined>(params: {
|
|
method: string;
|
|
requestParams?: unknown;
|
|
timeoutMs?: number;
|
|
startOptions?: CodexAppServerStartOptions;
|
|
authProfileId?: string | null;
|
|
agentDir?: string;
|
|
config?: Parameters<typeof resolveCodexAppServerAuthProfileIdForAgent>[0]["config"];
|
|
isolated?: boolean;
|
|
}): Promise<T>;
|
|
export async function requestCodexAppServerJson<T = JsonValue | undefined>(params: {
|
|
method: string;
|
|
requestParams?: unknown;
|
|
timeoutMs?: number;
|
|
startOptions?: CodexAppServerStartOptions;
|
|
authProfileId?: string | null;
|
|
agentDir?: string;
|
|
config?: Parameters<typeof resolveCodexAppServerAuthProfileIdForAgent>[0]["config"];
|
|
isolated?: boolean;
|
|
}): Promise<T> {
|
|
const timeoutMs = params.timeoutMs ?? 60_000;
|
|
return await withTimeout(
|
|
(async () => {
|
|
const client = await (
|
|
params.isolated ? createIsolatedCodexAppServerClient : getSharedCodexAppServerClient
|
|
)({
|
|
startOptions: params.startOptions,
|
|
timeoutMs,
|
|
authProfileId: params.authProfileId,
|
|
agentDir: params.agentDir,
|
|
config: params.config,
|
|
});
|
|
try {
|
|
return await client.request<T>(params.method, params.requestParams, { timeoutMs });
|
|
} finally {
|
|
if (params.isolated) {
|
|
// Wait for the child to actually exit (with a SIGKILL fallback) so
|
|
// the parent process doesn't hang on an orphaned codex app-server.
|
|
// The stdio bin shim does not always propagate stdin EOF to the
|
|
// underlying codex binary, so the unref'd close() path can leave
|
|
// the child running and keep the parent's event loop alive.
|
|
await client.closeAndWait({ exitTimeoutMs: 2_000, forceKillDelayMs: 250 });
|
|
}
|
|
}
|
|
})(),
|
|
timeoutMs,
|
|
`codex app-server ${params.method} timed out`,
|
|
);
|
|
}
|