mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
feat(codex): support the openai-api-curated marketplace wire name (#115955)
This commit is contained in:
committed by
GitHub
parent
5fa8c4a4e5
commit
34e81ca0dc
@@ -28,7 +28,10 @@ working.
|
||||
- The target Codex app-server can see the expected marketplace, plugin, and
|
||||
app inventory.
|
||||
- Migration supports only `openai-curated` plugins that it observed as
|
||||
source-installed in the source Codex home.
|
||||
source-installed in the source Codex home. Codex serves the same catalog to
|
||||
API-key and Bedrock accounts under the `openai-api-curated` wire name;
|
||||
OpenClaw treats both names as the one curated catalog, so configured
|
||||
`openai-curated` plugins resolve from either.
|
||||
- Manually configured `workspace-directory` plugins must already appear
|
||||
installed and enabled under their exact marketplace-qualified identity in
|
||||
`plugin/installed`. Their owned apps must be accessible and callable for the
|
||||
|
||||
@@ -187,6 +187,7 @@ const CURATED_MARKETPLACE_POLL_INTERVAL_MS = 2_000;
|
||||
const COMPUTER_USE_MARKETPLACE_NAME_PRIORITY = [
|
||||
"openai-bundled",
|
||||
"openai-curated",
|
||||
"openai-api-curated",
|
||||
"openai-curated-remote",
|
||||
"local",
|
||||
];
|
||||
|
||||
@@ -277,6 +277,67 @@ describe("Codex plugin inventory", () => {
|
||||
expect(inventory.diagnostics).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it("accepts the API-key curated marketplace wire name", async () => {
|
||||
const appCache = new CodexAppInventoryCache();
|
||||
await appCache.refreshNow({
|
||||
key: "runtime",
|
||||
nowMs: 0,
|
||||
request: async (method, params) =>
|
||||
codexAppInventoryResponse(method, [appInfo("google-calendar-app", true)], params),
|
||||
});
|
||||
const listed = {
|
||||
marketplaces: [
|
||||
{
|
||||
name: "openai-api-curated",
|
||||
path: "/codex-home/.tmp/plugins/.agents/plugins/api_marketplace.json",
|
||||
interface: null,
|
||||
plugins: [
|
||||
pluginSummary("google-calendar@openai-api-curated", {
|
||||
name: "google-calendar",
|
||||
installed: true,
|
||||
enabled: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
marketplaceLoadErrors: [],
|
||||
} satisfies v2.PluginInstalledResponse;
|
||||
|
||||
const inventory = await readCodexPluginInventory({
|
||||
pluginConfig: {
|
||||
codexPlugins: {
|
||||
enabled: true,
|
||||
plugins: {
|
||||
"google-calendar": {
|
||||
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
||||
pluginName: "google-calendar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
appCache,
|
||||
appCacheKey: "runtime",
|
||||
nowMs: 1,
|
||||
request: async (method, params) => {
|
||||
if (method === "plugin/installed") {
|
||||
return listed;
|
||||
}
|
||||
if (method === "plugin/read") {
|
||||
expect(params).toEqual({
|
||||
marketplacePath: "/codex-home/.tmp/plugins/.agents/plugins/api_marketplace.json",
|
||||
pluginName: "google-calendar",
|
||||
});
|
||||
return pluginDetail("google-calendar", [appSummary("google-calendar-app")]);
|
||||
}
|
||||
throw new Error(`unexpected request ${method}`);
|
||||
},
|
||||
});
|
||||
|
||||
expect(inventory.records[0]?.ownedAppIds).toStrictEqual(["google-calendar-app"]);
|
||||
expect(inventory.records[0]?.apps[0]?.accessible).toBe(true);
|
||||
expect(inventory.diagnostics).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it("fails closed when an installed remote curated plugin omits its opaque id", async () => {
|
||||
const calls: string[] = [];
|
||||
const inventory = await readCodexPluginInventory({
|
||||
|
||||
@@ -23,6 +23,11 @@ import type {
|
||||
import type { CodexAppServerRequestResult, v2 } from "./protocol.js";
|
||||
|
||||
const CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME = `${CODEX_PLUGINS_MARKETPLACE_NAME}-remote`;
|
||||
// Codex serves the curated catalog under this wire name for API-key/Bedrock
|
||||
// accounts (codex-rs/core-plugins is_openai_curated_marketplace_name). It is
|
||||
// the same logical catalog, so configured `openai-curated` plugins resolve
|
||||
// from it and marketplace refs normalize back to CODEX_PLUGINS_MARKETPLACE_NAME.
|
||||
const CODEX_PLUGINS_API_MARKETPLACE_NAME = "openai-api-curated";
|
||||
|
||||
/** Request callback used to call Codex app-server plugin/app methods. */
|
||||
export type CodexPluginRuntimeRequest = (method: string, params?: unknown) => Promise<unknown>;
|
||||
@@ -545,15 +550,11 @@ function marketplaceRef(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* True for either supported OpenAI curated marketplace wire name. Codex also
|
||||
* counts `openai-api-curated` (API-key/Bedrock accounts) as curated, but
|
||||
* OpenClaw's native plugin flows are ChatGPT-account scoped, so that catalog
|
||||
* stays out of discovery and activation until it gets end-to-end support.
|
||||
*/
|
||||
/** True for any supported OpenAI curated marketplace wire name, matching Codex's own curated predicate. */
|
||||
export function isOpenAiCuratedMarketplace(marketplace: v2.PluginMarketplaceEntry): boolean {
|
||||
return (
|
||||
marketplace.name === CODEX_PLUGINS_MARKETPLACE_NAME ||
|
||||
marketplace.name === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME
|
||||
marketplace.name === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME ||
|
||||
marketplace.name === CODEX_PLUGINS_API_MARKETPLACE_NAME
|
||||
);
|
||||
}
|
||||
|
||||
@@ -556,6 +556,48 @@ describe("buildCodexMigrationProvider", () => {
|
||||
expect(sourceAppServerClientScope).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("discovers installed plugins from the API-key curated marketplace", async () => {
|
||||
const fixture = await createCodexFixture();
|
||||
appServerRequest.mockImplementation(async ({ method }: { method: string }) => {
|
||||
if (method === "plugin/installed") {
|
||||
return {
|
||||
marketplaces: [
|
||||
{
|
||||
name: "openai-api-curated",
|
||||
path: path.join(
|
||||
fixture.codexHome,
|
||||
".tmp/plugins/.agents/plugins/api_marketplace.json",
|
||||
),
|
||||
interface: null,
|
||||
plugins: [
|
||||
pluginSummary("google-calendar@openai-api-curated", {
|
||||
name: "google-calendar",
|
||||
installed: true,
|
||||
enabled: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
marketplaceLoadErrors: [],
|
||||
} satisfies v2.PluginInstalledResponse;
|
||||
}
|
||||
throw new Error(`unexpected request ${method}`);
|
||||
});
|
||||
|
||||
const source = await discoverCodexSource({ input: fixture.codexHome });
|
||||
|
||||
expect(source.pluginDiscoveryError).toBeUndefined();
|
||||
expect(source.plugins).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
pluginName: "google-calendar",
|
||||
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
||||
migratable: true,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores unrelated marketplace errors when no curated plugins are installed", async () => {
|
||||
const fixture = await createCodexFixture();
|
||||
appServerRequest.mockImplementation(async ({ method }: { method: string }) => {
|
||||
|
||||
@@ -199,7 +199,10 @@ function discoverInstalledCuratedPluginSources(
|
||||
if (!isOpenAiCuratedMarketplace(marketplace)) {
|
||||
continue;
|
||||
}
|
||||
const remote = marketplace.name !== CODEX_PLUGINS_MARKETPLACE_NAME;
|
||||
// Remote catalog entries carry no local path; the API-key curated variant
|
||||
// (`openai-api-curated`) is local like `openai-curated` and must not be
|
||||
// routed through remote plugin ids it does not have.
|
||||
const remote = !marketplace.path;
|
||||
for (const summary of marketplace.plugins) {
|
||||
if (!summary.installed) {
|
||||
continue;
|
||||
@@ -540,6 +543,7 @@ function pluginNameFromSummary(summary: v2.PluginSummary): string | undefined {
|
||||
}
|
||||
const marketplaceSuffix = [
|
||||
`@${CODEX_PLUGINS_MARKETPLACE_NAME}-remote`,
|
||||
`@openai-api-curated`,
|
||||
`@${CODEX_PLUGINS_MARKETPLACE_NAME}`,
|
||||
].find((suffix) => trimmed.endsWith(suffix));
|
||||
const withoutMarketplaceSuffix = marketplaceSuffix
|
||||
|
||||
Reference in New Issue
Block a user