feat: expand codex app server discovery
This commit is contained in:
@@ -73,6 +73,16 @@ test("device detail exposes Codex App Server discovered model and extension summ
|
||||
skills: [{ name: "image2-ui-prototype" }],
|
||||
plugins: [{ id: "github" }],
|
||||
apps: [{ id: "canva" }],
|
||||
experimentalFeatures: [
|
||||
{ name: "multi_agent", stage: "stable", enabled: true },
|
||||
{ name: "apps", stage: "beta", enabled: false },
|
||||
],
|
||||
collaborationModes: [{ id: "solo" }, { id: "plan" }],
|
||||
permissionProfiles: [{ id: ":workspace" }],
|
||||
mcpServers: [
|
||||
{ name: "github", toolCount: 2, authStatus: "oAuth" },
|
||||
{ name: "figma", toolCount: 0, authStatus: "notLoggedIn" },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -84,6 +94,7 @@ test("device detail exposes Codex App Server discovered model and extension summ
|
||||
assert.equal(cards.capabilities.items.codexAppServer, "Codex App Server:已连接");
|
||||
assert.equal(cards.capabilities.items.codexModels, "模型:2 个 · 默认 gpt-5.4 · 快速 gpt-5.4-mini · 深度 gpt-5.4");
|
||||
assert.equal(cards.capabilities.items.codexExtensions, "扩展:Skill 1 个 · Plugin 1 个 · App 1 个");
|
||||
assert.equal(cards.capabilities.items.codexGovernance, "治理:实验特性 2 个 · 协作模式 2 个 · MCP 2 个 · 权限 1 个");
|
||||
});
|
||||
|
||||
test("device detail exposes folder and project conflict skeleton from workspace policy", async () => {
|
||||
|
||||
@@ -59,6 +59,10 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
|
||||
defaultModelId: "gpt-5.4",
|
||||
fastModelId: "gpt-5.4-mini",
|
||||
providerCapabilities: { webSearch: true },
|
||||
experimentalFeatures: [{ name: "multi_agent", stage: "stable", enabled: true }],
|
||||
collaborationModes: [{ id: "plan" }],
|
||||
permissionProfiles: [{ id: ":workspace" }],
|
||||
mcpServers: [{ name: "github", toolCount: 2, authStatus: "oAuth" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -71,4 +75,14 @@ test("device heartbeat preserves Codex App Server capability metadata", async ()
|
||||
const updatedDevice = nextState.devices.find((item) => item.id === device!.id);
|
||||
assert.equal(updatedDevice?.capabilities?.codexAppServer.metadata?.models?.[0]?.id, "gpt-5.4");
|
||||
assert.equal(updatedDevice?.capabilities?.codexAppServer.metadata?.providerCapabilities?.webSearch, true);
|
||||
assert.equal(
|
||||
(updatedDevice?.capabilities?.codexAppServer.metadata?.experimentalFeatures as Array<{ name: string }> | undefined)?.[0]
|
||||
?.name,
|
||||
"multi_agent",
|
||||
);
|
||||
assert.equal(
|
||||
(updatedDevice?.capabilities?.codexAppServer.metadata?.mcpServers as Array<{ toolCount: number }> | undefined)?.[0]
|
||||
?.toolCount,
|
||||
2,
|
||||
);
|
||||
});
|
||||
|
||||
110
tests/fixtures/codex-app-server-runtime.mjs
vendored
110
tests/fixtures/codex-app-server-runtime.mjs
vendored
@@ -156,6 +156,116 @@ rl.on("line", (line) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "experimentalFeature/list") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
data: [
|
||||
{
|
||||
name: "multi_agent",
|
||||
stage: "stable",
|
||||
displayName: "Multi agent",
|
||||
description: "Allow spawned agents to coordinate work",
|
||||
announcement: "internal token=sk-secret-should-not-leak",
|
||||
enabled: true,
|
||||
defaultEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "apps",
|
||||
stage: "beta",
|
||||
displayName: "Apps",
|
||||
description: "Enable app connectors",
|
||||
announcement: null,
|
||||
enabled: false,
|
||||
defaultEnabled: false,
|
||||
},
|
||||
],
|
||||
nextCursor: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "collaborationMode/list") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
data: [
|
||||
{
|
||||
id: "solo",
|
||||
name: "solo",
|
||||
displayName: "Solo",
|
||||
description: "Single-thread execution",
|
||||
},
|
||||
{
|
||||
id: "plan",
|
||||
name: "plan",
|
||||
displayName: "Plan",
|
||||
description: "Plan before coding",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "permissionProfile/list") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
data: [
|
||||
{
|
||||
id: ":workspace",
|
||||
description: "Workspace write",
|
||||
filesystem: {
|
||||
"/Users/kris/code/boss": "write",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: ":read-only",
|
||||
description: "Read-only",
|
||||
},
|
||||
],
|
||||
nextCursor: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "mcpServerStatus/list") {
|
||||
send({
|
||||
id: message.id,
|
||||
result: {
|
||||
data: [
|
||||
{
|
||||
name: "github",
|
||||
tools: {
|
||||
"repos/list": { name: "repos/list", description: "List repositories" },
|
||||
"issues/read": { name: "issues/read", description: "Read issues" },
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
name: "private repo token=sk-secret-should-not-leak",
|
||||
uri: "file:///Users/kris/.ssh/id_ed25519",
|
||||
},
|
||||
],
|
||||
resourceTemplates: [],
|
||||
authStatus: "oAuth",
|
||||
},
|
||||
{
|
||||
name: "figma",
|
||||
tools: {},
|
||||
resources: [],
|
||||
resourceTemplates: [],
|
||||
authStatus: "notLoggedIn",
|
||||
},
|
||||
],
|
||||
nextCursor: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.method === "thread/resume") {
|
||||
send({
|
||||
id: message.id,
|
||||
|
||||
@@ -8,6 +8,7 @@ import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
discoverCodexAppServerCapabilities,
|
||||
executeCodexAppServerTask,
|
||||
getCodexAppServerRunnerConfig,
|
||||
shouldUseCodexAppServerTaskRunner,
|
||||
@@ -15,6 +16,35 @@ import {
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
test("codex app-server discovery includes governance and MCP summaries without leaking internals", async () => {
|
||||
const runnerConfig = getCodexAppServerRunnerConfig(process.env, {
|
||||
codexAppServerEnabled: true,
|
||||
codexAppServerCommand: process.execPath,
|
||||
codexAppServerArgs: ["tests/fixtures/codex-app-server-runtime.mjs"],
|
||||
codexAppServerWorkdir: repoRoot,
|
||||
codexAppServerTimeoutMs: 5000,
|
||||
codexAppServerDiscoveryLimit: 20,
|
||||
});
|
||||
|
||||
const metadata = await discoverCodexAppServerCapabilities(runnerConfig);
|
||||
|
||||
assert.equal(metadata.experimentalFeatures[0].name, "multi_agent");
|
||||
assert.equal(metadata.experimentalFeatures[0].stage, "stable");
|
||||
assert.equal(metadata.experimentalFeatures[0].enabled, true);
|
||||
assert.equal(metadata.collaborationModes[1].id, "plan");
|
||||
assert.equal(metadata.permissionProfiles[0].id, ":workspace");
|
||||
assert.equal(metadata.mcpServers[0].name, "github");
|
||||
assert.equal(metadata.mcpServers[0].toolCount, 2);
|
||||
assert.equal(metadata.mcpServers[0].authStatus, "oAuth");
|
||||
|
||||
const serialized = JSON.stringify(metadata);
|
||||
assert.equal(serialized.includes("sk-secret-should-not-leak"), false);
|
||||
assert.equal(serialized.includes("/Users/kris"), false);
|
||||
assert.equal(serialized.includes("id_ed25519"), false);
|
||||
assert.equal(serialized.includes("filesystem"), false);
|
||||
assert.equal(serialized.includes("resources"), false);
|
||||
});
|
||||
|
||||
function encodeWsTextFrame(value) {
|
||||
const payload = Buffer.from(value);
|
||||
if (payload.length < 126) {
|
||||
|
||||
Reference in New Issue
Block a user