@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 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 image_path, image_url, or prompt", "command");
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 join47, resolve as resolve29 } from "node:path";
370571
- import { mkdtemp as mkdtemp3, rm as rm8 } from "node:fs/promises";
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
- hash.update(relative10(root, file).replaceAll("\\", "/"));
370604
- hash.update("\x00");
370605
- hash.update(await readFile18(file));
370606
- hash.update("\x00");
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 lstat6, readFile as readFile19, readdir as readdir10 } from "node:fs/promises";
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 lstat6(path);
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: resolve29(options.cwd ?? process.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(resolve29(path));
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 = resolve29(path);
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: resolve29(options.cwd ?? process.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: resolve29(options.cwd ?? process.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: resolve29(options.cwd ?? process.cwd()) })).message);
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: resolve29(options.cwd ?? process.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(join47(tmpdir5(), "ohs-plugin-import-"));
371146
- const output = join47(temporaryRoot, "native");
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(resolve29(source), options.from);
371149
- const inspection = await converter.inspect(resolve29(source));
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: resolve29(options.cwd ?? process.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 rm8(temporaryRoot, { recursive: true, force: true });
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 blocked conversion item", collect2, []).action(async (source, options) => {
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(resolve29(source), options.from);
371173
- const inspection = await converter.inspect(resolve29(source));
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.items.map((item) => `${item.fidelity.padEnd(11)} ${item.id}`).join(`
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: resolve29(options.output), approvals: options.approve });
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 resolve30 } from "node:path";
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(resolve30(path), "utf-8"));
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=A30D9095E02E07FA64756E2164756E21
372887
+ //# debugId=037007773B000ECC64756E2164756E21