@subshell-ai/plugin-codex 0.1.0 → 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/LICENSE +203 -0
- package/dist/index.d.ts +60 -23
- package/dist/index.js +46 -23
- package/icon.svg +4 -0
- package/package.json +13 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Copyright 2026 Disaresta, LLC
|
|
2
|
+
|
|
3
|
+
Apache License
|
|
4
|
+
Version 2.0, January 2004
|
|
5
|
+
http://www.apache.org/licenses/
|
|
6
|
+
|
|
7
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
8
|
+
|
|
9
|
+
1. Definitions.
|
|
10
|
+
|
|
11
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
12
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
13
|
+
|
|
14
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
15
|
+
the copyright owner that is granting the License.
|
|
16
|
+
|
|
17
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
18
|
+
other entities that control, are controlled by, or are under common
|
|
19
|
+
control with that entity. For the purposes of this definition,
|
|
20
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
21
|
+
direction or management of such entity, whether by contract or
|
|
22
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
23
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
24
|
+
|
|
25
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
26
|
+
exercising permissions granted by this License.
|
|
27
|
+
|
|
28
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
29
|
+
including but not limited to software source code, documentation
|
|
30
|
+
source, and configuration files.
|
|
31
|
+
|
|
32
|
+
"Object" form shall mean any form resulting from mechanical
|
|
33
|
+
transformation or translation of a Source form, including but
|
|
34
|
+
not limited to compiled object code, generated documentation,
|
|
35
|
+
and conversions to other media types.
|
|
36
|
+
|
|
37
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
38
|
+
Object form, made available under the License, as indicated by a
|
|
39
|
+
copyright notice that is included in or attached to the work
|
|
40
|
+
(an example is provided in the Appendix below).
|
|
41
|
+
|
|
42
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
43
|
+
form, that is based on (or derived from) the Work and for which the
|
|
44
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
45
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
46
|
+
of this License, Derivative Works shall not include works that remain
|
|
47
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
48
|
+
the Work and Derivative Works thereof.
|
|
49
|
+
|
|
50
|
+
"Contribution" shall mean any work of authorship, including
|
|
51
|
+
the original version of the Work and any modifications or additions
|
|
52
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
53
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
54
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
55
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
56
|
+
means any form of electronic, verbal, or written communication sent
|
|
57
|
+
to the Licensor or its representatives, including but not limited to
|
|
58
|
+
communication on electronic mailing lists, source code control systems,
|
|
59
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
60
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
61
|
+
excluding communication that is conspicuously marked or otherwise
|
|
62
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
63
|
+
|
|
64
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
65
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
66
|
+
subsequently incorporated within the Work.
|
|
67
|
+
|
|
68
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
69
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
70
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
71
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
72
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
73
|
+
Work and such Derivative Works in Source or Object form.
|
|
74
|
+
|
|
75
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
76
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
77
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
78
|
+
(except as stated in this section) patent license to make, have made,
|
|
79
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
80
|
+
where such license applies only to those patent claims licensable
|
|
81
|
+
by such Contributor that are necessarily infringed by their
|
|
82
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
83
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
84
|
+
institute patent litigation against any entity (including a
|
|
85
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
86
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
87
|
+
or contributory patent infringement, then any patent licenses
|
|
88
|
+
granted to You under this License for that Work shall terminate
|
|
89
|
+
as of the date such litigation is filed.
|
|
90
|
+
|
|
91
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
92
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
93
|
+
modifications, and in Source or Object form, provided that You
|
|
94
|
+
meet the following conditions:
|
|
95
|
+
|
|
96
|
+
(a) You must give any other recipients of the Work or
|
|
97
|
+
Derivative Works a copy of this License; and
|
|
98
|
+
|
|
99
|
+
(b) You must cause any modified files to carry prominent notices
|
|
100
|
+
stating that You changed the files; and
|
|
101
|
+
|
|
102
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
103
|
+
that You distribute, all copyright, patent, trademark, and
|
|
104
|
+
attribution notices from the Source form of the Work,
|
|
105
|
+
excluding those notices that do not pertain to any part of
|
|
106
|
+
the Derivative Works; and
|
|
107
|
+
|
|
108
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
109
|
+
distribution, then any Derivative Works that You distribute must
|
|
110
|
+
include a readable copy of the attribution notices contained
|
|
111
|
+
within such NOTICE file, excluding those notices that do not
|
|
112
|
+
pertain to any part of the Derivative Works, in at least one
|
|
113
|
+
of the following places: within a NOTICE text file distributed
|
|
114
|
+
as part of the Derivative Works; within the Source form or
|
|
115
|
+
documentation, if provided along with the Derivative Works; or,
|
|
116
|
+
within a display generated by the Derivative Works, if and
|
|
117
|
+
wherever such third-party notices normally appear. The contents
|
|
118
|
+
of the NOTICE file are for informational purposes only and
|
|
119
|
+
do not modify the License. You may add Your own attribution
|
|
120
|
+
notices within Derivative Works that You distribute, alongside
|
|
121
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
122
|
+
that such additional attribution notices cannot be construed
|
|
123
|
+
as modifying the License.
|
|
124
|
+
|
|
125
|
+
You may add Your own copyright statement to Your modifications and
|
|
126
|
+
may provide additional or different license terms and conditions
|
|
127
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
128
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
129
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
130
|
+
the conditions stated in this License.
|
|
131
|
+
|
|
132
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
133
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
134
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
135
|
+
this License, without any additional terms or conditions.
|
|
136
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
137
|
+
the terms of any separate license agreement you may have executed
|
|
138
|
+
with Licensor regarding such Contributions.
|
|
139
|
+
|
|
140
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
141
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
142
|
+
except as required for reasonable and customary use in describing the
|
|
143
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
144
|
+
|
|
145
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
146
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
147
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
148
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
149
|
+
implied, including, without limitation, any warranties or conditions
|
|
150
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
151
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
152
|
+
appropriateness of using or redistributing the Work and assume any
|
|
153
|
+
risks associated with Your exercise of permissions under this License.
|
|
154
|
+
|
|
155
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
156
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
157
|
+
unless required by applicable law (such as deliberate and grossly
|
|
158
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
159
|
+
liable to You for damages, including any direct, indirect, special,
|
|
160
|
+
incidental, or consequential damages of any character arising as a
|
|
161
|
+
result of this License or out of the use or inability to use the
|
|
162
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
163
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
164
|
+
other commercial damages or losses), even if such Contributor
|
|
165
|
+
has been advised of the possibility of such damages.
|
|
166
|
+
|
|
167
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
168
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
169
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
170
|
+
or other liability obligations and/or rights consistent with this
|
|
171
|
+
License. However, in accepting such obligations, You may act only
|
|
172
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
173
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
174
|
+
defend, and hold each Contributor harmless for any liability
|
|
175
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
176
|
+
of your accepting any such warranty or additional liability.
|
|
177
|
+
|
|
178
|
+
END OF TERMS AND CONDITIONS
|
|
179
|
+
|
|
180
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
181
|
+
|
|
182
|
+
To apply the Apache License to your work, attach the following
|
|
183
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
184
|
+
replaced with your own identifying information. (Don't include
|
|
185
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
186
|
+
comment syntax for the file format. We also recommend that a
|
|
187
|
+
file or class name and description of purpose be included on the
|
|
188
|
+
same "printed page" as the copyright notice for easier
|
|
189
|
+
identification within third-party archives.
|
|
190
|
+
|
|
191
|
+
Copyright [yyyy] [name of copyright owner]
|
|
192
|
+
|
|
193
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
194
|
+
you may not use this file except in compliance with the License.
|
|
195
|
+
You may obtain a copy of the License at
|
|
196
|
+
|
|
197
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
198
|
+
|
|
199
|
+
Unless required by applicable law or agreed to in writing, software
|
|
200
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
201
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
202
|
+
See the License for the specific language governing permissions and
|
|
203
|
+
limitations under the License.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../plugin-api/dist/types-
|
|
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
|
|
31
|
-
interface
|
|
32
|
-
/** Human-friendly
|
|
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
|
|
42
|
+
/** If true, only this preset's config sources apply (isolation) */
|
|
43
43
|
configIsolation: boolean;
|
|
44
|
-
/** If true, new subshells from this
|
|
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
|
|
48
|
-
interface
|
|
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
|
|
54
|
+
interface PresetValidationResult {
|
|
55
55
|
valid: boolean;
|
|
56
|
-
issues:
|
|
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
|
|
65
|
-
|
|
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 */
|
|
@@ -88,6 +88,39 @@ interface BuildCommandInput {
|
|
|
88
88
|
id: string;
|
|
89
89
|
mode: "start" | "resume";
|
|
90
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* How a harness hook re-enters the subshell binary on the PANE's machine, as
|
|
93
|
+
* the host resolved it for that machine: `{ command, args }` with the
|
|
94
|
+
* reporting subcommand already appended, so a plugin adds only its own verb
|
|
95
|
+
* words. Splice it into whatever hook mechanism the harness has.
|
|
96
|
+
*
|
|
97
|
+
* A hook runs where the pane runs, and the only program guaranteed to exist
|
|
98
|
+
* there is the binary that launched the pane — which is why this arrives
|
|
99
|
+
* from the host rather than being a command a plugin writes for itself. The
|
|
100
|
+
* hooks here were once `bun -e '<inlined JS>'`, and every machine without a
|
|
101
|
+
* bun on its PATH (i.e. most of them) opened its harness on
|
|
102
|
+
* `bun: command not found`.
|
|
103
|
+
*
|
|
104
|
+
* Undefined means the host could not resolve one. OMIT the hooks then: a
|
|
105
|
+
* command the pane cannot run costs the same signal and adds an error to
|
|
106
|
+
* every session.
|
|
107
|
+
*/
|
|
108
|
+
reporter?: ReporterSpec;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* A command prefix that re-enters the subshell binary on a pane's machine,
|
|
112
|
+
* ready for a plugin's own verb words to be appended.
|
|
113
|
+
*
|
|
114
|
+
* Shaped like {@link McpLaunchSpec} and resolved from the same host question
|
|
115
|
+
* ("am I a compiled binary, or is `bun` running my entry script?"), but kept
|
|
116
|
+
* separate because they carry different subcommands and the MCP one is
|
|
117
|
+
* operator-overridable while this one is not.
|
|
118
|
+
*/
|
|
119
|
+
interface ReporterSpec {
|
|
120
|
+
/** Executable to run (the binary itself, or the interpreter running it). */
|
|
121
|
+
command: string;
|
|
122
|
+
/** Everything before the plugin's verb words (any entry script, then the subcommand). */
|
|
123
|
+
args: string[];
|
|
91
124
|
}
|
|
92
125
|
/**
|
|
93
126
|
* Restart-resume, implemented only by plugins that can continue a previous
|
|
@@ -136,12 +169,12 @@ interface McpRegistration {
|
|
|
136
169
|
args?: string[];
|
|
137
170
|
/**
|
|
138
171
|
* Extra pane env the harness needs to discover the file (e.g. OPENCODE_CONFIG).
|
|
139
|
-
* Consumed by the HOST, which bakes it into the pane env ahead of
|
|
172
|
+
* Consumed by the HOST, which bakes it into the pane env ahead of preset env;
|
|
140
173
|
* plugins never read it back.
|
|
141
174
|
*/
|
|
142
175
|
env?: Record<string, string>;
|
|
143
176
|
}
|
|
144
|
-
/** One copy-paste line shown in the
|
|
177
|
+
/** One copy-paste line shown in the preset editor for manual-setup harnesses. */
|
|
145
178
|
interface McpSetupStep {
|
|
146
179
|
/** What the user should do, and where the text goes */
|
|
147
180
|
label: string;
|
|
@@ -226,8 +259,8 @@ interface PluginHost {
|
|
|
226
259
|
interface SubshellPlugin {
|
|
227
260
|
/** Builds the argv (no shell) used to launch a subshell. */
|
|
228
261
|
buildCommand(input: BuildCommandInput): string[];
|
|
229
|
-
/** Validates a
|
|
230
|
-
|
|
262
|
+
/** Validates a preset definition before saving. */
|
|
263
|
+
validatePreset(preset: PresetDefinition): PresetValidationResult;
|
|
231
264
|
/** Which optional members below are meaningful on this plugin. */
|
|
232
265
|
capabilities(): PluginCapability[];
|
|
233
266
|
/** Maps a harness exit code to a human label (null = unknown). */
|
|
@@ -259,14 +292,14 @@ interface SubshellPlugin {
|
|
|
259
292
|
mcpRegistration?(launch: McpLaunchSpec, configPath: string): McpRegistration;
|
|
260
293
|
/** How users obtain the subshell MCP tools here. Declare the `mcp` capability with it. */
|
|
261
294
|
mcpSetup?(launch: McpLaunchSpec): McpSetupInfo;
|
|
262
|
-
/** Settings rendered in the
|
|
263
|
-
|
|
264
|
-
/** Known extra env var suggestions for the
|
|
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. */
|
|
265
298
|
suggestedEnv?(): {
|
|
266
299
|
key: string;
|
|
267
300
|
description: string;
|
|
268
301
|
}[];
|
|
269
|
-
/** Known CLI flag suggestions for the
|
|
302
|
+
/** Known CLI flag suggestions for the preset editor. */
|
|
270
303
|
suggestedFlags?(): {
|
|
271
304
|
flag: string;
|
|
272
305
|
description: string;
|
|
@@ -304,7 +337,11 @@ interface SubshellManifest {
|
|
|
304
337
|
name: string;
|
|
305
338
|
/** One-line description shown in the UI */
|
|
306
339
|
description: string;
|
|
307
|
-
/**
|
|
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
|
+
*/
|
|
308
345
|
icon?: string;
|
|
309
346
|
/** Module to import, relative to the package directory */
|
|
310
347
|
entry: string;
|
|
@@ -333,7 +370,7 @@ declare const manifest: SubshellManifest;
|
|
|
333
370
|
*
|
|
334
371
|
* Launch shape:
|
|
335
372
|
* codex [-m <model>] [-s <sandbox>] [-a <approval>] [mcp -c pairs]
|
|
336
|
-
* [
|
|
373
|
+
* [preset flags] [extra flags]
|
|
337
374
|
* A bare launch (no subcommand, no prompt) opens the interactive TUI. Codex
|
|
338
375
|
* has no create-time flag for naming its own session and no way to pin a
|
|
339
376
|
* conversation id, so neither the subshell name nor a resume capability is
|
|
@@ -341,7 +378,7 @@ declare const manifest: SubshellManifest;
|
|
|
341
378
|
* The pane's
|
|
342
379
|
* cwd is already the working directory, so `-C/--cd` is never passed, and
|
|
343
380
|
* `--dangerously-bypass-approvals-and-sandbox` is NEVER baked into a launch
|
|
344
|
-
* (a user may put it in their own
|
|
381
|
+
* (a user may put it in their own preset flags — that is their call).
|
|
345
382
|
*
|
|
346
383
|
* subshell MCP is wired automatically WITHOUT touching user state: every
|
|
347
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-
|
|
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
|
|
35
|
-
if (block.apiVersion >
|
|
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
|
|
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
|
|
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
|
|
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
|
|
105
|
+
function validateGenericPreset(preset) {
|
|
90
106
|
const issues = [];
|
|
91
|
-
if (!
|
|
107
|
+
if (!preset.name || preset.name.trim().length === 0) issues.push({
|
|
92
108
|
field: "name",
|
|
93
|
-
message: "
|
|
109
|
+
message: "Preset name is required."
|
|
94
110
|
});
|
|
95
|
-
for (const [key, value] of Object.entries(
|
|
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
|
|
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.
|
|
137
|
+
version: "0.2.0",
|
|
122
138
|
license: "Apache-2.0",
|
|
123
139
|
type: "module",
|
|
124
140
|
main: "dist/index.js",
|
|
@@ -127,6 +143,13 @@ const parsed = parseManifest({
|
|
|
127
143
|
"import": "./dist/index.js"
|
|
128
144
|
} },
|
|
129
145
|
types: "dist/index.d.ts",
|
|
146
|
+
homepage: "https://subshell.sh",
|
|
147
|
+
repository: {
|
|
148
|
+
"type": "git",
|
|
149
|
+
"url": "git+https://github.com/subshell-ai/subshell.git",
|
|
150
|
+
"directory": "packages/plugins/codex"
|
|
151
|
+
},
|
|
152
|
+
bugs: { "url": "https://github.com/subshell-ai/subshell/issues" },
|
|
130
153
|
scripts: {
|
|
131
154
|
"build": "tsdown",
|
|
132
155
|
"build:dev": "hash-runner",
|
|
@@ -145,15 +168,15 @@ const parsed = parseManifest({
|
|
|
145
168
|
"tsdown": "0.22.14",
|
|
146
169
|
"typescript": "7.0.2"
|
|
147
170
|
},
|
|
148
|
-
files: ["dist"],
|
|
171
|
+
files: ["dist", "icon.svg"],
|
|
149
172
|
publishConfig: { "access": "public" },
|
|
150
173
|
subshell: {
|
|
151
|
-
"apiVersion":
|
|
174
|
+
"apiVersion": 2,
|
|
152
175
|
"id": "codex",
|
|
153
176
|
"type": "agent-harness",
|
|
154
177
|
"name": "Codex",
|
|
155
178
|
"description": "OpenAI's agentic coding CLI (interactive TUI)",
|
|
156
|
-
"icon": "
|
|
179
|
+
"icon": "icon.svg",
|
|
157
180
|
"entry": "dist/index.js",
|
|
158
181
|
"detect": {
|
|
159
182
|
"binaryName": "codex",
|
|
@@ -232,7 +255,7 @@ function tomlStringArray(values) {
|
|
|
232
255
|
*
|
|
233
256
|
* Launch shape:
|
|
234
257
|
* codex [-m <model>] [-s <sandbox>] [-a <approval>] [mcp -c pairs]
|
|
235
|
-
* [
|
|
258
|
+
* [preset flags] [extra flags]
|
|
236
259
|
* A bare launch (no subcommand, no prompt) opens the interactive TUI. Codex
|
|
237
260
|
* has no create-time flag for naming its own session and no way to pin a
|
|
238
261
|
* conversation id, so neither the subshell name nor a resume capability is
|
|
@@ -240,7 +263,7 @@ function tomlStringArray(values) {
|
|
|
240
263
|
* The pane's
|
|
241
264
|
* cwd is already the working directory, so `-C/--cd` is never passed, and
|
|
242
265
|
* `--dangerously-bypass-approvals-and-sandbox` is NEVER baked into a launch
|
|
243
|
-
* (a user may put it in their own
|
|
266
|
+
* (a user may put it in their own preset flags — that is their call).
|
|
244
267
|
*
|
|
245
268
|
* subshell MCP is wired automatically WITHOUT touching user state: every
|
|
246
269
|
* launch carries `-c mcp_servers.subshell.command=… -c mcp_servers.subshell.args=…`
|
|
@@ -261,14 +284,14 @@ function tomlStringArray(values) {
|
|
|
261
284
|
const createPlugin = (_host) => ({
|
|
262
285
|
capabilities: () => ["mcp", "settings"],
|
|
263
286
|
buildCommand(input) {
|
|
264
|
-
const { binary,
|
|
287
|
+
const { binary, preset, extraFlags, mcp } = input;
|
|
265
288
|
const args = [binary];
|
|
266
|
-
const s =
|
|
289
|
+
const s = preset.settings ?? {};
|
|
267
290
|
if (typeof s.model === "string" && s.model) args.push("-m", s.model);
|
|
268
291
|
if (typeof s.sandbox === "string" && s.sandbox) args.push("-s", s.sandbox);
|
|
269
292
|
if (typeof s.askForApproval === "string" && s.askForApproval) args.push("-a", s.askForApproval);
|
|
270
293
|
if (mcp?.args) args.push(...mcp.args);
|
|
271
|
-
for (const flag of
|
|
294
|
+
for (const flag of preset.flags) args.push(flag);
|
|
272
295
|
if (extraFlags) args.push(...extraFlags);
|
|
273
296
|
return args;
|
|
274
297
|
},
|
|
@@ -310,10 +333,10 @@ const createPlugin = (_host) => ({
|
|
|
310
333
|
summary: "Subshell registers itself with every Codex subshell automatically (per-invocation -c mcp_servers.subshell.* overrides; your ~/.codex config is never modified)."
|
|
311
334
|
};
|
|
312
335
|
},
|
|
313
|
-
|
|
314
|
-
return
|
|
336
|
+
validatePreset(preset) {
|
|
337
|
+
return validateGenericPreset(preset);
|
|
315
338
|
},
|
|
316
|
-
|
|
339
|
+
presetSettings() {
|
|
317
340
|
return CODEX_SETTINGS_FIELDS;
|
|
318
341
|
},
|
|
319
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.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -12,6 +12,15 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"types": "dist/index.d.ts",
|
|
15
|
+
"homepage": "https://subshell.sh",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/subshell-ai/subshell.git",
|
|
19
|
+
"directory": "packages/plugins/codex"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/subshell-ai/subshell/issues"
|
|
23
|
+
},
|
|
15
24
|
"scripts": {
|
|
16
25
|
"build": "tsdown",
|
|
17
26
|
"build:dev": "hash-runner",
|
|
@@ -30,19 +39,17 @@
|
|
|
30
39
|
"tsdown": "0.22.14",
|
|
31
40
|
"typescript": "7.0.2"
|
|
32
41
|
},
|
|
33
|
-
"files": [
|
|
34
|
-
"dist"
|
|
35
|
-
],
|
|
42
|
+
"files": ["dist", "icon.svg"],
|
|
36
43
|
"publishConfig": {
|
|
37
44
|
"access": "public"
|
|
38
45
|
},
|
|
39
46
|
"subshell": {
|
|
40
|
-
"apiVersion":
|
|
47
|
+
"apiVersion": 2,
|
|
41
48
|
"id": "codex",
|
|
42
49
|
"type": "agent-harness",
|
|
43
50
|
"name": "Codex",
|
|
44
51
|
"description": "OpenAI's agentic coding CLI (interactive TUI)",
|
|
45
|
-
"icon": "
|
|
52
|
+
"icon": "icon.svg",
|
|
46
53
|
"entry": "dist/index.js",
|
|
47
54
|
"detect": {
|
|
48
55
|
"binaryName": "codex",
|