@rzx/ohs 1.1.1 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +468 -34
- package/dist/index.js.map +10 -7
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -286055,8 +286055,8 @@ function createDaemonImageToTextTool(options) {
|
|
|
286055
286055
|
};
|
|
286056
286056
|
}
|
|
286057
286057
|
async function recognizeAttachment(input, context, options) {
|
|
286058
|
-
if (Object.keys(input).some((key) => key !== "attachment_id")) {
|
|
286059
|
-
return errorResult4("attachment_id cannot be combined with
|
|
286058
|
+
if (Object.keys(input).some((key) => key !== "attachment_id" && key !== "prompt")) {
|
|
286059
|
+
return errorResult4("attachment_id cannot be combined with another image source", "command");
|
|
286060
286060
|
}
|
|
286061
286061
|
const assetId = typeof input.attachment_id === "string" ? input.attachment_id.trim() : "";
|
|
286062
286062
|
if (!assetId || !context.sessionId)
|
|
@@ -370567,12 +370567,12 @@ function createMcpCommand() {
|
|
|
370567
370567
|
// src/commands/plugin.ts
|
|
370568
370568
|
init_src15();
|
|
370569
370569
|
init_src12();
|
|
370570
|
-
import { join as
|
|
370571
|
-
import { mkdtemp as mkdtemp3, rm as
|
|
370570
|
+
import { join as join49, resolve as resolve31 } from "node:path";
|
|
370571
|
+
import { mkdtemp as mkdtemp3, rm as rm9 } from "node:fs/promises";
|
|
370572
370572
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
370573
370573
|
// ../../packages/plugin-converters/src/core/digest.ts
|
|
370574
370574
|
import { createHash as createHash13 } from "node:crypto";
|
|
370575
|
-
import { readFile as readFile18, readdir as readdir9 } from "node:fs/promises";
|
|
370575
|
+
import { lstat as lstat6, readFile as readFile18, readdir as readdir9 } from "node:fs/promises";
|
|
370576
370576
|
import { join as join43, relative as relative10 } from "node:path";
|
|
370577
370577
|
function stable(value) {
|
|
370578
370578
|
if (Array.isArray(value))
|
|
@@ -370598,12 +370598,20 @@ async function files(root, dir = root) {
|
|
|
370598
370598
|
return out;
|
|
370599
370599
|
}
|
|
370600
370600
|
async function digestSource(root) {
|
|
370601
|
+
if ((await lstat6(root)).isSymbolicLink())
|
|
370602
|
+
throw new Error("Source root link is not allowed");
|
|
370601
370603
|
const hash = createHash13("sha256");
|
|
370604
|
+
hash.update(`openharness-source-v2
|
|
370605
|
+
`);
|
|
370602
370606
|
for (const file of await files(root)) {
|
|
370603
|
-
|
|
370604
|
-
hash.update(
|
|
370605
|
-
|
|
370606
|
-
|
|
370607
|
+
const content = await readFile18(file);
|
|
370608
|
+
hash.update(JSON.stringify([
|
|
370609
|
+
relative10(root, file).replaceAll("\\", "/"),
|
|
370610
|
+
content.length,
|
|
370611
|
+
createHash13("sha256").update(content).digest("hex")
|
|
370612
|
+
]));
|
|
370613
|
+
hash.update(`
|
|
370614
|
+
`);
|
|
370607
370615
|
}
|
|
370608
370616
|
return hash.digest("hex");
|
|
370609
370617
|
}
|
|
@@ -370660,13 +370668,13 @@ async function detectClaudeCodePlugin(root) {
|
|
|
370660
370668
|
return { converterId: "claude-code", confidence: evidence.includes(".claude-plugin/plugin.json") ? 1 : 0.7, evidence };
|
|
370661
370669
|
}
|
|
370662
370670
|
// ../../packages/plugin-converters/src/claude-code/parser.ts
|
|
370663
|
-
import { access as access7, lstat as
|
|
370671
|
+
import { access as access7, lstat as lstat7, readFile as readFile19, readdir as readdir10 } from "node:fs/promises";
|
|
370664
370672
|
import { basename as basename13, isAbsolute as isAbsolute13, join as join45, relative as relative11, resolve as resolve27 } from "node:path";
|
|
370665
370673
|
var exists5 = async (path) => access7(path).then(() => true, () => false);
|
|
370666
370674
|
async function collect(root, path, extensions) {
|
|
370667
370675
|
if (!await exists5(path))
|
|
370668
370676
|
return [];
|
|
370669
|
-
const info = await
|
|
370677
|
+
const info = await lstat7(path);
|
|
370670
370678
|
if (info.isSymbolicLink())
|
|
370671
370679
|
throw new Error(`Claude source symlink is not allowed: ${path}`);
|
|
370672
370680
|
if (info.isFile())
|
|
@@ -371007,10 +371015,428 @@ class ClaudeCodePluginConverter {
|
|
|
371007
371015
|
}
|
|
371008
371016
|
}
|
|
371009
371017
|
}
|
|
371018
|
+
// ../../packages/plugin-converters/src/codex/converter.ts
|
|
371019
|
+
init_src12();
|
|
371020
|
+
import { randomUUID as randomUUID28 } from "node:crypto";
|
|
371021
|
+
import { copyFile as copyFile2, lstat as lstat9, mkdir as mkdir15, readFile as readFile23, realpath as realpath8, rename as rename5, rm as rm8, writeFile as writeFile12 } from "node:fs/promises";
|
|
371022
|
+
import { dirname as dirname24, join as join48, relative as relative14, resolve as resolve30 } from "node:path";
|
|
371023
|
+
|
|
371024
|
+
// ../../packages/plugin-converters/src/codex/source.ts
|
|
371025
|
+
import { lstat as lstat8, readFile as readFile21, readdir as readdir11 } from "node:fs/promises";
|
|
371026
|
+
import { dirname as dirname23, isAbsolute as isAbsolute14, join as join47, relative as relative13, resolve as resolve29 } from "node:path";
|
|
371027
|
+
var PORTABLE_PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
371028
|
+
var legacyManifest = ".codex-plugin/plugin.json";
|
|
371029
|
+
function object3(value, label) {
|
|
371030
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
371031
|
+
throw new Error(`${label} must be an object`);
|
|
371032
|
+
return value;
|
|
371033
|
+
}
|
|
371034
|
+
async function exists6(path) {
|
|
371035
|
+
try {
|
|
371036
|
+
await lstat8(path);
|
|
371037
|
+
return true;
|
|
371038
|
+
} catch (error) {
|
|
371039
|
+
if (error.code === "ENOENT")
|
|
371040
|
+
return false;
|
|
371041
|
+
throw error;
|
|
371042
|
+
}
|
|
371043
|
+
}
|
|
371044
|
+
async function sourceFiles(root, directory = root) {
|
|
371045
|
+
const info = await lstat8(directory);
|
|
371046
|
+
if (info.isSymbolicLink())
|
|
371047
|
+
throw new Error(`Codex source link is not allowed: ${directory}`);
|
|
371048
|
+
if (!info.isDirectory())
|
|
371049
|
+
throw new Error(`Codex source must be a directory: ${directory}`);
|
|
371050
|
+
const files = [];
|
|
371051
|
+
for (const entry of (await readdir11(directory, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
371052
|
+
const full = join47(directory, entry.name);
|
|
371053
|
+
if (entry.isSymbolicLink())
|
|
371054
|
+
throw new Error(`Codex source link is not allowed: ${full}`);
|
|
371055
|
+
if (entry.isDirectory())
|
|
371056
|
+
files.push(...await sourceFiles(root, full));
|
|
371057
|
+
else if (entry.isFile())
|
|
371058
|
+
files.push(relative13(root, full).replaceAll("\\", "/"));
|
|
371059
|
+
else
|
|
371060
|
+
throw new Error(`Unsupported Codex source file type: ${full}`);
|
|
371061
|
+
}
|
|
371062
|
+
return files;
|
|
371063
|
+
}
|
|
371064
|
+
function isWithin3(root, path) {
|
|
371065
|
+
const rel = relative13(resolve29(root), resolve29(path));
|
|
371066
|
+
return rel === "" || !isAbsolute14(rel) && rel !== ".." && !rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
|
|
371067
|
+
}
|
|
371068
|
+
function pathDeclaration(root, value) {
|
|
371069
|
+
if (!value.startsWith("./") || /[\\:\0]/.test(value) || value.split("/").includes("..")) {
|
|
371070
|
+
throw new Error(`Invalid Codex component path: ${value}`);
|
|
371071
|
+
}
|
|
371072
|
+
const full = resolve29(root, value);
|
|
371073
|
+
if (full === root || !isWithin3(root, full))
|
|
371074
|
+
throw new Error(`Codex component path escapes source: ${value}`);
|
|
371075
|
+
const path = relative13(root, full).replaceAll("\\", "/");
|
|
371076
|
+
if (path.split("/").some((part) => [".git", ".codex-plugin", ".claude-plugin", ".openharness-plugin", ".openharness-conversion"].includes(part))) {
|
|
371077
|
+
throw new Error(`Reserved Codex component path: ${value}`);
|
|
371078
|
+
}
|
|
371079
|
+
return path;
|
|
371080
|
+
}
|
|
371081
|
+
async function declaredPaths(root, value, label, defaults) {
|
|
371082
|
+
if (value === undefined)
|
|
371083
|
+
return (await Promise.all(defaults.map(async (path) => await exists6(join47(root, path)) ? path : undefined))).filter((path) => path !== undefined);
|
|
371084
|
+
const values = typeof value === "string" ? [value] : value;
|
|
371085
|
+
if (!Array.isArray(values) || !values.every((path) => typeof path === "string"))
|
|
371086
|
+
throw new Error(`${label} must be a path or path array`);
|
|
371087
|
+
const paths = values.map((path) => pathDeclaration(root, path));
|
|
371088
|
+
for (const path of paths)
|
|
371089
|
+
if (!await exists6(join47(root, path)))
|
|
371090
|
+
throw new Error(`Codex component path does not exist: ${path}`);
|
|
371091
|
+
return [...new Set(paths)];
|
|
371092
|
+
}
|
|
371093
|
+
async function readJson2(root, path) {
|
|
371094
|
+
return object3(JSON.parse(await readFile21(join47(root, path), "utf8")), path);
|
|
371095
|
+
}
|
|
371096
|
+
async function detectCodexPlugin(rootInput) {
|
|
371097
|
+
const root = resolve29(rootInput);
|
|
371098
|
+
if (await exists6(join47(root, legacyManifest)))
|
|
371099
|
+
return { converterId: "codex", confidence: 1, evidence: [legacyManifest] };
|
|
371100
|
+
if (!await exists6(join47(root, "plugin.json")))
|
|
371101
|
+
return null;
|
|
371102
|
+
await sourceFiles(root);
|
|
371103
|
+
const manifest = await readJson2(root, "plugin.json");
|
|
371104
|
+
return manifest.$schema === PORTABLE_PLUGIN_SCHEMA ? { converterId: "codex", confidence: 1, evidence: [`plugin.json:${PORTABLE_PLUGIN_SCHEMA}`] } : null;
|
|
371105
|
+
}
|
|
371106
|
+
async function readCodexSource(rootInput) {
|
|
371107
|
+
const root = resolve29(rootInput);
|
|
371108
|
+
const files = await sourceFiles(root);
|
|
371109
|
+
const portableCandidate = files.includes("plugin.json") ? await readJson2(root, "plugin.json") : undefined;
|
|
371110
|
+
if (typeof portableCandidate?.$schema === "string" && portableCandidate.$schema.startsWith("https://agent-plugins.org/") && portableCandidate.$schema !== PORTABLE_PLUGIN_SCHEMA) {
|
|
371111
|
+
throw new Error(`Unsupported portable plugin schema: ${portableCandidate.$schema}`);
|
|
371112
|
+
}
|
|
371113
|
+
const portable = portableCandidate?.$schema === PORTABLE_PLUGIN_SCHEMA;
|
|
371114
|
+
const extensions = portable && portableCandidate.extensions !== undefined ? object3(portableCandidate.extensions, "extensions") : {};
|
|
371115
|
+
const inline = extensions["com.openai"];
|
|
371116
|
+
const legacy = (!portable || inline === undefined) && files.includes(legacyManifest) ? await readJson2(root, legacyManifest) : undefined;
|
|
371117
|
+
const manifest = portable ? portableCandidate : legacy;
|
|
371118
|
+
if (!manifest)
|
|
371119
|
+
throw new Error("Codex manifest is missing or the portable schema is not supported");
|
|
371120
|
+
if (typeof manifest.name !== "string" || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(manifest.name))
|
|
371121
|
+
throw new Error("Codex name must use kebab-case");
|
|
371122
|
+
if (manifest.version !== undefined && (typeof manifest.version !== "string" || !manifest.version.trim()))
|
|
371123
|
+
throw new Error("Codex version must be a nonempty string");
|
|
371124
|
+
const settings = portable ? extensions["com.openai"] === undefined ? legacy ?? {} : object3(extensions["com.openai"], "extensions.com.openai") : manifest;
|
|
371125
|
+
const inventory = {};
|
|
371126
|
+
const declaredSkillRoots = await declaredPaths(root, portable ? undefined : settings.skills, "skills", ["skills"]);
|
|
371127
|
+
const skillRoots = declaredSkillRoots.filter((path) => !declaredSkillRoots.some((parent) => parent !== path && path.startsWith(`${parent}/`)));
|
|
371128
|
+
inventory.skills = [];
|
|
371129
|
+
for (const path of skillRoots) {
|
|
371130
|
+
const skills = files.filter((file) => (file === path || file.startsWith(`${path}/`)) && file.split("/").at(-1) === "SKILL.md");
|
|
371131
|
+
if (!skills.length)
|
|
371132
|
+
throw new Error(`Codex skill path contains no SKILL.md: ${path}`);
|
|
371133
|
+
inventory.skills.push(...skills.map((file) => join47(root, file)));
|
|
371134
|
+
}
|
|
371135
|
+
inventory.skills = [...new Set(inventory.skills)];
|
|
371136
|
+
const mcp = [];
|
|
371137
|
+
const mcpDeclaration = portable ? undefined : settings.mcpServers;
|
|
371138
|
+
if (mcpDeclaration && typeof mcpDeclaration === "object" && !Array.isArray(mcpDeclaration)) {
|
|
371139
|
+
const raw = object3(mcpDeclaration, "mcpServers");
|
|
371140
|
+
mcp.push({ path: `${legacyManifest}#mcpServers`, servers: object3(raw.mcpServers ?? raw, "mcpServers"), portable: false });
|
|
371141
|
+
} else {
|
|
371142
|
+
for (const path of await declaredPaths(root, mcpDeclaration, "mcpServers", [portable ? "mcp.json" : ".mcp.json"])) {
|
|
371143
|
+
const raw = await readJson2(root, path);
|
|
371144
|
+
mcp.push({ path, servers: object3(raw.mcpServers, `${path}.mcpServers`), portable });
|
|
371145
|
+
}
|
|
371146
|
+
}
|
|
371147
|
+
inventory.mcpServers = mcp.map((value) => value.path);
|
|
371148
|
+
const unsupported = [];
|
|
371149
|
+
if (portable) {
|
|
371150
|
+
const portableFields = new Set(["$schema", "name", "version", "description", "author", "homepage", "repository", "license", "keywords", "extensions"]);
|
|
371151
|
+
for (const key of Object.keys(manifest).filter((key) => !portableFields.has(key))) {
|
|
371152
|
+
unsupported.push({ kind: "manifest", path: `plugin.json#${key}`, reason: `Unrecognized portable manifest field: ${key}` });
|
|
371153
|
+
}
|
|
371154
|
+
for (const [key, extension] of Object.entries(extensions)) {
|
|
371155
|
+
if (key === "com.openai")
|
|
371156
|
+
continue;
|
|
371157
|
+
object3(extension, `extensions.${key}`);
|
|
371158
|
+
unsupported.push({ kind: "extensions", path: `plugin.json#extensions.${key}`, reason: `Portable extension ${key} has no Native conversion` });
|
|
371159
|
+
}
|
|
371160
|
+
}
|
|
371161
|
+
for (const skill of inventory.skills) {
|
|
371162
|
+
const path = relative13(root, join47(dirname23(skill), "agents/openai.yaml")).replaceAll("\\", "/");
|
|
371163
|
+
if (files.includes(path))
|
|
371164
|
+
unsupported.push({ kind: "skillMetadata", path, reason: "Codex skill invocation policy, tool dependencies and presentation in agents/openai.yaml are not applied by Native" });
|
|
371165
|
+
}
|
|
371166
|
+
for (const kind of ["apps", "hooks", "agents", "commands", "lspServers", "tools", "workflows", "channels", "providers", "ui", "outputStyles", "themes", "monitors", "binaries"]) {
|
|
371167
|
+
const declaration = settings[kind];
|
|
371168
|
+
const defaults = kind === "hooks" ? ["hooks/hooks.json"] : [];
|
|
371169
|
+
let paths;
|
|
371170
|
+
if (declaration !== null && typeof declaration === "object" && (!Array.isArray(declaration) || declaration.some((item) => typeof item !== "string"))) {
|
|
371171
|
+
paths = [`${portable ? "plugin.json" : legacyManifest}#${kind}`];
|
|
371172
|
+
} else
|
|
371173
|
+
paths = await declaredPaths(root, declaration, kind, defaults);
|
|
371174
|
+
for (const path of paths)
|
|
371175
|
+
unsupported.push({ kind, path, reason: `Codex ${kind} has no supported Native conversion in this version` });
|
|
371176
|
+
if (paths.length)
|
|
371177
|
+
inventory[kind] = paths;
|
|
371178
|
+
}
|
|
371179
|
+
const known = new Set(["$schema", "name", "version", "description", "author", "homepage", "repository", "license", "keywords", "skills", "mcpServers", "interface", "extensions", ...unsupported.map((item) => item.kind)]);
|
|
371180
|
+
for (const key of Object.keys(settings).filter((key) => !known.has(key))) {
|
|
371181
|
+
unsupported.push({ kind: key, path: `${portable ? "plugin.json" : legacyManifest}#${key}`, reason: `Unrecognized Codex manifest field: ${key}` });
|
|
371182
|
+
}
|
|
371183
|
+
const ui = settings.interface === undefined ? {} : object3(settings.interface, "interface");
|
|
371184
|
+
return {
|
|
371185
|
+
inspection: { root, format: "codex", identity: { id: `converted.codex.${manifest.name}`, name: manifest.name, version: manifest.version ?? "0.0.0" }, inventory, diagnostics: [] },
|
|
371186
|
+
files,
|
|
371187
|
+
metadataPaths: [...portable ? ["plugin.json"] : [], ...mcp.map((item) => item.path), ...unsupported.map((item) => item.path)],
|
|
371188
|
+
skillRoots,
|
|
371189
|
+
mcp,
|
|
371190
|
+
unsupported,
|
|
371191
|
+
...typeof manifest.description === "string" ? { description: manifest.description } : {},
|
|
371192
|
+
...typeof ui.displayName === "string" && ui.displayName.trim() ? { displayName: ui.displayName } : {}
|
|
371193
|
+
};
|
|
371194
|
+
}
|
|
371195
|
+
|
|
371196
|
+
// ../../packages/plugin-converters/src/codex/mcp.ts
|
|
371197
|
+
function stringMap(value) {
|
|
371198
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
|
|
371199
|
+
}
|
|
371200
|
+
function mapCodexMcp(value, portable) {
|
|
371201
|
+
let row;
|
|
371202
|
+
try {
|
|
371203
|
+
row = object3(value, "MCP server");
|
|
371204
|
+
} catch {
|
|
371205
|
+
return { reason: "MCP server must be an object" };
|
|
371206
|
+
}
|
|
371207
|
+
const unknown = Object.keys(row).filter((key) => !["type", "command", "args", "env", "url", "headers"].includes(key));
|
|
371208
|
+
if (unknown.length)
|
|
371209
|
+
return { reason: `MCP fields have no verified Native equivalent: ${unknown.sort().join(", ")}` };
|
|
371210
|
+
if (/\$\{|\$[A-Z_][A-Z0-9_]*|%[A-Z_][A-Z0-9_]*%/.test(JSON.stringify(row)))
|
|
371211
|
+
return { reason: "MCP environment or plugin-root interpolation is not supported" };
|
|
371212
|
+
if (row.command !== undefined && row.url !== undefined)
|
|
371213
|
+
return { reason: "MCP command and url cannot be combined" };
|
|
371214
|
+
if (typeof row.url === "string") {
|
|
371215
|
+
if (row.type !== undefined && row.type !== "http" && row.type !== "streamable-http" && row.type !== "sse")
|
|
371216
|
+
return { reason: "Unsupported MCP transport" };
|
|
371217
|
+
if (portable && row.type !== "streamable-http")
|
|
371218
|
+
return { reason: "Portable HTTP MCP requires streamable-http transport" };
|
|
371219
|
+
if (row.args !== undefined || row.env !== undefined || row.headers !== undefined && !stringMap(row.headers))
|
|
371220
|
+
return { reason: "Unsupported HTTP MCP options" };
|
|
371221
|
+
let url;
|
|
371222
|
+
try {
|
|
371223
|
+
url = new URL(row.url);
|
|
371224
|
+
} catch {
|
|
371225
|
+
return { reason: "Invalid MCP URL" };
|
|
371226
|
+
}
|
|
371227
|
+
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.hash)
|
|
371228
|
+
return { reason: "MCP requires an HTTP(S) URL without credentials or a fragment" };
|
|
371229
|
+
return { config: { type: row.type === "sse" ? "sse" : "http", url: row.url, ...row.headers === undefined ? {} : { headers: row.headers } }, permissions: { network: [url.origin] }, reason: "MCP transport is adapted to Native; connection and authentication happen after installation" };
|
|
371230
|
+
}
|
|
371231
|
+
if (typeof row.command === "string" && /^[a-zA-Z0-9_-]+(?:\.exe)?$/.test(row.command)) {
|
|
371232
|
+
if (row.type !== undefined && row.type !== "stdio" || portable && row.type !== "stdio" || row.headers !== undefined)
|
|
371233
|
+
return { reason: "Unsupported stdio MCP options" };
|
|
371234
|
+
if (row.args !== undefined && (!Array.isArray(row.args) || !row.args.every((item) => typeof item === "string")))
|
|
371235
|
+
return { reason: "MCP args must be a string array" };
|
|
371236
|
+
if (row.env !== undefined && !stringMap(row.env))
|
|
371237
|
+
return { reason: "MCP env must contain strings" };
|
|
371238
|
+
const args = row.args ?? [];
|
|
371239
|
+
if (["npx", "npm", "pnpm", "yarn", "bunx", "uvx", "sh", "bash", "cmd", "powershell", "pwsh"].includes(row.command.toLowerCase().replace(/\.exe$/, "")) || args.some((arg) => /[\\/]|\.(?:[cm]?js|ts|py|sh|exe)$/.test(arg) || ["-e", "--eval", "-c", "--command"].includes(arg)))
|
|
371240
|
+
return { reason: "MCP local paths, shell code or dependency execution require explicit preparation not supported by this converter" };
|
|
371241
|
+
return { config: { type: "stdio", command: row.command, ...row.args === undefined ? {} : { args }, ...row.env === undefined ? {} : { env: row.env } }, permissions: { process: [row.command] }, reason: "MCP runs an already-installed executable from PATH; availability is checked when connecting" };
|
|
371242
|
+
}
|
|
371243
|
+
return { reason: "MCP requires a supported remote URL or an installed executable name" };
|
|
371244
|
+
}
|
|
371245
|
+
|
|
371246
|
+
// ../../packages/plugin-converters/src/codex/converter.ts
|
|
371247
|
+
var VERSION3 = "1.0.0";
|
|
371248
|
+
var MAPPING_VERSION = "1";
|
|
371249
|
+
async function exists7(path) {
|
|
371250
|
+
try {
|
|
371251
|
+
await lstat9(path);
|
|
371252
|
+
return true;
|
|
371253
|
+
} catch (error) {
|
|
371254
|
+
if (error.code === "ENOENT")
|
|
371255
|
+
return false;
|
|
371256
|
+
throw error;
|
|
371257
|
+
}
|
|
371258
|
+
}
|
|
371259
|
+
async function prepare(inspection) {
|
|
371260
|
+
const before = await digestSource(inspection.root);
|
|
371261
|
+
const source = await readCodexSource(inspection.root);
|
|
371262
|
+
if (digestValue(source.inspection) !== digestValue(inspection))
|
|
371263
|
+
throw new Error("Source inspection changed; inspect and plan again");
|
|
371264
|
+
const items = (inspection.inventory.skills ?? []).map((path) => {
|
|
371265
|
+
const sourcePath = relative14(inspection.root, path).replaceAll("\\", "/");
|
|
371266
|
+
return { id: `skills:${sourcePath}`, sourceKind: "skills", sourcePath, targetKind: "skills", fidelity: "exact" };
|
|
371267
|
+
});
|
|
371268
|
+
const servers = Object.create(null);
|
|
371269
|
+
const seenServers = new Set;
|
|
371270
|
+
const permissions = {};
|
|
371271
|
+
for (const contribution of source.mcp) {
|
|
371272
|
+
for (const [name, raw] of Object.entries(contribution.servers).sort(([a], [b]) => a.localeCompare(b))) {
|
|
371273
|
+
if (!name.trim() || ["__proto__", "prototype", "constructor"].includes(name) || seenServers.has(name))
|
|
371274
|
+
throw new Error(`Invalid or duplicate Codex MCP server name: ${name}`);
|
|
371275
|
+
seenServers.add(name);
|
|
371276
|
+
const mapped = mapCodexMcp(raw, contribution.portable);
|
|
371277
|
+
const id = `mcpServers:${contribution.path}#${name}`;
|
|
371278
|
+
const requiredApprovals = [id];
|
|
371279
|
+
if (mapped.config) {
|
|
371280
|
+
servers[name] = mapped.config;
|
|
371281
|
+
for (const category of ["network", "process"]) {
|
|
371282
|
+
for (const permission of mapped.permissions?.[category] ?? []) {
|
|
371283
|
+
permissions[category] = [...new Set([...permissions[category] ?? [], permission])].sort();
|
|
371284
|
+
requiredApprovals.push(`${category}:${permission}`);
|
|
371285
|
+
}
|
|
371286
|
+
}
|
|
371287
|
+
}
|
|
371288
|
+
items.push({
|
|
371289
|
+
id,
|
|
371290
|
+
sourceKind: "mcpServers",
|
|
371291
|
+
sourcePath: contribution.path,
|
|
371292
|
+
...mapped.config ? { targetKind: "mcpServers" } : {},
|
|
371293
|
+
fidelity: mapped.config ? "adapted" : "unsupported",
|
|
371294
|
+
reason: mapped.reason,
|
|
371295
|
+
requiredApprovals
|
|
371296
|
+
});
|
|
371297
|
+
}
|
|
371298
|
+
}
|
|
371299
|
+
for (const value of source.unsupported) {
|
|
371300
|
+
const id = `${value.kind}:${value.path}`;
|
|
371301
|
+
items.push({ id, sourceKind: value.kind, sourcePath: value.path, fidelity: "unsupported", reason: value.reason, requiredApprovals: [id] });
|
|
371302
|
+
}
|
|
371303
|
+
const sourceDigest = await digestSource(inspection.root);
|
|
371304
|
+
if (before !== sourceDigest)
|
|
371305
|
+
throw new Error("Source changed during conversion planning; inspect and plan again");
|
|
371306
|
+
return {
|
|
371307
|
+
source,
|
|
371308
|
+
servers,
|
|
371309
|
+
permissions,
|
|
371310
|
+
plan: {
|
|
371311
|
+
schemaVersion: 1,
|
|
371312
|
+
converterId: "codex",
|
|
371313
|
+
converterVersion: VERSION3,
|
|
371314
|
+
sourceFormat: "codex",
|
|
371315
|
+
sourceDigest,
|
|
371316
|
+
optionsDigest: digestValue({}),
|
|
371317
|
+
mappingVersion: MAPPING_VERSION,
|
|
371318
|
+
items: items.sort((a, b) => a.id.localeCompare(b.id)),
|
|
371319
|
+
diagnostics: []
|
|
371320
|
+
}
|
|
371321
|
+
};
|
|
371322
|
+
}
|
|
371323
|
+
async function checkOutput(sourceRoot, output) {
|
|
371324
|
+
if (isWithin3(sourceRoot, output))
|
|
371325
|
+
throw new Error("Conversion output cannot be inside the source directory");
|
|
371326
|
+
if (await exists7(output))
|
|
371327
|
+
throw new Error(`Conversion output already exists: ${output}`);
|
|
371328
|
+
let parent = dirname24(output);
|
|
371329
|
+
while (!await exists7(parent))
|
|
371330
|
+
parent = dirname24(parent);
|
|
371331
|
+
const canonicalOutput = resolve30(await realpath8(parent), relative14(parent, output));
|
|
371332
|
+
if (isWithin3(await realpath8(sourceRoot), canonicalOutput))
|
|
371333
|
+
throw new Error("Conversion output resolves inside the source directory");
|
|
371334
|
+
}
|
|
371335
|
+
function isSourceMetadata(path, source) {
|
|
371336
|
+
const lower = path.toLowerCase();
|
|
371337
|
+
const top = lower.split("/")[0];
|
|
371338
|
+
if ([".git", ".codex-plugin", ".claude-plugin", ".openharness-plugin", ".openharness-conversion"].includes(top))
|
|
371339
|
+
return true;
|
|
371340
|
+
return source.metadataPaths.some((item) => item.toLowerCase() === lower);
|
|
371341
|
+
}
|
|
371342
|
+
|
|
371343
|
+
class CodexPluginConverter {
|
|
371344
|
+
id = "codex";
|
|
371345
|
+
version = VERSION3;
|
|
371346
|
+
sourceFormat = "codex";
|
|
371347
|
+
detect = detectCodexPlugin;
|
|
371348
|
+
async inspect(root) {
|
|
371349
|
+
return (await readCodexSource(root)).inspection;
|
|
371350
|
+
}
|
|
371351
|
+
async plan(inspection, options = {}) {
|
|
371352
|
+
if (Object.keys(options).length)
|
|
371353
|
+
throw new Error("Codex conversion options are not supported in this version");
|
|
371354
|
+
return (await prepare(inspection)).plan;
|
|
371355
|
+
}
|
|
371356
|
+
async convert(input) {
|
|
371357
|
+
const output = resolve30(input.output);
|
|
371358
|
+
await checkOutput(input.inspection.root, output);
|
|
371359
|
+
await readCodexSource(input.inspection.root);
|
|
371360
|
+
if (await digestSource(input.inspection.root) !== input.plan.sourceDigest)
|
|
371361
|
+
throw new Error("Source changed after conversion planning; inspect and plan again");
|
|
371362
|
+
const prepared = await prepare(input.inspection);
|
|
371363
|
+
if (digestValue(prepared.plan) !== digestValue(input.plan))
|
|
371364
|
+
throw new Error("Conversion plan does not match the current source and converter; plan again");
|
|
371365
|
+
const missing = [...new Set(prepared.plan.items.flatMap((item) => item.requiredApprovals ?? []))].filter((item) => !input.approvals?.includes(item));
|
|
371366
|
+
if (missing.length)
|
|
371367
|
+
throw new Error(`Explicit conversion approval required: ${missing.join(", ")}`);
|
|
371368
|
+
const components = {};
|
|
371369
|
+
const skillFiles = prepared.plan.items.filter((item) => item.sourceKind === "skills" && item.fidelity === "exact");
|
|
371370
|
+
if (skillFiles.length)
|
|
371371
|
+
components.skills = skillFiles.map((item) => `./${item.sourcePath}`);
|
|
371372
|
+
if (Object.keys(prepared.servers).length)
|
|
371373
|
+
components.mcpServers = ["./mcp/servers.json"];
|
|
371374
|
+
if (!Object.keys(components).length)
|
|
371375
|
+
throw new Error("Codex source contains no convertible components; see unsupported plan items");
|
|
371376
|
+
const manifest = {
|
|
371377
|
+
schemaVersion: 1,
|
|
371378
|
+
...input.inspection.identity,
|
|
371379
|
+
...prepared.source.description === undefined ? {} : { description: prepared.source.description },
|
|
371380
|
+
...prepared.source.displayName === undefined ? {} : { displayName: prepared.source.displayName },
|
|
371381
|
+
components,
|
|
371382
|
+
...Object.keys(prepared.permissions).length ? { permissions: prepared.permissions } : {},
|
|
371383
|
+
metadata: { origin: "converted", sourceFormat: this.sourceFormat, converterId: this.id, converterVersion: this.version }
|
|
371384
|
+
};
|
|
371385
|
+
const report = { schemaVersion: 1, status: input.plan.items.some((item) => item.fidelity === "unsupported") ? "partial" : "success", items: input.plan.items, diagnostics: input.plan.diagnostics };
|
|
371386
|
+
const temporary = `${output}.tmp-${randomUUID28()}`;
|
|
371387
|
+
const copied = [];
|
|
371388
|
+
try {
|
|
371389
|
+
await mkdir15(temporary, { recursive: true });
|
|
371390
|
+
for (const path of prepared.source.files) {
|
|
371391
|
+
if (isSourceMetadata(path, prepared.source))
|
|
371392
|
+
continue;
|
|
371393
|
+
if (components.mcpServers && path.toLowerCase() === "mcp/servers.json")
|
|
371394
|
+
throw new Error("Source resource conflicts with generated Native MCP path");
|
|
371395
|
+
const sourcePath = join48(input.inspection.root, path);
|
|
371396
|
+
if (!(await lstat9(sourcePath)).isFile())
|
|
371397
|
+
throw new Error(`Source file changed during conversion: ${path}`);
|
|
371398
|
+
const target = join48(temporary, path);
|
|
371399
|
+
await mkdir15(dirname24(target), { recursive: true });
|
|
371400
|
+
await copyFile2(sourcePath, target);
|
|
371401
|
+
copied.push(path);
|
|
371402
|
+
}
|
|
371403
|
+
const writeJson = async (path, value) => {
|
|
371404
|
+
const target = join48(temporary, path);
|
|
371405
|
+
await mkdir15(dirname24(target), { recursive: true });
|
|
371406
|
+
await writeFile12(target, JSON.stringify(value, null, 2));
|
|
371407
|
+
};
|
|
371408
|
+
if (components.mcpServers)
|
|
371409
|
+
await writeJson("mcp/servers.json", { servers: prepared.servers });
|
|
371410
|
+
await writeJson(".openharness-plugin/plugin.json", manifest);
|
|
371411
|
+
await writeJson(".openharness-conversion/plan.json", input.plan);
|
|
371412
|
+
await writeJson(".openharness-conversion/report.json", report);
|
|
371413
|
+
await writeJson(".openharness-conversion/provenance.json", { sourceFormat: this.sourceFormat, sourceDigest: input.plan.sourceDigest, converterId: this.id, converterVersion: this.version, mappingVersion: MAPPING_VERSION });
|
|
371414
|
+
await readCodexSource(input.inspection.root);
|
|
371415
|
+
if (await digestSource(input.inspection.root) !== input.plan.sourceDigest)
|
|
371416
|
+
throw new Error("Source changed during conversion; inspect and plan again");
|
|
371417
|
+
for (const path of copied) {
|
|
371418
|
+
if (!(await readFile23(join48(temporary, path))).equals(await readFile23(join48(input.inspection.root, path))))
|
|
371419
|
+
throw new Error(`Source changed while copying: ${path}`);
|
|
371420
|
+
}
|
|
371421
|
+
const validation = await validateNativePlugin(temporary);
|
|
371422
|
+
if (validation.status !== "valid")
|
|
371423
|
+
throw new Error(validation.diagnostics.map((item) => item.message).join("; "));
|
|
371424
|
+
if (digestValue(requestedPluginPermissions(validation.plugin.manifest)) !== digestValue(requestedPluginPermissions(manifest)))
|
|
371425
|
+
throw new Error("Converted permissions changed during validation");
|
|
371426
|
+
await checkOutput(input.inspection.root, output);
|
|
371427
|
+
await rename5(temporary, output);
|
|
371428
|
+
return report;
|
|
371429
|
+
} catch (error) {
|
|
371430
|
+
await rm8(temporary, { recursive: true, force: true });
|
|
371431
|
+
throw error;
|
|
371432
|
+
}
|
|
371433
|
+
}
|
|
371434
|
+
}
|
|
371010
371435
|
// ../../packages/plugin-converters/src/index.ts
|
|
371011
371436
|
function createBuiltinConverterRegistry() {
|
|
371012
371437
|
const registry = new ConverterRegistry;
|
|
371013
371438
|
registry.register(new ClaudeCodePluginConverter);
|
|
371439
|
+
registry.register(new CodexPluginConverter);
|
|
371014
371440
|
return registry;
|
|
371015
371441
|
}
|
|
371016
371442
|
|
|
@@ -371022,6 +371448,15 @@ async function client() {
|
|
|
371022
371448
|
return new OpenHarnessClient({ baseUrl: daemon.url, token: daemon.token });
|
|
371023
371449
|
}
|
|
371024
371450
|
var collect2 = (value, previous) => [...previous, value];
|
|
371451
|
+
function formatConversionPlan(plan) {
|
|
371452
|
+
return plan.items.map((item) => [
|
|
371453
|
+
`${item.fidelity.padEnd(11)} ${item.id}`,
|
|
371454
|
+
...item.reason ? [` ${item.reason}`] : [],
|
|
371455
|
+
...(item.requiredApprovals ?? []).map((approval) => ` --approve ${JSON.stringify(approval)}`)
|
|
371456
|
+
].join(`
|
|
371457
|
+
`)).join(`
|
|
371458
|
+
`);
|
|
371459
|
+
}
|
|
371025
371460
|
var activationLabels = {
|
|
371026
371461
|
inactive: "inactive",
|
|
371027
371462
|
active: "active",
|
|
@@ -371091,11 +371526,11 @@ function formatPluginList(result, verbose = false) {
|
|
|
371091
371526
|
function createPluginCommand() {
|
|
371092
371527
|
const cmd = new Command3("plugin").description("Manage Native Plugins");
|
|
371093
371528
|
cmd.command("list").option("--cwd <path>").option("--verbose", "show permissions, contributions, runtime, and diagnostics").option("--json", "print the complete machine-readable response").action(async (options) => {
|
|
371094
|
-
const result = await (await client()).listPlugins({ cwd:
|
|
371529
|
+
const result = await (await client()).listPlugins({ cwd: resolve31(options.cwd ?? process.cwd()) });
|
|
371095
371530
|
console.log(options.json ? JSON.stringify(result, null, 2) : formatPluginList(result, options.verbose));
|
|
371096
371531
|
});
|
|
371097
371532
|
cmd.command("validate").argument("<path>").action(async (path) => {
|
|
371098
|
-
const result = await validateNativePlugin(
|
|
371533
|
+
const result = await validateNativePlugin(resolve31(path));
|
|
371099
371534
|
if (result.status === "valid")
|
|
371100
371535
|
console.log(`Valid Native Plugin: ${result.plugin.manifest.id}@${result.plugin.manifest.version}`);
|
|
371101
371536
|
else {
|
|
@@ -371106,7 +371541,7 @@ function createPluginCommand() {
|
|
|
371106
371541
|
});
|
|
371107
371542
|
for (const [name, link] of [["install-local", false], ["link", true]]) {
|
|
371108
371543
|
cmd.command(name).argument("<path>").option("--cwd <path>").option("--approve <permission>", "approve a requested permission", collect2, []).action(async (path, options) => {
|
|
371109
|
-
const sourcePath =
|
|
371544
|
+
const sourcePath = resolve31(path);
|
|
371110
371545
|
const validation = await validateNativePlugin(sourcePath);
|
|
371111
371546
|
if (!validation.plugin)
|
|
371112
371547
|
throw new Error(validation.diagnostics.map((item) => item.message).join("; "));
|
|
@@ -371116,7 +371551,7 @@ function createPluginCommand() {
|
|
|
371116
371551
|
if (missing.length)
|
|
371117
371552
|
throw new Error(`Explicit approval required: ${missing.join(", ")}`);
|
|
371118
371553
|
const result = await (await client()).installLocalPlugin({
|
|
371119
|
-
cwd:
|
|
371554
|
+
cwd: resolve31(options.cwd ?? process.cwd()),
|
|
371120
371555
|
sourcePath,
|
|
371121
371556
|
scope: "user",
|
|
371122
371557
|
approvedPermissions: approved,
|
|
@@ -371128,25 +371563,25 @@ function createPluginCommand() {
|
|
|
371128
371563
|
for (const action of ["enable", "disable"])
|
|
371129
371564
|
cmd.command(action).argument("<id>").option("--cwd <path>").action(async (id, options) => {
|
|
371130
371565
|
const api = await client();
|
|
371131
|
-
const input = { cwd:
|
|
371566
|
+
const input = { cwd: resolve31(options.cwd ?? process.cwd()) };
|
|
371132
371567
|
console.log((action === "enable" ? await api.enablePlugin(id, input) : await api.disablePlugin(id, input)).message);
|
|
371133
371568
|
});
|
|
371134
371569
|
cmd.command("uninstall").argument("<id>").option("--cwd <path>").action(async (id, options) => {
|
|
371135
|
-
console.log((await (await client()).uninstallPlugin(id, { cwd:
|
|
371570
|
+
console.log((await (await client()).uninstallPlugin(id, { cwd: resolve31(options.cwd ?? process.cwd()) })).message);
|
|
371136
371571
|
});
|
|
371137
371572
|
cmd.command("details").argument("<id>").option("--cwd <path>").action(async (id, options) => {
|
|
371138
|
-
const listed = await (await client()).listPlugins({ cwd:
|
|
371573
|
+
const listed = await (await client()).listPlugins({ cwd: resolve31(options.cwd ?? process.cwd()) });
|
|
371139
371574
|
const plugin = listed.plugins.find((item) => item.identity.id === id);
|
|
371140
371575
|
if (!plugin)
|
|
371141
371576
|
throw new Error(`Plugin not found: ${id}`);
|
|
371142
371577
|
console.log(JSON.stringify(plugin, null, 2));
|
|
371143
371578
|
});
|
|
371144
|
-
cmd.command("install").argument("<source>").requiredOption("--from <converter>").option("--cwd <path>").option("--approve <item>", "approve conversion item or permission", collect2, []).action(async (source, options) => {
|
|
371145
|
-
const temporaryRoot = await mkdtemp3(
|
|
371146
|
-
const output =
|
|
371579
|
+
cmd.command("install").argument("<source>").requiredOption("--from <converter>", "source converter: claude-code or codex").option("--cwd <path>").option("--approve <item>", "approve conversion item or permission", collect2, []).action(async (source, options) => {
|
|
371580
|
+
const temporaryRoot = await mkdtemp3(join49(tmpdir5(), "ohs-plugin-import-"));
|
|
371581
|
+
const output = join49(temporaryRoot, "native");
|
|
371147
371582
|
try {
|
|
371148
|
-
const { converter } = await createBuiltinConverterRegistry().detect(
|
|
371149
|
-
const inspection = await converter.inspect(
|
|
371583
|
+
const { converter } = await createBuiltinConverterRegistry().detect(resolve31(source), options.from);
|
|
371584
|
+
const inspection = await converter.inspect(resolve31(source));
|
|
371150
371585
|
const plan = await converter.plan(inspection, {});
|
|
371151
371586
|
await converter.convert({ inspection, plan, output, approvals: options.approve });
|
|
371152
371587
|
const validation = await validateNativePlugin(output);
|
|
@@ -371157,29 +371592,28 @@ function createPluginCommand() {
|
|
|
371157
371592
|
if (missing.length)
|
|
371158
371593
|
throw new Error(`Explicit permission approval required: ${missing.join(", ")}`);
|
|
371159
371594
|
const result = await (await client()).installLocalPlugin({
|
|
371160
|
-
cwd:
|
|
371595
|
+
cwd: resolve31(options.cwd ?? process.cwd()),
|
|
371161
371596
|
sourcePath: output,
|
|
371162
371597
|
scope: "user",
|
|
371163
371598
|
approvedPermissions: requested
|
|
371164
371599
|
});
|
|
371165
371600
|
console.log(result.message);
|
|
371166
371601
|
} finally {
|
|
371167
|
-
await
|
|
371602
|
+
await rm9(temporaryRoot, { recursive: true, force: true });
|
|
371168
371603
|
}
|
|
371169
371604
|
});
|
|
371170
|
-
cmd.command("convert").argument("<source>").option("--from <converter>").option("--output <path>").option("--dry-run").option("--json").option("--approve <item>", "approve a
|
|
371605
|
+
cmd.command("convert").argument("<source>").option("--from <converter>", "source converter: claude-code or codex").option("--output <path>").option("--dry-run").option("--json").option("--approve <item>", "approve a conversion change, omitted component, or permission shown in the plan", collect2, []).action(async (source, options) => {
|
|
371171
371606
|
const registry = createBuiltinConverterRegistry();
|
|
371172
|
-
const { converter, detection } = await registry.detect(
|
|
371173
|
-
const inspection = await converter.inspect(
|
|
371607
|
+
const { converter, detection } = await registry.detect(resolve31(source), options.from);
|
|
371608
|
+
const inspection = await converter.inspect(resolve31(source));
|
|
371174
371609
|
const plan = await converter.plan(inspection, {});
|
|
371175
371610
|
if (options.dryRun) {
|
|
371176
|
-
console.log(options.json ? JSON.stringify({ detection, inspection, plan }, null, 2) : plan
|
|
371177
|
-
`));
|
|
371611
|
+
console.log(options.json ? JSON.stringify({ detection, inspection, plan }, null, 2) : formatConversionPlan(plan));
|
|
371178
371612
|
return;
|
|
371179
371613
|
}
|
|
371180
371614
|
if (!options.output)
|
|
371181
371615
|
throw new Error("--output is required unless --dry-run is used");
|
|
371182
|
-
const report = await converter.convert({ inspection, plan, output:
|
|
371616
|
+
const report = await converter.convert({ inspection, plan, output: resolve31(options.output), approvals: options.approve });
|
|
371183
371617
|
console.log(options.json ? JSON.stringify(report, null, 2) : `Converted ${inspection.identity.id}: ${report.status}`);
|
|
371184
371618
|
});
|
|
371185
371619
|
return cmd;
|
|
@@ -371696,7 +372130,7 @@ function createSandboxCommand() {
|
|
|
371696
372130
|
init_src3();
|
|
371697
372131
|
init_src9();
|
|
371698
372132
|
import { readFileSync as readFileSync15 } from "node:fs";
|
|
371699
|
-
import { resolve as
|
|
372133
|
+
import { resolve as resolve33 } from "node:path";
|
|
371700
372134
|
import { Command as Command8 } from "commander";
|
|
371701
372135
|
function createWorkflowCommand() {
|
|
371702
372136
|
const cmd = new Command8("workflow").description("Inspect and manage persisted workflow runs");
|
|
@@ -371853,7 +372287,7 @@ function readJsonOption(path, inlineJson) {
|
|
|
371853
372287
|
if (inlineJson)
|
|
371854
372288
|
return JSON.parse(inlineJson);
|
|
371855
372289
|
if (path)
|
|
371856
|
-
return JSON.parse(readFileSync15(
|
|
372290
|
+
return JSON.parse(readFileSync15(resolve33(path), "utf-8"));
|
|
371857
372291
|
return;
|
|
371858
372292
|
}
|
|
371859
372293
|
function parseCsv(value) {
|
|
@@ -372450,4 +372884,4 @@ program.command("doctor").description("Check environment and dependencies").acti
|
|
|
372450
372884
|
});
|
|
372451
372885
|
program.parse();
|
|
372452
372886
|
|
|
372453
|
-
//# debugId=
|
|
372887
|
+
//# debugId=037007773B000ECC64756E2164756E21
|