@subshell-ai/plugin-codex 0.1.1 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../plugin-api/dist/types-CSNepTNr.d.ts
1
+ //#region ../../plugin-api/dist/types-B_wiApwK.d.ts
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * The contract a Subshell plugin implements.
@@ -27,9 +27,9 @@ type PluginType = "agent-harness" | "terminal";
27
27
  * branches on {@link PluginType}.
28
28
  */
29
29
  type PluginCapability = "mcp" | "resume" | "attention" | "settings";
30
- /** A harness profile as defined by the user (decoded JSON blobs). */
31
- interface ProfileDefinition {
32
- /** Human-friendly profile name */
30
+ /** A harness preset as defined by the user (decoded JSON blobs). */
31
+ interface PresetDefinition {
32
+ /** Human-friendly preset name */
33
33
  name: string;
34
34
  /** Optional longer description */
35
35
  description?: string | null;
@@ -39,21 +39,21 @@ interface ProfileDefinition {
39
39
  flags: string[];
40
40
  /** Settings blob passed to the harness (e.g. claude --settings JSON) */
41
41
  settings: Record<string, unknown> | null;
42
- /** If true, only this profile's config sources apply (isolation) */
42
+ /** If true, only this preset's config sources apply (isolation) */
43
43
  configIsolation: boolean;
44
- /** If true, new subshells from this profile auto-restart on exit */
44
+ /** If true, new subshells from this preset auto-restart on exit */
45
45
  restartOnExit?: boolean;
46
46
  }
47
- /** Snapshot of a single field-level validation error on a profile. */
48
- interface ProfileValidationIssue {
47
+ /** Snapshot of a single field-level validation error on a preset. */
48
+ interface PresetValidationIssue {
49
49
  /** Field name (e.g. "name", "env", "settings") */
50
50
  field: string;
51
51
  /** Human-readable problem description */
52
52
  message: string;
53
53
  }
54
- interface ProfileValidationResult {
54
+ interface PresetValidationResult {
55
55
  valid: boolean;
56
- issues: ProfileValidationIssue[];
56
+ issues: PresetValidationIssue[];
57
57
  }
58
58
  /** Everything a plugin needs to build a launch command. */
59
59
  interface BuildCommandInput {
@@ -61,8 +61,8 @@ interface BuildCommandInput {
61
61
  binary: string;
62
62
  /** Working directory the harness runs in */
63
63
  cwd: string;
64
- /** The validated profile being used */
65
- profile: ProfileDefinition;
64
+ /** The validated preset being used */
65
+ preset: PresetDefinition;
66
66
  /** Subshell display name ("" = let the harness pick a default) */
67
67
  subshellName: string;
68
68
  /** Any additional CLI flags from route/request context */
@@ -169,12 +169,12 @@ interface McpRegistration {
169
169
  args?: string[];
170
170
  /**
171
171
  * Extra pane env the harness needs to discover the file (e.g. OPENCODE_CONFIG).
172
- * Consumed by the HOST, which bakes it into the pane env ahead of profile env;
172
+ * Consumed by the HOST, which bakes it into the pane env ahead of preset env;
173
173
  * plugins never read it back.
174
174
  */
175
175
  env?: Record<string, string>;
176
176
  }
177
- /** One copy-paste line shown in the profile editor for manual-setup harnesses. */
177
+ /** One copy-paste line shown in the preset editor for manual-setup harnesses. */
178
178
  interface McpSetupStep {
179
179
  /** What the user should do, and where the text goes */
180
180
  label: string;
@@ -259,8 +259,8 @@ interface PluginHost {
259
259
  interface SubshellPlugin {
260
260
  /** Builds the argv (no shell) used to launch a subshell. */
261
261
  buildCommand(input: BuildCommandInput): string[];
262
- /** Validates a profile definition before saving. */
263
- validateProfile(profile: ProfileDefinition): ProfileValidationResult;
262
+ /** Validates a preset definition before saving. */
263
+ validatePreset(preset: PresetDefinition): PresetValidationResult;
264
264
  /** Which optional members below are meaningful on this plugin. */
265
265
  capabilities(): PluginCapability[];
266
266
  /** Maps a harness exit code to a human label (null = unknown). */
@@ -292,14 +292,14 @@ interface SubshellPlugin {
292
292
  mcpRegistration?(launch: McpLaunchSpec, configPath: string): McpRegistration;
293
293
  /** How users obtain the subshell MCP tools here. Declare the `mcp` capability with it. */
294
294
  mcpSetup?(launch: McpLaunchSpec): McpSetupInfo;
295
- /** Settings rendered in the PROFILE editor and stored on the profile. */
296
- profileSettings?(): SettingsField[];
297
- /** Known extra env var suggestions for the profile editor. */
295
+ /** Settings rendered in the PRESET editor and stored on the preset. */
296
+ presetSettings?(): SettingsField[];
297
+ /** Known extra env var suggestions for the preset editor. */
298
298
  suggestedEnv?(): {
299
299
  key: string;
300
300
  description: string;
301
301
  }[];
302
- /** Known CLI flag suggestions for the profile editor. */
302
+ /** Known CLI flag suggestions for the preset editor. */
303
303
  suggestedFlags?(): {
304
304
  flag: string;
305
305
  description: string;
@@ -337,7 +337,11 @@ interface SubshellManifest {
337
337
  name: string;
338
338
  /** One-line description shown in the UI */
339
339
  description: string;
340
- /** Optional emoji/glyph */
340
+ /**
341
+ * Relative path to this plugin's icon file, e.g. `icon.svg`. Served by
342
+ * the control plane at `/api/plugins/<id>/icon`; absent means the UI
343
+ * renders a monogram instead.
344
+ */
341
345
  icon?: string;
342
346
  /** Module to import, relative to the package directory */
343
347
  entry: string;
@@ -366,7 +370,7 @@ declare const manifest: SubshellManifest;
366
370
  *
367
371
  * Launch shape:
368
372
  * codex [-m <model>] [-s <sandbox>] [-a <approval>] [mcp -c pairs]
369
- * [profile flags] [extra flags]
373
+ * [preset flags] [extra flags]
370
374
  * A bare launch (no subcommand, no prompt) opens the interactive TUI. Codex
371
375
  * has no create-time flag for naming its own session and no way to pin a
372
376
  * conversation id, so neither the subshell name nor a resume capability is
@@ -374,7 +378,7 @@ declare const manifest: SubshellManifest;
374
378
  * The pane's
375
379
  * cwd is already the working directory, so `-C/--cd` is never passed, and
376
380
  * `--dangerously-bypass-approvals-and-sandbox` is NEVER baked into a launch
377
- * (a user may put it in their own profile flags — that is their call).
381
+ * (a user may put it in their own preset flags — that is their call).
378
382
  *
379
383
  * subshell MCP is wired automatically WITHOUT touching user state: every
380
384
  * launch carries `-c mcp_servers.subshell.command=… -c mcp_servers.subshell.args=…`
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../plugin-api/dist/manifest-UFScnYqn.js
1
+ //#region ../../plugin-api/dist/manifest-Czd9ea1g.js
2
2
  /** Every {@link PluginType}, for anything that has to iterate or validate them. */
3
3
  const PLUGIN_TYPES = ["agent-harness", "terminal"];
4
4
  /**
@@ -9,6 +9,17 @@ const PLUGIN_TYPES = ["agent-harness", "terminal"];
9
9
  */
10
10
  const MCP_SERVER_NAME = "subshell";
11
11
  /**
12
+ * Image formats a plugin icon may be. The server maps these to a Content-Type
13
+ * from a fixed table rather than sniffing the file, so this list and that
14
+ * table are one decision: adding a format here without adding it there leaves
15
+ * an icon that parses and then 500s when something asks for it.
16
+ */
17
+ const ICON_EXTENSIONS = [
18
+ ".svg",
19
+ ".png",
20
+ ".webp"
21
+ ];
22
+ /**
12
23
  * Ids become directory names under `<dataDir>/plugins/`, so they are path
13
24
  * segments and nothing else: no separators, no traversal, no surprises from a
14
25
  * case-insensitive filesystem.
@@ -31,13 +42,18 @@ function parseManifest(pkgJson) {
31
42
  const block = pkgJson.subshell;
32
43
  if (!isRecord(block)) return { error: "package.json has no `subshell` block, so it is not a Subshell plugin" };
33
44
  if (typeof block.apiVersion !== "number" || !Number.isInteger(block.apiVersion)) return { error: "`subshell.apiVersion` must be an integer" };
34
- if (block.apiVersion < 1) return { error: `this plugin declares plugin-api ${block.apiVersion}, which is not a version; the lowest is 1 and this host implements 1` };
35
- if (block.apiVersion > 1) return { error: `this plugin needs plugin-api ${block.apiVersion}, but this host implements 1; upgrade the agent` };
45
+ if (block.apiVersion < 1) return { error: `this plugin declares plugin-api ${block.apiVersion}, which is not a version; the lowest is 1 and this host implements 2` };
46
+ if (block.apiVersion > 2) return { error: `this plugin needs plugin-api ${block.apiVersion}, but this host implements 2; upgrade the agent` };
36
47
  if (typeof block.id !== "string" || !ID_RE.test(block.id)) return { error: "`subshell.id` must be lowercase letters, digits and hyphens, starting alphanumeric (it becomes a directory name)" };
37
48
  if (typeof block.type !== "string" || !PLUGIN_TYPES.includes(block.type)) return { error: `\`subshell.type\` must be one of: ${PLUGIN_TYPES.join(", ")}` };
38
49
  if (typeof block.name !== "string" || block.name.trim() === "") return { error: "`subshell.name` must be a non-empty string" };
39
50
  if (typeof block.description !== "string") return { error: "`subshell.description` must be a string" };
40
- if (block.icon !== void 0 && typeof block.icon !== "string") return { error: "`subshell.icon` must be a string" };
51
+ if (block.icon !== void 0) {
52
+ if (typeof block.icon !== "string" || block.icon.trim() === "") return { error: "`subshell.icon` must be a non-empty relative path to an image in the package" };
53
+ if (block.icon.startsWith("/") || block.icon.split("/").includes("..")) return { error: "`subshell.icon` must stay inside the package (no leading `/` and no `..` segment)" };
54
+ const icon = block.icon;
55
+ if (!ICON_EXTENSIONS.some((ext) => icon.endsWith(ext))) return { error: `\`subshell.icon\` must end in one of: ${ICON_EXTENSIONS.join(", ")}` };
56
+ }
41
57
  if (typeof block.entry !== "string" || block.entry.trim() === "") return { error: "`subshell.entry` must be a non-empty relative path" };
42
58
  if (block.entry.startsWith("/") || block.entry.split("/").includes("..")) return { error: "`subshell.entry` must stay inside the package (no leading `/` and no `..` segment)" };
43
59
  let detect;
@@ -80,23 +96,23 @@ function parseManifest(pkgJson) {
80
96
  //#endregion
81
97
  //#region ../../plugin-api/dist/index.js
82
98
  /**
83
- * The profile checks every built-in harness performs unchanged: a non-empty
99
+ * The preset checks every built-in harness performs unchanged: a non-empty
84
100
  * name, string env values, and flags that look like flags. Harnesses with
85
101
  * extra rules run their own checks on top of these.
86
- * @param profile - The decoded profile definition to check
102
+ * @param preset - The decoded preset definition to check
87
103
  * @returns Issues per field; `valid` is true when no issues were found
88
104
  */
89
- function validateGenericProfile(profile) {
105
+ function validateGenericPreset(preset) {
90
106
  const issues = [];
91
- if (!profile.name || profile.name.trim().length === 0) issues.push({
107
+ if (!preset.name || preset.name.trim().length === 0) issues.push({
92
108
  field: "name",
93
- message: "Profile name is required."
109
+ message: "Preset name is required."
94
110
  });
95
- for (const [key, value] of Object.entries(profile.env)) if (typeof value !== "string") issues.push({
111
+ for (const [key, value] of Object.entries(preset.env)) if (typeof value !== "string") issues.push({
96
112
  field: "env",
97
113
  message: `Env var "${key}" must be a string.`
98
114
  });
99
- for (const flag of profile.flags) if (!flag.startsWith("-")) issues.push({
115
+ for (const flag of preset.flags) if (!flag.startsWith("-")) issues.push({
100
116
  field: "flags",
101
117
  message: `Flag "${flag}" must start with "-".`
102
118
  });
@@ -118,7 +134,7 @@ function validateGenericProfile(profile) {
118
134
  const parsed = parseManifest({
119
135
  name: "@subshell-ai/plugin-codex",
120
136
  description: "Codex harness plugin for Subshell",
121
- version: "0.1.1",
137
+ version: "0.2.0",
122
138
  license: "Apache-2.0",
123
139
  type: "module",
124
140
  main: "dist/index.js",
@@ -152,15 +168,15 @@ const parsed = parseManifest({
152
168
  "tsdown": "0.22.14",
153
169
  "typescript": "7.0.2"
154
170
  },
155
- files: ["dist"],
171
+ files: ["dist", "icon.svg"],
156
172
  publishConfig: { "access": "public" },
157
173
  subshell: {
158
- "apiVersion": 1,
174
+ "apiVersion": 2,
159
175
  "id": "codex",
160
176
  "type": "agent-harness",
161
177
  "name": "Codex",
162
178
  "description": "OpenAI's agentic coding CLI (interactive TUI)",
163
- "icon": "📖",
179
+ "icon": "icon.svg",
164
180
  "entry": "dist/index.js",
165
181
  "detect": {
166
182
  "binaryName": "codex",
@@ -239,7 +255,7 @@ function tomlStringArray(values) {
239
255
  *
240
256
  * Launch shape:
241
257
  * codex [-m <model>] [-s <sandbox>] [-a <approval>] [mcp -c pairs]
242
- * [profile flags] [extra flags]
258
+ * [preset flags] [extra flags]
243
259
  * A bare launch (no subcommand, no prompt) opens the interactive TUI. Codex
244
260
  * has no create-time flag for naming its own session and no way to pin a
245
261
  * conversation id, so neither the subshell name nor a resume capability is
@@ -247,7 +263,7 @@ function tomlStringArray(values) {
247
263
  * The pane's
248
264
  * cwd is already the working directory, so `-C/--cd` is never passed, and
249
265
  * `--dangerously-bypass-approvals-and-sandbox` is NEVER baked into a launch
250
- * (a user may put it in their own profile flags — that is their call).
266
+ * (a user may put it in their own preset flags — that is their call).
251
267
  *
252
268
  * subshell MCP is wired automatically WITHOUT touching user state: every
253
269
  * launch carries `-c mcp_servers.subshell.command=… -c mcp_servers.subshell.args=…`
@@ -268,14 +284,14 @@ function tomlStringArray(values) {
268
284
  const createPlugin = (_host) => ({
269
285
  capabilities: () => ["mcp", "settings"],
270
286
  buildCommand(input) {
271
- const { binary, profile, extraFlags, mcp } = input;
287
+ const { binary, preset, extraFlags, mcp } = input;
272
288
  const args = [binary];
273
- const s = profile.settings ?? {};
289
+ const s = preset.settings ?? {};
274
290
  if (typeof s.model === "string" && s.model) args.push("-m", s.model);
275
291
  if (typeof s.sandbox === "string" && s.sandbox) args.push("-s", s.sandbox);
276
292
  if (typeof s.askForApproval === "string" && s.askForApproval) args.push("-a", s.askForApproval);
277
293
  if (mcp?.args) args.push(...mcp.args);
278
- for (const flag of profile.flags) args.push(flag);
294
+ for (const flag of preset.flags) args.push(flag);
279
295
  if (extraFlags) args.push(...extraFlags);
280
296
  return args;
281
297
  },
@@ -317,10 +333,10 @@ const createPlugin = (_host) => ({
317
333
  summary: "Subshell registers itself with every Codex subshell automatically (per-invocation -c mcp_servers.subshell.* overrides; your ~/.codex config is never modified)."
318
334
  };
319
335
  },
320
- validateProfile(profile) {
321
- return validateGenericProfile(profile);
336
+ validatePreset(preset) {
337
+ return validateGenericPreset(preset);
322
338
  },
323
- profileSettings() {
339
+ presetSettings() {
324
340
  return CODEX_SETTINGS_FIELDS;
325
341
  },
326
342
  suggestedEnv() {
package/icon.svg ADDED
@@ -0,0 +1,4 @@
1
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><title>Codex</title>
2
+ <circle cx="12" cy="12" r="12" fill="#0080F7"/>
3
+ <path d="M9.94494 9.59163V8.13227C9.94494 8.00935 9.99105 7.91713 10.0985 7.85575L13.0327 6.16599C13.4321 5.93558 13.9083 5.8281 14.3998 5.8281C16.2432 5.8281 17.4108 7.25677 17.4108 8.77751C17.4108 8.885 17.4108 9.00792 17.3953 9.13083L14.3537 7.34884C14.1694 7.24135 13.985 7.24135 13.8007 7.34884L9.94494 9.59163ZM16.7963 15.2755V11.7883C16.7963 11.5732 16.704 11.4196 16.5197 11.3121L12.664 9.0693L13.9236 8.34725C14.0311 8.28587 14.1234 8.28587 14.2308 8.34725L17.165 10.037C18.0099 10.5287 18.5782 11.5732 18.5782 12.587C18.5782 13.7544 17.887 14.8298 16.7963 15.2753V15.2755ZM9.03861 12.2031L7.77896 11.4658C7.67146 11.4045 7.62535 11.3122 7.62535 11.1893V7.8098C7.62535 6.16613 8.88501 4.92176 10.5902 4.92176C11.2354 4.92176 11.8344 5.13689 12.3415 5.52089L9.31526 7.27218C9.13097 7.37968 9.03875 7.53328 9.03875 7.74841V12.2033L9.03861 12.2031ZM11.75 13.77L9.94494 12.7562V10.6056L11.75 9.59178L13.5549 10.6056V12.7562L11.75 13.77ZM12.9098 18.44C12.2645 18.44 11.6655 18.2249 11.1585 17.8409L14.1847 16.0896C14.369 15.9821 14.4612 15.8285 14.4612 15.6134V11.1585L15.7363 11.8958C15.8438 11.9572 15.8899 12.0494 15.8899 12.1723V15.5519C15.8899 17.1955 14.6148 18.44 12.9098 18.44ZM9.26901 15.0144L6.33486 13.3246C5.4899 12.833 4.92161 11.7885 4.92161 10.7746C4.92161 9.59177 5.62824 8.53183 6.71886 8.0863V11.5887C6.71886 11.8039 6.81109 11.9575 6.99538 12.065L10.8359 14.2923L9.57621 15.0144C9.46872 15.0758 9.37649 15.0758 9.26901 15.0144ZM9.10013 17.5337C7.36426 17.5337 6.08919 16.2279 6.08919 14.6149C6.08919 14.492 6.1046 14.3691 6.11988 14.2462L9.1461 15.9975C9.33039 16.105 9.51483 16.105 9.69912 15.9975L13.5549 13.7702V15.2295C13.5549 15.3524 13.5088 15.4446 13.4013 15.506L10.4671 17.1958C10.0677 17.4262 9.59148 17.5337 9.09999 17.5337H9.10013ZM12.9098 19.3616C14.7685 19.3616 16.32 18.0406 16.6735 16.2893C18.3939 15.8438 19.5 14.2308 19.5 12.5872C19.5 11.5118 19.0391 10.4673 18.2096 9.71454C18.2864 9.39192 18.3326 9.0693 18.3326 8.74682C18.3326 6.55014 16.5505 4.90634 14.4921 4.90634C14.0774 4.90634 13.6779 4.96772 13.2785 5.10605C12.5872 4.43011 11.6347 4 10.5902 4C8.7314 4 7.17996 5.32103 6.8265 7.07232C5.10605 7.51786 4 9.13083 4 10.7745C4 11.8498 4.4608 12.8944 5.29035 13.6471C5.21354 13.9697 5.16743 14.2923 5.16743 14.6148C5.16743 16.8114 6.94941 18.4552 9.00792 18.4552C9.42261 18.4552 9.82204 18.3939 10.2215 18.2556C10.9127 18.9315 11.8651 19.3616 12.9098 19.3616Z" fill="white"/>
4
+ </svg>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@subshell-ai/plugin-codex",
3
3
  "description": "Codex harness plugin for Subshell",
4
- "version": "0.1.1",
4
+ "version": "0.2.0",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -39,19 +39,17 @@
39
39
  "tsdown": "0.22.14",
40
40
  "typescript": "7.0.2"
41
41
  },
42
- "files": [
43
- "dist"
44
- ],
42
+ "files": ["dist", "icon.svg"],
45
43
  "publishConfig": {
46
44
  "access": "public"
47
45
  },
48
46
  "subshell": {
49
- "apiVersion": 1,
47
+ "apiVersion": 2,
50
48
  "id": "codex",
51
49
  "type": "agent-harness",
52
50
  "name": "Codex",
53
51
  "description": "OpenAI's agentic coding CLI (interactive TUI)",
54
- "icon": "📖",
52
+ "icon": "icon.svg",
55
53
  "entry": "dist/index.js",
56
54
  "detect": {
57
55
  "binaryName": "codex",