@agentplugins/adapter-codex 0.1.0

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/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @agentplugins/adapter-codex
2
+
3
+ > AgentPlugins platform adapter for [OpenAI Codex CLI](https://developers.openai.com/codex/plugins).
4
+
5
+ Compiles a universal `PluginManifest` into OpenAI Codex's native plugin layout: `plugin.json` manifest and `hooks.json` hook wiring (inline handlers are wrapped as Node scripts that receive JSON on stdin and respond on stdout).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @agentplugins/adapter-codex
11
+ ```
12
+
13
+ Typically installed transitively via [`@agentplugins/cli`](https://www.npmjs.com/package/@agentplugins/cli).
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import { createCodexAdapter } from '@agentplugins/adapter-codex';
19
+
20
+ const adapter = createCodexAdapter();
21
+ const output = await adapter.compile(manifest);
22
+ ```
23
+
24
+ Or via the CLI:
25
+
26
+ ```bash
27
+ npx agentplugins build --target codex
28
+ ```
29
+
30
+ ## Output shape
31
+
32
+ A successful build writes to `dist/codex/`:
33
+
34
+ ```
35
+ dist/codex/
36
+ ├── .codex-plugin/
37
+ │ └── plugin.json
38
+ └── hooks/
39
+ └── hooks.json
40
+ ```
41
+
42
+ Install with: `cp -r dist/codex ~/.codex/plugins/`
43
+
44
+ ## Hook protocol
45
+
46
+ Codex invokes hooks with a JSON event on **stdin** and expects a JSON response on **stdout**. Exit code `0` = allow, `2` = block (for `Stop` / `SubagentStop`).
47
+
48
+ ## License
49
+
50
+ MIT
@@ -0,0 +1,103 @@
1
+ import { PlatformAdapter, TargetPlatform, UniversalHookName, HandlerType, PluginManifest, ValidationIssue, AdapterOutput } from '@agentplugins/core';
2
+
3
+ /**
4
+ * @agentplugins/adapter-codex
5
+ *
6
+ * Platform adapter for OpenAI Codex CLI.
7
+ *
8
+ * Codex plugins use a JSON stdin/stdout protocol where:
9
+ * - Hooks receive JSON events via stdin
10
+ * - Hooks output JSON responses to stdout
11
+ * - Exit code 0 = success / allow
12
+ * - Exit code 2 = block (for Stop / SubagentStop hooks)
13
+ *
14
+ * Supported hooks (10):
15
+ * SessionStart, SubagentStart, PreToolUse, PermissionRequest,
16
+ * PostToolUse, PreCompact, PostCompact, UserPromptSubmit,
17
+ * SubagentStop, Stop
18
+ *
19
+ * Handler types:
20
+ * - command only (stdin/stdout JSON protocol)
21
+ *
22
+ * Manifest:
23
+ * - .codex-plugin/plugin.json
24
+ *
25
+ * Skills:
26
+ * - skills/<name>/SKILL.md
27
+ *
28
+ * MCP:
29
+ * - .mcp.json
30
+ *
31
+ * Environment variables:
32
+ * - PLUGIN_ROOT – absolute path to the plugin directory
33
+ * - PLUGIN_DATA – path to a writable data directory
34
+ */
35
+
36
+ declare const PLATFORM_NAME: TargetPlatform;
37
+ declare const DISPLAY_NAME = "OpenAI Codex CLI";
38
+ /** Hooks that Codex natively supports. */
39
+ declare const SUPPORTED_HOOKS: readonly UniversalHookName[];
40
+ /** Only command handlers (stdin/stdout JSON protocol) are supported on Codex. */
41
+ declare const SUPPORTED_HANDLERS: readonly HandlerType[];
42
+ declare const MANIFEST_PATH = ".codex-plugin/plugin.json";
43
+ declare const MANIFEST_FORMAT: "json";
44
+ declare const EXIT_CODE_BLOCK = 2;
45
+ declare const EXIT_CODE_SUCCESS = 0;
46
+ /** Mapping from universal hook names to Codex event names. */
47
+ declare const HOOK_NAME_MAP: Partial<Record<(typeof SUPPORTED_HOOKS)[number], string>>;
48
+ /**
49
+ * AgentPlugins platform adapter for OpenAI Codex CLI.
50
+ *
51
+ * Implements the {@link PlatformAdapter} interface to compile
52
+ * cross-platform AgentPlugins plugins into Codex-compatible plugin
53
+ * bundles with JSON stdin/stdout command handlers.
54
+ */
55
+ declare class CodexPlatformAdapter implements PlatformAdapter {
56
+ readonly name: TargetPlatform;
57
+ readonly displayName: string;
58
+ readonly supportedHooks: readonly UniversalHookName[];
59
+ readonly supportedHandlers: readonly HandlerType[];
60
+ readonly manifestPath: string;
61
+ readonly manifestFormat: "json" | "toml";
62
+ /**
63
+ * Validate a plugin manifest for Codex compatibility.
64
+ *
65
+ * Checks that:
66
+ * - Only supported hooks are used
67
+ * - Only "command" handler types are specified
68
+ * - Inline handlers have either a command or script
69
+ * - Skills have valid names and non-empty instructions
70
+ *
71
+ * @param plugin - The plugin manifest to validate
72
+ * @returns Array of validation issues (errors and warnings)
73
+ */
74
+ validate(plugin: PluginManifest): ValidationIssue[];
75
+ /**
76
+ * Compile a plugin manifest into Codex-compatible output files.
77
+ *
78
+ * Produces:
79
+ * - .codex-plugin/plugin.json – main manifest
80
+ * - skills/<name>/SKILL.md – skill documentation files
81
+ * - .mcp.json – MCP server configuration (if any)
82
+ * - hooks/<hook-name>.js – wrapped inline handler scripts
83
+ *
84
+ * @param plugin - The plugin manifest to compile
85
+ * @returns {@link AdapterOutput} with files, manifest, warnings, and post-install notes
86
+ */
87
+ compile(plugin: PluginManifest): AdapterOutput;
88
+ }
89
+ /**
90
+ * Factory function that creates a new {@link CodexPlatformAdapter} instance.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { createCodexAdapter } from "@agentplugins/adapter-codex";
95
+ *
96
+ * const adapter = createCodexAdapter();
97
+ * const issues = adapter.validate(manifest);
98
+ * const output = adapter.compile(manifest);
99
+ * ```
100
+ */
101
+ declare function createCodexAdapter(): CodexPlatformAdapter;
102
+
103
+ export { CodexPlatformAdapter, DISPLAY_NAME, EXIT_CODE_BLOCK, EXIT_CODE_SUCCESS, HOOK_NAME_MAP, MANIFEST_FORMAT, MANIFEST_PATH, PLATFORM_NAME, SUPPORTED_HANDLERS, SUPPORTED_HOOKS, createCodexAdapter, createCodexAdapter as default };
@@ -0,0 +1,103 @@
1
+ import { PlatformAdapter, TargetPlatform, UniversalHookName, HandlerType, PluginManifest, ValidationIssue, AdapterOutput } from '@agentplugins/core';
2
+
3
+ /**
4
+ * @agentplugins/adapter-codex
5
+ *
6
+ * Platform adapter for OpenAI Codex CLI.
7
+ *
8
+ * Codex plugins use a JSON stdin/stdout protocol where:
9
+ * - Hooks receive JSON events via stdin
10
+ * - Hooks output JSON responses to stdout
11
+ * - Exit code 0 = success / allow
12
+ * - Exit code 2 = block (for Stop / SubagentStop hooks)
13
+ *
14
+ * Supported hooks (10):
15
+ * SessionStart, SubagentStart, PreToolUse, PermissionRequest,
16
+ * PostToolUse, PreCompact, PostCompact, UserPromptSubmit,
17
+ * SubagentStop, Stop
18
+ *
19
+ * Handler types:
20
+ * - command only (stdin/stdout JSON protocol)
21
+ *
22
+ * Manifest:
23
+ * - .codex-plugin/plugin.json
24
+ *
25
+ * Skills:
26
+ * - skills/<name>/SKILL.md
27
+ *
28
+ * MCP:
29
+ * - .mcp.json
30
+ *
31
+ * Environment variables:
32
+ * - PLUGIN_ROOT – absolute path to the plugin directory
33
+ * - PLUGIN_DATA – path to a writable data directory
34
+ */
35
+
36
+ declare const PLATFORM_NAME: TargetPlatform;
37
+ declare const DISPLAY_NAME = "OpenAI Codex CLI";
38
+ /** Hooks that Codex natively supports. */
39
+ declare const SUPPORTED_HOOKS: readonly UniversalHookName[];
40
+ /** Only command handlers (stdin/stdout JSON protocol) are supported on Codex. */
41
+ declare const SUPPORTED_HANDLERS: readonly HandlerType[];
42
+ declare const MANIFEST_PATH = ".codex-plugin/plugin.json";
43
+ declare const MANIFEST_FORMAT: "json";
44
+ declare const EXIT_CODE_BLOCK = 2;
45
+ declare const EXIT_CODE_SUCCESS = 0;
46
+ /** Mapping from universal hook names to Codex event names. */
47
+ declare const HOOK_NAME_MAP: Partial<Record<(typeof SUPPORTED_HOOKS)[number], string>>;
48
+ /**
49
+ * AgentPlugins platform adapter for OpenAI Codex CLI.
50
+ *
51
+ * Implements the {@link PlatformAdapter} interface to compile
52
+ * cross-platform AgentPlugins plugins into Codex-compatible plugin
53
+ * bundles with JSON stdin/stdout command handlers.
54
+ */
55
+ declare class CodexPlatformAdapter implements PlatformAdapter {
56
+ readonly name: TargetPlatform;
57
+ readonly displayName: string;
58
+ readonly supportedHooks: readonly UniversalHookName[];
59
+ readonly supportedHandlers: readonly HandlerType[];
60
+ readonly manifestPath: string;
61
+ readonly manifestFormat: "json" | "toml";
62
+ /**
63
+ * Validate a plugin manifest for Codex compatibility.
64
+ *
65
+ * Checks that:
66
+ * - Only supported hooks are used
67
+ * - Only "command" handler types are specified
68
+ * - Inline handlers have either a command or script
69
+ * - Skills have valid names and non-empty instructions
70
+ *
71
+ * @param plugin - The plugin manifest to validate
72
+ * @returns Array of validation issues (errors and warnings)
73
+ */
74
+ validate(plugin: PluginManifest): ValidationIssue[];
75
+ /**
76
+ * Compile a plugin manifest into Codex-compatible output files.
77
+ *
78
+ * Produces:
79
+ * - .codex-plugin/plugin.json – main manifest
80
+ * - skills/<name>/SKILL.md – skill documentation files
81
+ * - .mcp.json – MCP server configuration (if any)
82
+ * - hooks/<hook-name>.js – wrapped inline handler scripts
83
+ *
84
+ * @param plugin - The plugin manifest to compile
85
+ * @returns {@link AdapterOutput} with files, manifest, warnings, and post-install notes
86
+ */
87
+ compile(plugin: PluginManifest): AdapterOutput;
88
+ }
89
+ /**
90
+ * Factory function that creates a new {@link CodexPlatformAdapter} instance.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { createCodexAdapter } from "@agentplugins/adapter-codex";
95
+ *
96
+ * const adapter = createCodexAdapter();
97
+ * const issues = adapter.validate(manifest);
98
+ * const output = adapter.compile(manifest);
99
+ * ```
100
+ */
101
+ declare function createCodexAdapter(): CodexPlatformAdapter;
102
+
103
+ export { CodexPlatformAdapter, DISPLAY_NAME, EXIT_CODE_BLOCK, EXIT_CODE_SUCCESS, HOOK_NAME_MAP, MANIFEST_FORMAT, MANIFEST_PATH, PLATFORM_NAME, SUPPORTED_HANDLERS, SUPPORTED_HOOKS, createCodexAdapter, createCodexAdapter as default };
package/dist/index.js ADDED
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CodexPlatformAdapter: () => CodexPlatformAdapter,
24
+ DISPLAY_NAME: () => DISPLAY_NAME,
25
+ EXIT_CODE_BLOCK: () => EXIT_CODE_BLOCK,
26
+ EXIT_CODE_SUCCESS: () => EXIT_CODE_SUCCESS,
27
+ HOOK_NAME_MAP: () => HOOK_NAME_MAP,
28
+ MANIFEST_FORMAT: () => MANIFEST_FORMAT,
29
+ MANIFEST_PATH: () => MANIFEST_PATH,
30
+ PLATFORM_NAME: () => PLATFORM_NAME,
31
+ SUPPORTED_HANDLERS: () => SUPPORTED_HANDLERS,
32
+ SUPPORTED_HOOKS: () => SUPPORTED_HOOKS,
33
+ createCodexAdapter: () => createCodexAdapter,
34
+ default: () => index_default
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+ var import_core = require("@agentplugins/core");
38
+ var PLATFORM_NAME = "codex";
39
+ var DISPLAY_NAME = "OpenAI Codex CLI";
40
+ var SUPPORTED_HOOKS = [
41
+ "sessionStart",
42
+ "subagentStart",
43
+ "preToolUse",
44
+ "permissionRequest",
45
+ "postToolUse",
46
+ "preCompact",
47
+ "postCompact",
48
+ "userPromptSubmit",
49
+ "subagentStop",
50
+ "stop"
51
+ ];
52
+ var SUPPORTED_HANDLERS = ["command"];
53
+ var MANIFEST_PATH = ".codex-plugin/plugin.json";
54
+ var MANIFEST_FORMAT = "json";
55
+ var EXIT_CODE_BLOCK = 2;
56
+ var EXIT_CODE_SUCCESS = 0;
57
+ var HOOK_NAME_MAP = {
58
+ sessionStart: "SessionStart",
59
+ subagentStart: "SubagentStart",
60
+ preToolUse: "PreToolUse",
61
+ permissionRequest: "PermissionRequest",
62
+ postToolUse: "PostToolUse",
63
+ preCompact: "PreCompact",
64
+ postCompact: "PostCompact",
65
+ userPromptSubmit: "UserPromptSubmit",
66
+ subagentStop: "SubagentStop",
67
+ stop: "Stop"
68
+ };
69
+ var TOOL_MATCHED_HOOKS = [
70
+ "PreToolUse",
71
+ "PermissionRequest",
72
+ "PostToolUse"
73
+ ];
74
+ function validateHook(hookName, hook) {
75
+ const issues = [];
76
+ const codexEvent = HOOK_NAME_MAP[hookName];
77
+ if (!codexEvent) {
78
+ issues.push({
79
+ severity: import_core.Severity.ERROR,
80
+ message: `Hook "${hookName}" is not supported by Codex. Supported hooks: ${SUPPORTED_HOOKS.join(", ")}.`,
81
+ field: `hooks.${hookName}`,
82
+ suggestion: `Remove this hook or use a command handler instead.`
83
+ });
84
+ return issues;
85
+ }
86
+ if (hook.handler && hook.handler.type !== "command") {
87
+ issues.push({
88
+ severity: import_core.Severity.ERROR,
89
+ message: `Codex only supports "command" handlers. Hook "${hookName}" specifies type "${hook.handler.type}".`,
90
+ field: `hooks.${hookName}.handler.type`,
91
+ suggestion: `Use a command handler instead of an inline handler.`
92
+ });
93
+ }
94
+ return issues;
95
+ }
96
+ function validateSkill(skill, index) {
97
+ const issues = [];
98
+ if (!skill.name || skill.name.trim().length === 0) {
99
+ issues.push({
100
+ severity: import_core.Severity.ERROR,
101
+ message: `Skill at index ${index} must have a non-empty name.`,
102
+ field: `skills[${index}].name`
103
+ });
104
+ }
105
+ if (!skill.content || skill.content.trim().length === 0) {
106
+ issues.push({
107
+ severity: import_core.Severity.WARNING,
108
+ message: `Skill "${skill.name}" has empty content.`,
109
+ field: `skills[${index}].content`
110
+ });
111
+ }
112
+ return issues;
113
+ }
114
+ function buildCodexHookEntry(hookName, hook) {
115
+ const codexEvent = HOOK_NAME_MAP[hookName];
116
+ const entry = {
117
+ event: codexEvent
118
+ };
119
+ if (TOOL_MATCHED_HOOKS.includes(codexEvent)) {
120
+ entry.matcher = hook.matcher ?? "*";
121
+ }
122
+ if (hook.handler.type === "command") {
123
+ entry.command = hook.handler.command;
124
+ }
125
+ return entry;
126
+ }
127
+ function buildManifest(plugin, hookEntries) {
128
+ return {
129
+ name: plugin.name,
130
+ version: plugin.version,
131
+ description: plugin.description,
132
+ hooks: hookEntries,
133
+ environment: {
134
+ PLUGIN_ROOT: "${PLUGIN_ROOT}",
135
+ PLUGIN_DATA: "${PLUGIN_DATA}"
136
+ }
137
+ };
138
+ }
139
+ function buildMcpManifest(plugin) {
140
+ if (!plugin.mcpServers || Object.keys(plugin.mcpServers).length === 0) {
141
+ return null;
142
+ }
143
+ const servers = {};
144
+ for (const [name, server] of Object.entries(plugin.mcpServers)) {
145
+ servers[name] = {
146
+ command: server.command,
147
+ ...server.args && server.args.length > 0 ? { args: server.args } : {},
148
+ ...server.env ? { env: server.env } : {}
149
+ };
150
+ }
151
+ return { servers };
152
+ }
153
+ function buildSkillFile(skill) {
154
+ const parts = [];
155
+ parts.push(`# ${skill.name}`);
156
+ parts.push("");
157
+ if (skill.description) {
158
+ parts.push(skill.description);
159
+ parts.push("");
160
+ }
161
+ if (skill.content) {
162
+ parts.push(skill.content);
163
+ }
164
+ return parts.join("\n");
165
+ }
166
+ var CodexPlatformAdapter = class {
167
+ name = PLATFORM_NAME;
168
+ displayName = DISPLAY_NAME;
169
+ supportedHooks = SUPPORTED_HOOKS;
170
+ supportedHandlers = SUPPORTED_HANDLERS;
171
+ manifestPath = MANIFEST_PATH;
172
+ manifestFormat = MANIFEST_FORMAT;
173
+ /**
174
+ * Validate a plugin manifest for Codex compatibility.
175
+ *
176
+ * Checks that:
177
+ * - Only supported hooks are used
178
+ * - Only "command" handler types are specified
179
+ * - Inline handlers have either a command or script
180
+ * - Skills have valid names and non-empty instructions
181
+ *
182
+ * @param plugin - The plugin manifest to validate
183
+ * @returns Array of validation issues (errors and warnings)
184
+ */
185
+ validate(plugin) {
186
+ const issues = [];
187
+ if (plugin.hooks) {
188
+ for (const [hookName, hookDef] of Object.entries(plugin.hooks)) {
189
+ issues.push(...validateHook(hookName, hookDef));
190
+ }
191
+ }
192
+ if (plugin.skills) {
193
+ for (let i = 0; i < plugin.skills.length; i++) {
194
+ issues.push(...validateSkill(plugin.skills[i], i));
195
+ }
196
+ }
197
+ return issues;
198
+ }
199
+ /**
200
+ * Compile a plugin manifest into Codex-compatible output files.
201
+ *
202
+ * Produces:
203
+ * - .codex-plugin/plugin.json – main manifest
204
+ * - skills/<name>/SKILL.md – skill documentation files
205
+ * - .mcp.json – MCP server configuration (if any)
206
+ * - hooks/<hook-name>.js – wrapped inline handler scripts
207
+ *
208
+ * @param plugin - The plugin manifest to compile
209
+ * @returns {@link AdapterOutput} with files, manifest, warnings, and post-install notes
210
+ */
211
+ compile(plugin) {
212
+ const files = [];
213
+ const warnings = [];
214
+ const hookEntries = [];
215
+ if (plugin.hooks) {
216
+ for (const [hookName, hookDef] of Object.entries(plugin.hooks)) {
217
+ const codexEvent = HOOK_NAME_MAP[hookName];
218
+ if (!codexEvent) {
219
+ warnings.push(
220
+ `Skipping unsupported hook "${hookName}" \u2013 not included in compiled output.`
221
+ );
222
+ continue;
223
+ }
224
+ const entry = buildCodexHookEntry(hookName, hookDef);
225
+ if (hookDef.handler.type === "command") {
226
+ entry.command = hookDef.handler.command;
227
+ hookEntries.push(entry);
228
+ } else {
229
+ warnings.push(
230
+ `Hook "${hookName}": inline handlers are not supported on Codex. Use command handlers instead.`
231
+ );
232
+ }
233
+ }
234
+ }
235
+ if (plugin.skills) {
236
+ for (const skill of plugin.skills) {
237
+ const skillPath = `skills/${skill.name}/SKILL.md`;
238
+ const skillContent = buildSkillFile(skill);
239
+ files.push({
240
+ path: skillPath,
241
+ content: skillContent
242
+ });
243
+ }
244
+ }
245
+ const manifest = buildManifest(plugin, hookEntries);
246
+ files.push({
247
+ path: MANIFEST_PATH,
248
+ content: JSON.stringify(manifest, null, 2)
249
+ });
250
+ const mcpManifest = buildMcpManifest(plugin);
251
+ if (mcpManifest) {
252
+ files.push({
253
+ path: ".mcp.json",
254
+ content: JSON.stringify(mcpManifest, null, 2)
255
+ });
256
+ }
257
+ const postInstall = [
258
+ "OpenAI Codex Plugin",
259
+ "===================",
260
+ "",
261
+ `Plugin "${plugin.name}" v${plugin.version} compiled for Codex.`,
262
+ "",
263
+ "Install:",
264
+ ` 1. Copy the generated files into your project root (or a sub-directory).`,
265
+ ` 2. Ensure the hook scripts under hooks/ are executable:`,
266
+ ` chmod +x hooks/*.js`,
267
+ ` 3. Set environment variables:`,
268
+ ` export PLUGIN_ROOT=/path/to/plugin`,
269
+ ` export PLUGIN_DATA=/path/to/plugin/data`,
270
+ ` 4. Codex will auto-discover .codex-plugin/plugin.json when running`,
271
+ ` in the project directory.`,
272
+ "",
273
+ "Hook protocol:",
274
+ " - Codex sends JSON events to each hook via stdin.",
275
+ " - Hooks respond with JSON on stdout.",
276
+ " - Exit code 0 = success / allow the operation.",
277
+ ` - Exit code ${EXIT_CODE_BLOCK} = block (for Stop / SubagentStop hooks).`,
278
+ "",
279
+ "Output formats:",
280
+ ' SessionStart \u2192 { "hookSpecificOutput": { "hookEventName": "SessionStart", "additionalContext": "..." } }',
281
+ ' PreToolUse \u2192 { "systemMessage": "Warning message" }',
282
+ ' Stop / SubagentStop \u2192 { "decision": "block", "reason": "..." } (or exit 2)',
283
+ ""
284
+ ];
285
+ return {
286
+ files,
287
+ manifest,
288
+ warnings,
289
+ issues: [],
290
+ postInstall
291
+ };
292
+ }
293
+ };
294
+ function createCodexAdapter() {
295
+ return new CodexPlatformAdapter();
296
+ }
297
+ var index_default = createCodexAdapter;
298
+ // Annotate the CommonJS export names for ESM import in node:
299
+ 0 && (module.exports = {
300
+ CodexPlatformAdapter,
301
+ DISPLAY_NAME,
302
+ EXIT_CODE_BLOCK,
303
+ EXIT_CODE_SUCCESS,
304
+ HOOK_NAME_MAP,
305
+ MANIFEST_FORMAT,
306
+ MANIFEST_PATH,
307
+ PLATFORM_NAME,
308
+ SUPPORTED_HANDLERS,
309
+ SUPPORTED_HOOKS,
310
+ createCodexAdapter
311
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,276 @@
1
+ // src/index.ts
2
+ import { Severity } from "@agentplugins/core";
3
+ var PLATFORM_NAME = "codex";
4
+ var DISPLAY_NAME = "OpenAI Codex CLI";
5
+ var SUPPORTED_HOOKS = [
6
+ "sessionStart",
7
+ "subagentStart",
8
+ "preToolUse",
9
+ "permissionRequest",
10
+ "postToolUse",
11
+ "preCompact",
12
+ "postCompact",
13
+ "userPromptSubmit",
14
+ "subagentStop",
15
+ "stop"
16
+ ];
17
+ var SUPPORTED_HANDLERS = ["command"];
18
+ var MANIFEST_PATH = ".codex-plugin/plugin.json";
19
+ var MANIFEST_FORMAT = "json";
20
+ var EXIT_CODE_BLOCK = 2;
21
+ var EXIT_CODE_SUCCESS = 0;
22
+ var HOOK_NAME_MAP = {
23
+ sessionStart: "SessionStart",
24
+ subagentStart: "SubagentStart",
25
+ preToolUse: "PreToolUse",
26
+ permissionRequest: "PermissionRequest",
27
+ postToolUse: "PostToolUse",
28
+ preCompact: "PreCompact",
29
+ postCompact: "PostCompact",
30
+ userPromptSubmit: "UserPromptSubmit",
31
+ subagentStop: "SubagentStop",
32
+ stop: "Stop"
33
+ };
34
+ var TOOL_MATCHED_HOOKS = [
35
+ "PreToolUse",
36
+ "PermissionRequest",
37
+ "PostToolUse"
38
+ ];
39
+ function validateHook(hookName, hook) {
40
+ const issues = [];
41
+ const codexEvent = HOOK_NAME_MAP[hookName];
42
+ if (!codexEvent) {
43
+ issues.push({
44
+ severity: Severity.ERROR,
45
+ message: `Hook "${hookName}" is not supported by Codex. Supported hooks: ${SUPPORTED_HOOKS.join(", ")}.`,
46
+ field: `hooks.${hookName}`,
47
+ suggestion: `Remove this hook or use a command handler instead.`
48
+ });
49
+ return issues;
50
+ }
51
+ if (hook.handler && hook.handler.type !== "command") {
52
+ issues.push({
53
+ severity: Severity.ERROR,
54
+ message: `Codex only supports "command" handlers. Hook "${hookName}" specifies type "${hook.handler.type}".`,
55
+ field: `hooks.${hookName}.handler.type`,
56
+ suggestion: `Use a command handler instead of an inline handler.`
57
+ });
58
+ }
59
+ return issues;
60
+ }
61
+ function validateSkill(skill, index) {
62
+ const issues = [];
63
+ if (!skill.name || skill.name.trim().length === 0) {
64
+ issues.push({
65
+ severity: Severity.ERROR,
66
+ message: `Skill at index ${index} must have a non-empty name.`,
67
+ field: `skills[${index}].name`
68
+ });
69
+ }
70
+ if (!skill.content || skill.content.trim().length === 0) {
71
+ issues.push({
72
+ severity: Severity.WARNING,
73
+ message: `Skill "${skill.name}" has empty content.`,
74
+ field: `skills[${index}].content`
75
+ });
76
+ }
77
+ return issues;
78
+ }
79
+ function buildCodexHookEntry(hookName, hook) {
80
+ const codexEvent = HOOK_NAME_MAP[hookName];
81
+ const entry = {
82
+ event: codexEvent
83
+ };
84
+ if (TOOL_MATCHED_HOOKS.includes(codexEvent)) {
85
+ entry.matcher = hook.matcher ?? "*";
86
+ }
87
+ if (hook.handler.type === "command") {
88
+ entry.command = hook.handler.command;
89
+ }
90
+ return entry;
91
+ }
92
+ function buildManifest(plugin, hookEntries) {
93
+ return {
94
+ name: plugin.name,
95
+ version: plugin.version,
96
+ description: plugin.description,
97
+ hooks: hookEntries,
98
+ environment: {
99
+ PLUGIN_ROOT: "${PLUGIN_ROOT}",
100
+ PLUGIN_DATA: "${PLUGIN_DATA}"
101
+ }
102
+ };
103
+ }
104
+ function buildMcpManifest(plugin) {
105
+ if (!plugin.mcpServers || Object.keys(plugin.mcpServers).length === 0) {
106
+ return null;
107
+ }
108
+ const servers = {};
109
+ for (const [name, server] of Object.entries(plugin.mcpServers)) {
110
+ servers[name] = {
111
+ command: server.command,
112
+ ...server.args && server.args.length > 0 ? { args: server.args } : {},
113
+ ...server.env ? { env: server.env } : {}
114
+ };
115
+ }
116
+ return { servers };
117
+ }
118
+ function buildSkillFile(skill) {
119
+ const parts = [];
120
+ parts.push(`# ${skill.name}`);
121
+ parts.push("");
122
+ if (skill.description) {
123
+ parts.push(skill.description);
124
+ parts.push("");
125
+ }
126
+ if (skill.content) {
127
+ parts.push(skill.content);
128
+ }
129
+ return parts.join("\n");
130
+ }
131
+ var CodexPlatformAdapter = class {
132
+ name = PLATFORM_NAME;
133
+ displayName = DISPLAY_NAME;
134
+ supportedHooks = SUPPORTED_HOOKS;
135
+ supportedHandlers = SUPPORTED_HANDLERS;
136
+ manifestPath = MANIFEST_PATH;
137
+ manifestFormat = MANIFEST_FORMAT;
138
+ /**
139
+ * Validate a plugin manifest for Codex compatibility.
140
+ *
141
+ * Checks that:
142
+ * - Only supported hooks are used
143
+ * - Only "command" handler types are specified
144
+ * - Inline handlers have either a command or script
145
+ * - Skills have valid names and non-empty instructions
146
+ *
147
+ * @param plugin - The plugin manifest to validate
148
+ * @returns Array of validation issues (errors and warnings)
149
+ */
150
+ validate(plugin) {
151
+ const issues = [];
152
+ if (plugin.hooks) {
153
+ for (const [hookName, hookDef] of Object.entries(plugin.hooks)) {
154
+ issues.push(...validateHook(hookName, hookDef));
155
+ }
156
+ }
157
+ if (plugin.skills) {
158
+ for (let i = 0; i < plugin.skills.length; i++) {
159
+ issues.push(...validateSkill(plugin.skills[i], i));
160
+ }
161
+ }
162
+ return issues;
163
+ }
164
+ /**
165
+ * Compile a plugin manifest into Codex-compatible output files.
166
+ *
167
+ * Produces:
168
+ * - .codex-plugin/plugin.json – main manifest
169
+ * - skills/<name>/SKILL.md – skill documentation files
170
+ * - .mcp.json – MCP server configuration (if any)
171
+ * - hooks/<hook-name>.js – wrapped inline handler scripts
172
+ *
173
+ * @param plugin - The plugin manifest to compile
174
+ * @returns {@link AdapterOutput} with files, manifest, warnings, and post-install notes
175
+ */
176
+ compile(plugin) {
177
+ const files = [];
178
+ const warnings = [];
179
+ const hookEntries = [];
180
+ if (plugin.hooks) {
181
+ for (const [hookName, hookDef] of Object.entries(plugin.hooks)) {
182
+ const codexEvent = HOOK_NAME_MAP[hookName];
183
+ if (!codexEvent) {
184
+ warnings.push(
185
+ `Skipping unsupported hook "${hookName}" \u2013 not included in compiled output.`
186
+ );
187
+ continue;
188
+ }
189
+ const entry = buildCodexHookEntry(hookName, hookDef);
190
+ if (hookDef.handler.type === "command") {
191
+ entry.command = hookDef.handler.command;
192
+ hookEntries.push(entry);
193
+ } else {
194
+ warnings.push(
195
+ `Hook "${hookName}": inline handlers are not supported on Codex. Use command handlers instead.`
196
+ );
197
+ }
198
+ }
199
+ }
200
+ if (plugin.skills) {
201
+ for (const skill of plugin.skills) {
202
+ const skillPath = `skills/${skill.name}/SKILL.md`;
203
+ const skillContent = buildSkillFile(skill);
204
+ files.push({
205
+ path: skillPath,
206
+ content: skillContent
207
+ });
208
+ }
209
+ }
210
+ const manifest = buildManifest(plugin, hookEntries);
211
+ files.push({
212
+ path: MANIFEST_PATH,
213
+ content: JSON.stringify(manifest, null, 2)
214
+ });
215
+ const mcpManifest = buildMcpManifest(plugin);
216
+ if (mcpManifest) {
217
+ files.push({
218
+ path: ".mcp.json",
219
+ content: JSON.stringify(mcpManifest, null, 2)
220
+ });
221
+ }
222
+ const postInstall = [
223
+ "OpenAI Codex Plugin",
224
+ "===================",
225
+ "",
226
+ `Plugin "${plugin.name}" v${plugin.version} compiled for Codex.`,
227
+ "",
228
+ "Install:",
229
+ ` 1. Copy the generated files into your project root (or a sub-directory).`,
230
+ ` 2. Ensure the hook scripts under hooks/ are executable:`,
231
+ ` chmod +x hooks/*.js`,
232
+ ` 3. Set environment variables:`,
233
+ ` export PLUGIN_ROOT=/path/to/plugin`,
234
+ ` export PLUGIN_DATA=/path/to/plugin/data`,
235
+ ` 4. Codex will auto-discover .codex-plugin/plugin.json when running`,
236
+ ` in the project directory.`,
237
+ "",
238
+ "Hook protocol:",
239
+ " - Codex sends JSON events to each hook via stdin.",
240
+ " - Hooks respond with JSON on stdout.",
241
+ " - Exit code 0 = success / allow the operation.",
242
+ ` - Exit code ${EXIT_CODE_BLOCK} = block (for Stop / SubagentStop hooks).`,
243
+ "",
244
+ "Output formats:",
245
+ ' SessionStart \u2192 { "hookSpecificOutput": { "hookEventName": "SessionStart", "additionalContext": "..." } }',
246
+ ' PreToolUse \u2192 { "systemMessage": "Warning message" }',
247
+ ' Stop / SubagentStop \u2192 { "decision": "block", "reason": "..." } (or exit 2)',
248
+ ""
249
+ ];
250
+ return {
251
+ files,
252
+ manifest,
253
+ warnings,
254
+ issues: [],
255
+ postInstall
256
+ };
257
+ }
258
+ };
259
+ function createCodexAdapter() {
260
+ return new CodexPlatformAdapter();
261
+ }
262
+ var index_default = createCodexAdapter;
263
+ export {
264
+ CodexPlatformAdapter,
265
+ DISPLAY_NAME,
266
+ EXIT_CODE_BLOCK,
267
+ EXIT_CODE_SUCCESS,
268
+ HOOK_NAME_MAP,
269
+ MANIFEST_FORMAT,
270
+ MANIFEST_PATH,
271
+ PLATFORM_NAME,
272
+ SUPPORTED_HANDLERS,
273
+ SUPPORTED_HOOKS,
274
+ createCodexAdapter,
275
+ index_default as default
276
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@agentplugins/adapter-codex",
3
+ "version": "0.1.0",
4
+ "description": "AgentPlugins platform adapter for OpenAI Codex CLI",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.mjs",
10
+ "require": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsup src/index.ts --format cjs,esm --dts",
16
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
17
+ "typecheck": "tsc --noEmit",
18
+ "lint": "eslint src/**/*.ts",
19
+ "test": "vitest run"
20
+ },
21
+ "dependencies": {
22
+ "@agentplugins/core": "workspace:*"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^20.0.0",
26
+ "tsup": "^8.0.0",
27
+ "typescript": "^5.3.0",
28
+ "vitest": "^1.0.0"
29
+ },
30
+ "peerDependencies": {
31
+ "@agentplugins/core": "^0.1.0"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/espetro/agentplugins.git",
44
+ "directory": "packages/adapter-codex"
45
+ },
46
+ "keywords": [
47
+ "agentplugins",
48
+ "codex",
49
+ "openai",
50
+ "cli",
51
+ "adapter"
52
+ ]
53
+ }