superpowers-manager 0.5.2 → 0.7.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 +44 -26
- package/dist/adapter-result.js +5 -9
- package/dist/atomic.js +4 -14
- package/dist/cli-arguments.js +1 -27
- package/dist/cli.js +32 -52
- package/dist/commands/adapter-call.js +28 -0
- package/dist/commands/install.js +26 -107
- package/dist/commands/mutation.js +7 -0
- package/dist/commands/pin.js +1 -1
- package/dist/commands/prepare.js +39 -129
- package/dist/commands/probe.js +35 -264
- package/dist/commands/track-latest.js +1 -1
- package/dist/commands/uninstall.js +16 -92
- package/dist/commands/update.js +5 -25
- package/dist/effective-selection.js +7 -9
- package/dist/git-tree.js +85 -0
- package/dist/harness-command-result.js +47 -0
- package/dist/harnesses/codex/adapter.js +231 -510
- package/dist/harnesses/codex/compatibility.js +2 -17
- package/dist/harnesses/codex/conflicts.js +2 -3
- package/dist/harnesses/codex/harness.js +18 -263
- package/dist/harnesses/codex/hooks.js +1 -1
- package/dist/harnesses/codex/json.js +1 -11
- package/dist/harnesses/codex/lifecycle.js +80 -72
- package/dist/harnesses/codex/manifest-overlay.js +23 -18
- package/dist/harnesses/codex/paths.js +14 -29
- package/dist/harnesses/codex/presentation.js +8 -37
- package/dist/harnesses/codex/publication.js +13 -37
- package/dist/harnesses/codex/recovery.js +5 -5
- package/dist/harnesses/codex/stored-state.js +1 -1
- package/dist/harnesses/opencode/config.js +256 -0
- package/dist/harnesses/opencode/discovery.js +587 -0
- package/dist/harnesses/opencode/harness.js +37 -0
- package/dist/harnesses/opencode/install.js +601 -0
- package/dist/harnesses/opencode/native.js +57 -0
- package/dist/harnesses/opencode/package.js +89 -0
- package/dist/harnesses/opencode/paths.js +38 -0
- package/dist/harnesses/opencode/prepare.js +30 -0
- package/dist/harnesses/opencode/presentation.js +28 -0
- package/dist/harnesses/opencode/state.js +174 -0
- package/dist/harnesses/pi/compatibility.js +5 -37
- package/dist/harnesses/pi/install.js +7 -5
- package/dist/harnesses/pi/native.js +3 -47
- package/dist/harnesses/pi/package.js +5 -107
- package/dist/harnesses/pi/prepare.js +13 -104
- package/dist/harnesses/pi/presentation.js +23 -66
- package/dist/harnesses/pi/shared-skills.js +28 -39
- package/dist/harnesses/pi/state.js +7 -27
- package/dist/provenance.js +2 -2
- package/dist/python-json-format.js +0 -69
- package/dist/python-json.js +2 -27
- package/dist/python-text.js +9 -69
- package/dist/safe-path.js +2 -2
- package/dist/selection-store.js +2 -22
- package/dist/snapshot-package.js +78 -0
- package/dist/snapshot-prepare.js +99 -0
- package/dist/snapshot-presentation.js +44 -0
- package/dist/validator.js +39 -73
- package/node_modules/jsonc-parser/CHANGELOG.md +76 -0
- package/node_modules/jsonc-parser/LICENSE.md +21 -0
- package/node_modules/jsonc-parser/README.md +364 -0
- package/node_modules/jsonc-parser/SECURITY.md +41 -0
- package/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
- package/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
- package/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
- package/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
- package/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
- package/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
- package/node_modules/jsonc-parser/lib/esm/main.js +178 -0
- package/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
- package/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
- package/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
- package/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
- package/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
- package/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
- package/node_modules/jsonc-parser/lib/umd/main.js +194 -0
- package/node_modules/jsonc-parser/package.json +37 -0
- package/package.json +8 -4
- package/dist/domain/fingerprint.js +0 -4
- package/dist/selection-state-cli.js +0 -85
- package/dist/status.js +0 -24
- package/dist/upstream-cli.js +0 -110
- package/dist/validate-generated-plugin-cli.js +0 -118
|
@@ -2,21 +2,20 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { constants } from "node:fs";
|
|
4
4
|
import { access, copyFile, mkdir, readFile, realpath, stat, writeFile, } from "node:fs/promises";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
5
|
import { delimiter, isAbsolute, join } from "node:path";
|
|
7
|
-
import { AdapterMessageLog, failureResult, successResult, } from "../../adapter-result.js";
|
|
6
|
+
import { AdapterMessageLog, failureResult, hasTerminalControl, successResult, } from "../../adapter-result.js";
|
|
8
7
|
import { activePluginVersionFromJson, codexInstalledPluginsFromJson, installedListingHas, marketplaceRootFromJson, } from "./json.js";
|
|
9
8
|
import { CODEX_LEGACY_PLUGIN_ID, CODEX_MANAGER_PLUGIN_ID, inspectCodexConflicts, } from "./conflicts.js";
|
|
10
9
|
import { oneLine } from "../../cli-arguments.js";
|
|
11
|
-
import {
|
|
10
|
+
import { installedRootForVersion, pathsEqual } from "./state.js";
|
|
12
11
|
import { codexHome } from "./paths.js";
|
|
13
12
|
import { readCodexStoredState } from "./stored-state.js";
|
|
14
13
|
import { validateGeneratedPlugin } from "./generated-plugin.js";
|
|
15
14
|
import { classifyHooks, materializeHooks, readManifest, } from "./hooks.js";
|
|
16
15
|
import { applyManifestOverlay } from "./manifest-overlay.js";
|
|
17
16
|
import { readCodexBuildSource } from "../../provenance.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
17
|
+
import { codexOwnershipInspection } from "./lifecycle.js";
|
|
18
|
+
import { codexInstallReceipt } from "./presentation.js";
|
|
20
19
|
const PLUGIN_ID = CODEX_MANAGER_PLUGIN_ID;
|
|
21
20
|
const MARKETPLACE_NAME = "superpowers-manager";
|
|
22
21
|
const LEGACY_PLUGIN_ID = CODEX_LEGACY_PLUGIN_ID;
|
|
@@ -39,28 +38,6 @@ class AdapterFailure extends Error {
|
|
|
39
38
|
function fail(code, message, hints = []) {
|
|
40
39
|
throw new AdapterFailure(code, message, hints);
|
|
41
40
|
}
|
|
42
|
-
function parseFlags(argv, allowed) {
|
|
43
|
-
const values = {};
|
|
44
|
-
for (let index = 0; index < argv.length; index += 2) {
|
|
45
|
-
const flag = argv[index];
|
|
46
|
-
if (!allowed.includes(flag)) {
|
|
47
|
-
fail("invalid-arguments", `unknown flag: ${flag}`);
|
|
48
|
-
}
|
|
49
|
-
if (index + 1 >= argv.length) {
|
|
50
|
-
fail("invalid-arguments", `missing value for ${flag}`);
|
|
51
|
-
}
|
|
52
|
-
if (Object.hasOwn(values, flag)) {
|
|
53
|
-
fail("invalid-arguments", `duplicate flag: ${flag}`);
|
|
54
|
-
}
|
|
55
|
-
values[flag] = argv[index + 1];
|
|
56
|
-
}
|
|
57
|
-
for (const flag of allowed) {
|
|
58
|
-
if (!Object.hasOwn(values, flag)) {
|
|
59
|
-
fail("invalid-arguments", `missing required flag: ${flag}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return values;
|
|
63
|
-
}
|
|
64
41
|
function runCommand(file, args, env) {
|
|
65
42
|
// `git show ad56569a4c161e7b122967442e2b026eeb6395f6:scripts/core/common.sh:71::NODE_OPTIONS`
|
|
66
43
|
// is the system's only scrubbing site and it dies with scripts/ in 4c.
|
|
@@ -146,11 +123,6 @@ async function runCodexCommand(codexBin, args, env) {
|
|
|
146
123
|
return mapCodexLaunchFailure(cause, codexBin);
|
|
147
124
|
}
|
|
148
125
|
}
|
|
149
|
-
function reportOrphanedWorkspace(log) {
|
|
150
|
-
return (path) => {
|
|
151
|
-
log.appendText("stderr", workspaceRemovalFailure(path));
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
126
|
async function mutationCommand(log, codexBin, args, env) {
|
|
155
127
|
const result = await runCodexCommand(codexBin, args, env);
|
|
156
128
|
log.appendBytes("stderr", result.stdout);
|
|
@@ -188,16 +160,6 @@ async function requireCodex(codexBin, env) {
|
|
|
188
160
|
fail("command-not-found", `required Codex command not found: ${codexBin}`);
|
|
189
161
|
}
|
|
190
162
|
}
|
|
191
|
-
const BUILD_FLAGS = [
|
|
192
|
-
"--upstream-root",
|
|
193
|
-
"--candidate-root",
|
|
194
|
-
"--requested-ref",
|
|
195
|
-
"--resolved-ref",
|
|
196
|
-
"--commit",
|
|
197
|
-
"--manager-version",
|
|
198
|
-
"--upstream-manifest-version",
|
|
199
|
-
"--fallback-manifest",
|
|
200
|
-
];
|
|
201
163
|
async function directoryExists(path) {
|
|
202
164
|
try {
|
|
203
165
|
return (await stat(path)).isDirectory();
|
|
@@ -231,165 +193,120 @@ async function runBuild(input, env, log) {
|
|
|
231
193
|
if (!(await fileExists(fallbackManifest))) {
|
|
232
194
|
fail("invalid-arguments", `fallback manifest not found: ${fallbackManifest}`);
|
|
233
195
|
}
|
|
234
|
-
|
|
196
|
+
const candidateManifest = join(candidateRoot, ".codex-plugin/plugin.json");
|
|
197
|
+
const upstreamManifest = join(upstreamRoot, ".codex-plugin/plugin.json");
|
|
198
|
+
const manifestSource = (await fileExists(upstreamManifest))
|
|
199
|
+
? "upstream"
|
|
200
|
+
: "fallback";
|
|
235
201
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let plan;
|
|
255
|
-
let sourceRoot;
|
|
256
|
-
let realCandidateRoot;
|
|
257
|
-
try {
|
|
258
|
-
sourceRoot = await realpath(upstreamRoot);
|
|
259
|
-
realCandidateRoot = await realpath(candidateRoot);
|
|
260
|
-
const manifest = await readManifest(candidateManifest);
|
|
261
|
-
plan = await classifyHooks(manifest, manifestSource, sourceRoot);
|
|
262
|
-
}
|
|
263
|
-
catch (cause) {
|
|
264
|
-
log.appendText("stderr", `hook classification failed: ${oneLine(cause)}`);
|
|
265
|
-
fail("build-failed", "failed to prepare upstream Codex hooks");
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
await materializeHooks(plan, sourceRoot, realCandidateRoot);
|
|
269
|
-
}
|
|
270
|
-
catch (cause) {
|
|
271
|
-
log.appendText("stderr", `hook materialization failed: ${oneLine(cause)}`);
|
|
272
|
-
fail("build-failed", "failed to prepare upstream Codex hooks");
|
|
273
|
-
}
|
|
274
|
-
let source;
|
|
275
|
-
try {
|
|
276
|
-
// Decoded fatally, not leniently: the file can change between this read and `readManifest`'s read above.
|
|
277
|
-
const rawManifestBytes = await readFile(candidateManifest);
|
|
278
|
-
source = new TextDecoder("utf-8", {
|
|
279
|
-
fatal: true,
|
|
280
|
-
ignoreBOM: true,
|
|
281
|
-
}).decode(rawManifestBytes);
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
// Deliberately drops the cause. The Python interpolated the raw OSError here:
|
|
285
|
-
// `git show 0b6d50e1e9c688397285c6fa274dc8c9437d8ba3:scripts/adapters/codex/apply-manifest-overlay.py:42::read`,
|
|
286
|
-
// putting "[Errno 2] No such file or directory" on the operator's stream.
|
|
287
|
-
// The prefix is preserved; the errno leak is not.
|
|
288
|
-
log.appendText("stderr", `cannot read manifest JSON in ${candidateManifest}`);
|
|
289
|
-
fail("build-failed", "failed to apply manager manifest overlay");
|
|
290
|
-
}
|
|
291
|
-
let overlaid;
|
|
292
|
-
try {
|
|
293
|
-
overlaid = applyManifestOverlay(source, input.managerVersion, candidateManifest);
|
|
294
|
-
}
|
|
295
|
-
catch (cause) {
|
|
296
|
-
// applyManifestOverlay's own messages already name the manifest
|
|
297
|
-
// path — three are the frozen CPython wording, and the fourth (the
|
|
298
|
-
// numeric-overflow diagnostic, which has no CPython oracle wording
|
|
299
|
-
// to match) now carries the path via its own rewrap in
|
|
300
|
-
// src/harnesses/codex/manifest-overlay.ts. Emit as-is, with no added prefix — a
|
|
301
|
-
// prefix here would double up the path these messages already
|
|
302
|
-
// name.
|
|
303
|
-
log.appendText("stderr", oneLine(cause));
|
|
304
|
-
fail("build-failed", "failed to apply manager manifest overlay");
|
|
305
|
-
}
|
|
306
|
-
try {
|
|
307
|
-
await writeFile(candidateManifest, overlaid, "utf8");
|
|
308
|
-
}
|
|
309
|
-
catch {
|
|
310
|
-
log.appendText("stderr", `cannot write manifest JSON in ${candidateManifest}`);
|
|
311
|
-
fail("build-failed", "failed to apply manager manifest overlay");
|
|
312
|
-
}
|
|
313
|
-
try {
|
|
314
|
-
await copyFile(fallbackManifest, join(candidateRoot, ".codex-plugin/plugin.template.json"));
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
fail("build-failed", "cannot copy fallback manifest template into candidate");
|
|
318
|
-
}
|
|
319
|
-
let upstreamSource;
|
|
320
|
-
try {
|
|
321
|
-
upstreamSource = await readCodexBuildSource(join(candidateRoot, ".superpowers-upstream.json"));
|
|
322
|
-
}
|
|
323
|
-
catch {
|
|
324
|
-
fail("invalid-provenance", "candidate provenance is missing or invalid");
|
|
325
|
-
}
|
|
326
|
-
// The seven values the validator CLI would receive in split form:
|
|
327
|
-
// --plugin-root, --requested-ref, --resolved-ref, --commit,
|
|
328
|
-
// --manifest-version, --manifest-source, --upstream-manifest-version.
|
|
329
|
-
// The eighth, --source, is passed attached, where argparse accepts any
|
|
330
|
-
// dash-leading value, so it is deliberately absent here. Each value is
|
|
331
|
-
// paired with the ADAPTER-facing flag name to report: --manager-version
|
|
332
|
-
// (the CLI calls it --manifest-version) and --plugin-root /
|
|
333
|
-
// --manifest-source (derived, not user-supplied) deliberately differ
|
|
334
|
-
// from the validator CLI's own names, since the operator can only act
|
|
335
|
-
// on the adapter's surface.
|
|
336
|
-
const splitValues = [
|
|
337
|
-
{ value: candidateRoot, name: "--plugin-root" },
|
|
338
|
-
{ value: input.requestedRef, name: "--requested-ref" },
|
|
339
|
-
{ value: input.resolvedRef, name: "--resolved-ref" },
|
|
340
|
-
{ value: input.commit, name: "--commit" },
|
|
341
|
-
{ value: input.managerVersion, name: "--manager-version" },
|
|
342
|
-
{ value: manifestSource, name: "--manifest-source" },
|
|
343
|
-
{
|
|
344
|
-
value: input.upstreamManifestVersion,
|
|
345
|
-
name: "--upstream-manifest-version",
|
|
346
|
-
},
|
|
347
|
-
];
|
|
348
|
-
const firstRejected = splitValues.find(({ value }) => !isAcceptedSplitValue(value));
|
|
349
|
-
if (firstRejected !== undefined) {
|
|
350
|
-
// Declared exception to message-record parity: argparse wrote usage
|
|
351
|
-
// records here; this guard precedes the call and writes a
|
|
352
|
-
// differently-worded record naming the rejected flag instead. The
|
|
353
|
-
// failure code and message are unchanged.
|
|
354
|
-
const text = "Generated plugin validation failed:\n" +
|
|
355
|
-
`- validator argument \`${firstRejected.name}\` has a dash-leading value the argument parser rejects\n`;
|
|
356
|
-
log.appendBytes("stderr", Buffer.from(text, "utf8"));
|
|
357
|
-
fail("generated-plugin-validation-failed", "built-in generated plugin validation failed");
|
|
358
|
-
}
|
|
359
|
-
let errors;
|
|
360
|
-
try {
|
|
361
|
-
errors = await validateGeneratedPlugin({
|
|
362
|
-
pluginRoot: candidateRoot,
|
|
363
|
-
source: upstreamSource,
|
|
364
|
-
requestedRef: input.requestedRef,
|
|
365
|
-
resolvedRef: input.resolvedRef,
|
|
366
|
-
commit: input.commit,
|
|
367
|
-
manifestVersion: input.managerVersion,
|
|
368
|
-
manifestSource,
|
|
369
|
-
upstreamManifestVersion: input.upstreamManifestVersion,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
catch {
|
|
373
|
-
fail("generated-plugin-validation-failed", "built-in generated plugin validation failed");
|
|
374
|
-
}
|
|
375
|
-
if (errors.length > 0) {
|
|
376
|
-
// appendBytes, not appendText: one record per line, matching what
|
|
377
|
-
// mutationCommand writes from the subprocess streams.
|
|
378
|
-
const text = "Generated plugin validation failed:\n" +
|
|
379
|
-
errors.map((error) => `- ${error}\n`).join("");
|
|
380
|
-
log.appendBytes("stderr", Buffer.from(text, "utf8"));
|
|
381
|
-
fail("generated-plugin-validation-failed", "built-in generated plugin validation failed");
|
|
382
|
-
}
|
|
383
|
-
log.appendBytes("stdout", Buffer.from(`generated plugin validation passed: ${candidateRoot}\n`, "utf8"));
|
|
384
|
-
return {};
|
|
385
|
-
}, { onCleanupFailure: reportOrphanedWorkspace(log) });
|
|
202
|
+
await mkdir(join(candidateRoot, ".codex-plugin"), {
|
|
203
|
+
recursive: true,
|
|
204
|
+
});
|
|
205
|
+
await copyFile(manifestSource === "upstream" ? upstreamManifest : fallbackManifest, candidateManifest);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
fail("build-failed", manifestSource === "upstream"
|
|
209
|
+
? "cannot copy upstream manifest into candidate"
|
|
210
|
+
: "cannot copy fallback manifest into candidate");
|
|
211
|
+
}
|
|
212
|
+
let plan;
|
|
213
|
+
let sourceRoot;
|
|
214
|
+
let realCandidateRoot;
|
|
215
|
+
try {
|
|
216
|
+
sourceRoot = await realpath(upstreamRoot);
|
|
217
|
+
realCandidateRoot = await realpath(candidateRoot);
|
|
218
|
+
const manifest = await readManifest(candidateManifest);
|
|
219
|
+
plan = await classifyHooks(manifest, manifestSource, sourceRoot);
|
|
386
220
|
}
|
|
387
221
|
catch (cause) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
222
|
+
log.appendText("stderr", `hook classification failed: ${oneLine(cause)}`);
|
|
223
|
+
fail("build-failed", "failed to prepare upstream Codex hooks");
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
await materializeHooks(plan, sourceRoot, realCandidateRoot);
|
|
227
|
+
}
|
|
228
|
+
catch (cause) {
|
|
229
|
+
log.appendText("stderr", `hook materialization failed: ${oneLine(cause)}`);
|
|
230
|
+
fail("build-failed", "failed to prepare upstream Codex hooks");
|
|
231
|
+
}
|
|
232
|
+
let source;
|
|
233
|
+
try {
|
|
234
|
+
// Decoded fatally, not leniently: the file can change between this read and `readManifest`'s read above.
|
|
235
|
+
const rawManifestBytes = await readFile(candidateManifest);
|
|
236
|
+
source = new TextDecoder("utf-8", {
|
|
237
|
+
fatal: true,
|
|
238
|
+
ignoreBOM: true,
|
|
239
|
+
}).decode(rawManifestBytes);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Deliberately drops the cause. The Python interpolated the raw OSError here:
|
|
243
|
+
// `git show 0b6d50e1e9c688397285c6fa274dc8c9437d8ba3:scripts/adapters/codex/apply-manifest-overlay.py:42::read`,
|
|
244
|
+
// putting "[Errno 2] No such file or directory" on the operator's stream.
|
|
245
|
+
// The prefix is preserved; the errno leak is not.
|
|
246
|
+
log.appendText("stderr", `cannot read manifest JSON in ${candidateManifest}`);
|
|
247
|
+
fail("build-failed", "failed to apply manager manifest overlay");
|
|
248
|
+
}
|
|
249
|
+
let overlaid;
|
|
250
|
+
try {
|
|
251
|
+
overlaid = applyManifestOverlay(source, input.managerVersion, candidateManifest);
|
|
252
|
+
}
|
|
253
|
+
catch (cause) {
|
|
254
|
+
// applyManifestOverlay's own messages already name the manifest
|
|
255
|
+
// path — three are the frozen CPython wording, and the fourth (the
|
|
256
|
+
// numeric-overflow diagnostic, which has no CPython oracle wording
|
|
257
|
+
// to match) now carries the path via its own rewrap in
|
|
258
|
+
// src/harnesses/codex/manifest-overlay.ts. Emit as-is, with no added prefix — a
|
|
259
|
+
// prefix here would double up the path these messages already
|
|
260
|
+
// name.
|
|
261
|
+
log.appendText("stderr", oneLine(cause));
|
|
262
|
+
fail("build-failed", "failed to apply manager manifest overlay");
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
await writeFile(candidateManifest, overlaid, "utf8");
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
log.appendText("stderr", `cannot write manifest JSON in ${candidateManifest}`);
|
|
269
|
+
fail("build-failed", "failed to apply manager manifest overlay");
|
|
392
270
|
}
|
|
271
|
+
try {
|
|
272
|
+
await copyFile(fallbackManifest, join(candidateRoot, ".codex-plugin/plugin.template.json"));
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
fail("build-failed", "cannot copy fallback manifest template into candidate");
|
|
276
|
+
}
|
|
277
|
+
let upstreamSource;
|
|
278
|
+
try {
|
|
279
|
+
upstreamSource = await readCodexBuildSource(join(candidateRoot, ".superpowers-upstream.json"));
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
fail("invalid-provenance", "candidate provenance is missing or invalid");
|
|
283
|
+
}
|
|
284
|
+
let errors;
|
|
285
|
+
try {
|
|
286
|
+
errors = await validateGeneratedPlugin({
|
|
287
|
+
pluginRoot: candidateRoot,
|
|
288
|
+
source: upstreamSource,
|
|
289
|
+
requestedRef: input.requestedRef,
|
|
290
|
+
resolvedRef: input.resolvedRef,
|
|
291
|
+
commit: input.commit,
|
|
292
|
+
manifestVersion: input.managerVersion,
|
|
293
|
+
manifestSource,
|
|
294
|
+
upstreamManifestVersion: input.upstreamManifestVersion,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
fail("generated-plugin-validation-failed", "built-in generated plugin validation failed");
|
|
299
|
+
}
|
|
300
|
+
if (errors.length > 0) {
|
|
301
|
+
// appendBytes, not appendText: one record per line, matching what
|
|
302
|
+
// mutationCommand writes from the subprocess streams.
|
|
303
|
+
const text = "Generated plugin validation failed:\n" +
|
|
304
|
+
errors.map((error) => `- ${error}\n`).join("");
|
|
305
|
+
log.appendBytes("stderr", Buffer.from(text, "utf8"));
|
|
306
|
+
fail("generated-plugin-validation-failed", "built-in generated plugin validation failed");
|
|
307
|
+
}
|
|
308
|
+
log.appendBytes("stdout", Buffer.from(`generated plugin validation passed: ${candidateRoot}\n`, "utf8"));
|
|
309
|
+
return {};
|
|
393
310
|
}
|
|
394
311
|
async function runInstall(packageRoot, env, log) {
|
|
395
312
|
if (!isAbsolute(packageRoot)) {
|
|
@@ -404,268 +321,129 @@ async function runInstall(packageRoot, env, log) {
|
|
|
404
321
|
if (refreshMode === null) {
|
|
405
322
|
fail("invalid-arguments", `unsupported SUPERPOWERS_INSTALL_REFRESH_MODE: ${env.SUPERPOWERS_INSTALL_REFRESH_MODE}`);
|
|
406
323
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const marketplaceList = await listingCommand(log, codexBin, ["plugin", "marketplace", "list", "--json"], env);
|
|
412
|
-
let registeredRoot;
|
|
413
|
-
if (commandFailed(marketplaceList)) {
|
|
414
|
-
registeredRoot = (await storedStateAfterListingFailure(env, "install-failed", `cannot list Codex marketplaces via '${codexBin} plugin marketplace list --json'`)).managerMarketplaceRoot;
|
|
415
|
-
}
|
|
416
|
-
else {
|
|
417
|
-
try {
|
|
418
|
-
registeredRoot = marketplaceRootFromJson(marketplaceList.stdout, MARKETPLACE_NAME);
|
|
419
|
-
}
|
|
420
|
-
catch {
|
|
421
|
-
fail("install-failed", `cannot parse output of '${codexBin} plugin marketplace list --json'`);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
if (registeredRoot.length === 0) {
|
|
425
|
-
const added = await mutationCommand(log, codexBin, ["plugin", "marketplace", "add", packageRoot], env);
|
|
426
|
-
if (commandFailed(added)) {
|
|
427
|
-
fail("install-failed", `codex marketplace add failed for ${packageRoot}`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
else if (!(await pathsEqual(packageRoot, registeredRoot))) {
|
|
431
|
-
log.appendText("stdout", `marketplace ${MARKETPLACE_NAME} registered at ${registeredRoot}; re-registering at ${packageRoot}`);
|
|
432
|
-
const removed = await mutationCommand(log, codexBin, ["plugin", "marketplace", "remove", MARKETPLACE_NAME], env);
|
|
433
|
-
if (commandFailed(removed)) {
|
|
434
|
-
fail("install-failed", `codex marketplace remove failed for ${MARKETPLACE_NAME} (registered at ${registeredRoot})`);
|
|
435
|
-
}
|
|
436
|
-
const added = await mutationCommand(log, codexBin, ["plugin", "marketplace", "add", packageRoot], env);
|
|
437
|
-
if (commandFailed(added)) {
|
|
438
|
-
fail("install-failed", `marketplace ${MARKETPLACE_NAME} was removed but re-adding failed.`, [
|
|
439
|
-
`recover with: ${codexBin} plugin marketplace add ${packageRoot}`,
|
|
440
|
-
`previous root (last known good): ${registeredRoot}`,
|
|
441
|
-
]);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
if (refreshMode === "remove-add") {
|
|
445
|
-
await mutationCommand(log, codexBin, ["plugin", "remove", PLUGIN_ID], env);
|
|
446
|
-
}
|
|
447
|
-
const pluginAdded = await mutationCommand(log, codexBin, ["plugin", "add", PLUGIN_ID], env);
|
|
448
|
-
if (commandFailed(pluginAdded)) {
|
|
449
|
-
fail("install-failed", `codex plugin add failed for ${PLUGIN_ID}`);
|
|
450
|
-
}
|
|
451
|
-
return {
|
|
452
|
-
verification_hints: {
|
|
453
|
-
...(refreshMode === "add-only"
|
|
454
|
-
? {
|
|
455
|
-
mismatch: "retry with SUPERPOWERS_INSTALL_REFRESH_MODE=remove-add",
|
|
456
|
-
}
|
|
457
|
-
: {}),
|
|
458
|
-
missing: "verify with 'codex plugin list --json'.",
|
|
459
|
-
},
|
|
460
|
-
};
|
|
461
|
-
}, { onCleanupFailure: reportOrphanedWorkspace(log) });
|
|
324
|
+
const marketplaceList = await listingCommand(log, codexBin, ["plugin", "marketplace", "list", "--json"], env);
|
|
325
|
+
let registeredRoot;
|
|
326
|
+
if (commandFailed(marketplaceList)) {
|
|
327
|
+
registeredRoot = (await storedStateAfterListingFailure(env, "install-failed", `cannot list Codex marketplaces via '${codexBin} plugin marketplace list --json'`)).managerMarketplaceRoot;
|
|
462
328
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
329
|
+
else {
|
|
330
|
+
try {
|
|
331
|
+
registeredRoot = marketplaceRootFromJson(marketplaceList.stdout, MARKETPLACE_NAME);
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
fail("install-failed", `cannot parse output of '${codexBin} plugin marketplace list --json'`);
|
|
466
335
|
}
|
|
467
|
-
throw cause;
|
|
468
336
|
}
|
|
337
|
+
if (registeredRoot.length === 0) {
|
|
338
|
+
const added = await mutationCommand(log, codexBin, ["plugin", "marketplace", "add", packageRoot], env);
|
|
339
|
+
if (commandFailed(added)) {
|
|
340
|
+
fail("install-failed", `codex marketplace add failed for ${packageRoot}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else if (!(await pathsEqual(packageRoot, registeredRoot))) {
|
|
344
|
+
log.appendText("stdout", `marketplace ${MARKETPLACE_NAME} registered at ${registeredRoot}; re-registering at ${packageRoot}`);
|
|
345
|
+
const removed = await mutationCommand(log, codexBin, ["plugin", "marketplace", "remove", MARKETPLACE_NAME], env);
|
|
346
|
+
if (commandFailed(removed)) {
|
|
347
|
+
fail("install-failed", `codex marketplace remove failed for ${MARKETPLACE_NAME} (registered at ${registeredRoot})`);
|
|
348
|
+
}
|
|
349
|
+
const added = await mutationCommand(log, codexBin, ["plugin", "marketplace", "add", packageRoot], env);
|
|
350
|
+
if (commandFailed(added)) {
|
|
351
|
+
fail("install-failed", `marketplace ${MARKETPLACE_NAME} was removed but re-adding failed.`, [
|
|
352
|
+
`recover with: ${codexBin} plugin marketplace add ${packageRoot}`,
|
|
353
|
+
`previous root (last known good): ${registeredRoot}`,
|
|
354
|
+
]);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (refreshMode === "remove-add") {
|
|
358
|
+
await mutationCommand(log, codexBin, ["plugin", "remove", PLUGIN_ID], env);
|
|
359
|
+
}
|
|
360
|
+
const pluginAdded = await mutationCommand(log, codexBin, ["plugin", "add", PLUGIN_ID], env);
|
|
361
|
+
if (commandFailed(pluginAdded)) {
|
|
362
|
+
fail("install-failed", `codex plugin add failed for ${PLUGIN_ID}`);
|
|
363
|
+
}
|
|
364
|
+
return codexInstallReceipt("verify with 'codex plugin list --json'.", refreshMode === "add-only"
|
|
365
|
+
? "retry with SUPERPOWERS_INSTALL_REFRESH_MODE=remove-add"
|
|
366
|
+
: "");
|
|
469
367
|
}
|
|
470
368
|
async function runUninstall(input, env, log) {
|
|
471
369
|
const { pluginPresent, marketplacePresent } = input;
|
|
472
370
|
const codexBin = env.SUPERPOWERS_CODEX || "codex";
|
|
473
371
|
await requireCodex(codexBin, env);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (commandFailed(result)) {
|
|
481
|
-
fail("uninstall-failed", `codex plugin remove failed for ${PLUGIN_ID}`);
|
|
482
|
-
}
|
|
483
|
-
log.appendText("stdout", `removed plugin ${PLUGIN_ID}`);
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
log.appendText("stdout", "plugin not installed; skipping");
|
|
487
|
-
}
|
|
488
|
-
if (marketplacePresent) {
|
|
489
|
-
const result = await mutationCommand(log, codexBin, ["plugin", "marketplace", "remove", MARKETPLACE_NAME], env);
|
|
490
|
-
if (commandFailed(result)) {
|
|
491
|
-
fail("uninstall-failed", `codex plugin marketplace remove failed for ${MARKETPLACE_NAME}`);
|
|
492
|
-
}
|
|
493
|
-
log.appendText("stdout", `removed marketplace ${MARKETPLACE_NAME}`);
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
log.appendText("stdout", "marketplace not registered; skipping");
|
|
497
|
-
}
|
|
498
|
-
return {};
|
|
499
|
-
}, { onCleanupFailure: reportOrphanedWorkspace(log) });
|
|
372
|
+
if (pluginPresent) {
|
|
373
|
+
const result = await mutationCommand(log, codexBin, ["plugin", "remove", PLUGIN_ID], env);
|
|
374
|
+
if (commandFailed(result)) {
|
|
375
|
+
fail("uninstall-failed", `codex plugin remove failed for ${PLUGIN_ID}`);
|
|
376
|
+
}
|
|
377
|
+
log.appendText("stdout", `removed plugin ${PLUGIN_ID}`);
|
|
500
378
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
379
|
+
else {
|
|
380
|
+
log.appendText("stdout", "plugin not installed; skipping");
|
|
381
|
+
}
|
|
382
|
+
if (marketplacePresent) {
|
|
383
|
+
const result = await mutationCommand(log, codexBin, ["plugin", "marketplace", "remove", MARKETPLACE_NAME], env);
|
|
384
|
+
if (commandFailed(result)) {
|
|
385
|
+
fail("uninstall-failed", `codex plugin marketplace remove failed for ${MARKETPLACE_NAME}`);
|
|
504
386
|
}
|
|
505
|
-
|
|
387
|
+
log.appendText("stdout", `removed marketplace ${MARKETPLACE_NAME}`);
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
log.appendText("stdout", "marketplace not registered; skipping");
|
|
506
391
|
}
|
|
392
|
+
return {};
|
|
507
393
|
}
|
|
508
|
-
|
|
509
|
-
if (
|
|
510
|
-
|
|
394
|
+
function ownershipFromResources(pluginPresent, marketplacePresent, legacyPresent, conflicts) {
|
|
395
|
+
if (conflicts.some((item) => typeof item !== "string" ||
|
|
396
|
+
item.length === 0 ||
|
|
397
|
+
hasTerminalControl(item))) {
|
|
398
|
+
fail("malformed-result", "expected an array of strings at conflicts");
|
|
399
|
+
}
|
|
400
|
+
const managerPresent = pluginPresent || marketplacePresent;
|
|
401
|
+
const identityState = managerPresent
|
|
402
|
+
? legacyPresent
|
|
403
|
+
? "both"
|
|
404
|
+
: "manager"
|
|
405
|
+
: legacyPresent
|
|
406
|
+
? "legacy"
|
|
407
|
+
: "neither";
|
|
408
|
+
return codexOwnershipInspection(identityState, { pluginPresent, marketplacePresent }, conflicts);
|
|
409
|
+
}
|
|
410
|
+
async function runOwnership(context, env, log) {
|
|
411
|
+
const codexBin = env.SUPERPOWERS_CODEX || "codex";
|
|
412
|
+
await requireCodex(codexBin, env);
|
|
413
|
+
const plugins = await listingCommand(log, codexBin, ["plugin", "list", "--json"], env);
|
|
414
|
+
if (commandFailed(plugins)) {
|
|
415
|
+
const stored = await storedStateAfterListingFailure(env, "inspect-failed", `cannot list Codex plugins via '${codexBin} plugin list --json'`);
|
|
416
|
+
const conflicts = await inspectCodexConflicts({ root: context.root, env }, stored.installedPlugins);
|
|
417
|
+
return ownershipFromResources(stored.managerPluginPresent, true, stored.legacyPluginPresent || stored.legacyMarketplaceRoot !== null, conflicts);
|
|
418
|
+
}
|
|
419
|
+
let managerPlugin;
|
|
420
|
+
let legacyPlugin;
|
|
421
|
+
let conflicts;
|
|
422
|
+
try {
|
|
423
|
+
const installed = codexInstalledPluginsFromJson(plugins.stdout);
|
|
424
|
+
managerPlugin = installed.some((plugin) => plugin.pluginId === PLUGIN_ID);
|
|
425
|
+
legacyPlugin = installed.some((plugin) => plugin.pluginId === LEGACY_PLUGIN_ID);
|
|
426
|
+
conflicts = await inspectCodexConflicts({ root: context.root, env }, installed);
|
|
511
427
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
await requireCodex(codexBin, env);
|
|
515
|
-
let entered = false;
|
|
516
|
-
try {
|
|
517
|
-
return await withWorkspace(tmpdir(), "superpowers-manager.adapter-fingerprint.", async () => {
|
|
518
|
-
entered = true;
|
|
519
|
-
const listing = await listingCommand(log, codexBin, ["plugin", "list", "--json"], env);
|
|
520
|
-
if (commandFailed(listing)) {
|
|
521
|
-
fail("inspect-failed", `cannot list Codex plugins via '${codexBin} plugin list --json'`);
|
|
522
|
-
}
|
|
523
|
-
let activeVersion;
|
|
524
|
-
try {
|
|
525
|
-
activeVersion = activePluginVersionFromJson(listing.stdout, PLUGIN_ID);
|
|
526
|
-
}
|
|
527
|
-
catch {
|
|
528
|
-
fail("inspect-failed", `cannot parse output of '${codexBin} plugin list --json'`);
|
|
529
|
-
}
|
|
530
|
-
if (activeVersion.length === 0) {
|
|
531
|
-
return { view: "fingerprint", fingerprint: null };
|
|
532
|
-
}
|
|
533
|
-
let searchRoot = env.SUPERPOWERS_INSTALLED_SEARCH_ROOT;
|
|
534
|
-
if (!searchRoot) {
|
|
535
|
-
if (env.HOME === undefined) {
|
|
536
|
-
fail("inspect-failed", "cannot inspect active Codex plugin fingerprint without HOME");
|
|
537
|
-
}
|
|
538
|
-
searchRoot = join(env.HOME || "/", ".codex");
|
|
539
|
-
}
|
|
540
|
-
const activeRoot = installedRootForVersion(searchRoot, MARKETPLACE_NAME, "superpowers", activeVersion);
|
|
541
|
-
const fingerprint = await installedCommitFromRoot(activeRoot);
|
|
542
|
-
if (fingerprint.length === 0) {
|
|
543
|
-
fail("inspect-failed", `cannot inspect active Codex plugin fingerprint under ${activeRoot}`);
|
|
544
|
-
}
|
|
545
|
-
return { view: "fingerprint", fingerprint };
|
|
546
|
-
}, { onCleanupFailure: reportOrphanedWorkspace(log) });
|
|
547
|
-
}
|
|
548
|
-
catch (cause) {
|
|
549
|
-
if (!entered) {
|
|
550
|
-
fail("inspect-failed", `cannot create adapter fingerprint workspace under ${tmpdir()}`);
|
|
551
|
-
}
|
|
552
|
-
throw cause;
|
|
553
|
-
}
|
|
428
|
+
catch {
|
|
429
|
+
fail("inspect-failed", `cannot parse output of '${codexBin} plugin list --json'`);
|
|
554
430
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
await
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
const managerPresent = stored.managerPluginPresent ||
|
|
567
|
-
stored.managerMarketplaceRoot.length > 0;
|
|
568
|
-
const legacyPresent = stored.legacyPluginPresent ||
|
|
569
|
-
stored.legacyMarketplaceRoot !== null;
|
|
570
|
-
return {
|
|
571
|
-
view: "ownership",
|
|
572
|
-
resources: {
|
|
573
|
-
plugin: stored.managerPluginPresent,
|
|
574
|
-
marketplace: true,
|
|
575
|
-
},
|
|
576
|
-
legacy_resources: {
|
|
577
|
-
plugin: stored.legacyPluginPresent,
|
|
578
|
-
marketplace: stored.legacyMarketplaceRoot !== null,
|
|
579
|
-
},
|
|
580
|
-
identity_state: managerPresent
|
|
581
|
-
? legacyPresent
|
|
582
|
-
? "both"
|
|
583
|
-
: "manager"
|
|
584
|
-
: legacyPresent
|
|
585
|
-
? "legacy"
|
|
586
|
-
: "neither",
|
|
587
|
-
conflicts: [...conflicts],
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
let managerPlugin;
|
|
591
|
-
let legacyPlugin;
|
|
592
|
-
let conflicts;
|
|
593
|
-
try {
|
|
594
|
-
managerPlugin = installedListingHas(plugins.stdout, "installed", "pluginId", PLUGIN_ID);
|
|
595
|
-
legacyPlugin = installedListingHas(plugins.stdout, "installed", "pluginId", LEGACY_PLUGIN_ID);
|
|
596
|
-
conflicts = await inspectCodexConflicts({ root: context.root, env }, plugins.stdout.toString("utf8"));
|
|
597
|
-
}
|
|
598
|
-
catch {
|
|
599
|
-
fail("inspect-failed", `cannot parse output of '${codexBin} plugin list --json'`);
|
|
600
|
-
}
|
|
601
|
-
const marketplaces = await listingCommand(log, codexBin, ["plugin", "marketplace", "list", "--json"], env);
|
|
602
|
-
if (commandFailed(marketplaces)) {
|
|
603
|
-
const stored = await storedStateAfterListingFailure(env, "inspect-failed", `cannot list Codex marketplaces via '${codexBin} plugin marketplace list --json'`);
|
|
604
|
-
const conflicts = await inspectCodexConflicts({ root: context.root, env }, stored.installedListingJson);
|
|
605
|
-
const managerPresent = true;
|
|
606
|
-
const legacyPresent = stored.legacyPluginPresent ||
|
|
607
|
-
stored.legacyMarketplaceRoot !== null;
|
|
608
|
-
return {
|
|
609
|
-
view: "ownership",
|
|
610
|
-
resources: {
|
|
611
|
-
plugin: stored.managerPluginPresent,
|
|
612
|
-
marketplace: true,
|
|
613
|
-
},
|
|
614
|
-
legacy_resources: {
|
|
615
|
-
plugin: stored.legacyPluginPresent,
|
|
616
|
-
marketplace: stored.legacyMarketplaceRoot !== null,
|
|
617
|
-
},
|
|
618
|
-
identity_state: managerPresent
|
|
619
|
-
? legacyPresent
|
|
620
|
-
? "both"
|
|
621
|
-
: "manager"
|
|
622
|
-
: legacyPresent
|
|
623
|
-
? "legacy"
|
|
624
|
-
: "neither",
|
|
625
|
-
conflicts: [...conflicts],
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
let managerMarketplace;
|
|
629
|
-
let legacyMarketplace;
|
|
630
|
-
try {
|
|
631
|
-
managerMarketplace = installedListingHas(marketplaces.stdout, "marketplaces", "name", MARKETPLACE_NAME);
|
|
632
|
-
legacyMarketplace = installedListingHas(marketplaces.stdout, "marketplaces", "name", LEGACY_MARKETPLACE_NAME);
|
|
633
|
-
}
|
|
634
|
-
catch {
|
|
635
|
-
fail("inspect-failed", `cannot parse output of '${codexBin} plugin marketplace list --json'`);
|
|
636
|
-
}
|
|
637
|
-
const managerPresent = managerPlugin || managerMarketplace;
|
|
638
|
-
const legacyPresent = legacyPlugin || legacyMarketplace;
|
|
639
|
-
return {
|
|
640
|
-
view: "ownership",
|
|
641
|
-
resources: {
|
|
642
|
-
plugin: managerPlugin,
|
|
643
|
-
marketplace: managerMarketplace,
|
|
644
|
-
},
|
|
645
|
-
legacy_resources: {
|
|
646
|
-
plugin: legacyPlugin,
|
|
647
|
-
marketplace: legacyMarketplace,
|
|
648
|
-
},
|
|
649
|
-
identity_state: managerPresent
|
|
650
|
-
? legacyPresent
|
|
651
|
-
? "both"
|
|
652
|
-
: "manager"
|
|
653
|
-
: legacyPresent
|
|
654
|
-
? "legacy"
|
|
655
|
-
: "neither",
|
|
656
|
-
conflicts: [...conflicts],
|
|
657
|
-
};
|
|
658
|
-
}, { onCleanupFailure: reportOrphanedWorkspace(log) });
|
|
659
|
-
}
|
|
660
|
-
catch (cause) {
|
|
661
|
-
if (!entered) {
|
|
662
|
-
fail("inspect-failed", `cannot create adapter inspect workspace under ${tmpdir()}`);
|
|
663
|
-
}
|
|
664
|
-
throw cause;
|
|
665
|
-
}
|
|
431
|
+
const marketplaces = await listingCommand(log, codexBin, ["plugin", "marketplace", "list", "--json"], env);
|
|
432
|
+
if (commandFailed(marketplaces)) {
|
|
433
|
+
const stored = await storedStateAfterListingFailure(env, "inspect-failed", `cannot list Codex marketplaces via '${codexBin} plugin marketplace list --json'`);
|
|
434
|
+
const conflicts = await inspectCodexConflicts({ root: context.root, env }, stored.installedPlugins);
|
|
435
|
+
return ownershipFromResources(stored.managerPluginPresent, true, stored.legacyPluginPresent || stored.legacyMarketplaceRoot !== null, conflicts);
|
|
436
|
+
}
|
|
437
|
+
let managerMarketplace;
|
|
438
|
+
let legacyMarketplace;
|
|
439
|
+
try {
|
|
440
|
+
managerMarketplace = installedListingHas(marketplaces.stdout, "marketplaces", "name", MARKETPLACE_NAME);
|
|
441
|
+
legacyMarketplace = installedListingHas(marketplaces.stdout, "marketplaces", "name", LEGACY_MARKETPLACE_NAME);
|
|
666
442
|
}
|
|
667
|
-
|
|
668
|
-
|
|
443
|
+
catch {
|
|
444
|
+
fail("inspect-failed", `cannot parse output of '${codexBin} plugin marketplace list --json'`);
|
|
445
|
+
}
|
|
446
|
+
return ownershipFromResources(managerPlugin, managerMarketplace, legacyPlugin || legacyMarketplace, conflicts);
|
|
669
447
|
}
|
|
670
448
|
async function runCodexOperation(operation, context, execute) {
|
|
671
449
|
const env = { ...process.env, ...context.env };
|
|
@@ -690,8 +468,15 @@ export function codexInstall(packageRoot, context) {
|
|
|
690
468
|
export function codexRemove(input, context) {
|
|
691
469
|
return runCodexOperation("uninstall", context, (env, log) => runUninstall(input, env, log));
|
|
692
470
|
}
|
|
693
|
-
export function
|
|
694
|
-
return runCodexOperation("inspect", context, (env, log) =>
|
|
471
|
+
export function codexInspectOwnership(context) {
|
|
472
|
+
return runCodexOperation("inspect", context, (env, log) => runOwnership(context, env, log));
|
|
473
|
+
}
|
|
474
|
+
export function codexInspectControl(context) {
|
|
475
|
+
return runCodexOperation("inspect", context, async () => ({
|
|
476
|
+
probeEligibility: { kind: "allowed" },
|
|
477
|
+
mutationEligibility: { kind: "allowed" },
|
|
478
|
+
presentationValue: "managed",
|
|
479
|
+
}));
|
|
695
480
|
}
|
|
696
481
|
export function codexReadNativeState(context) {
|
|
697
482
|
return runCodexOperation("native-state", context, async (env, log) => {
|
|
@@ -757,67 +542,3 @@ export function codexReadNativeState(context) {
|
|
|
757
542
|
};
|
|
758
543
|
});
|
|
759
544
|
}
|
|
760
|
-
export async function runAdapter(argv, context) {
|
|
761
|
-
const rawOperation = argv[0];
|
|
762
|
-
const operation = rawOperation || "adapter";
|
|
763
|
-
const args = argv.slice(1);
|
|
764
|
-
try {
|
|
765
|
-
if (rawOperation === undefined || rawOperation.length === 0) {
|
|
766
|
-
fail("invalid-arguments", "missing adapter operation");
|
|
767
|
-
}
|
|
768
|
-
else if (operation === "build") {
|
|
769
|
-
const flags = parseFlags(args, BUILD_FLAGS);
|
|
770
|
-
return codexBuild({
|
|
771
|
-
upstreamRoot: flags["--upstream-root"],
|
|
772
|
-
candidateRoot: flags["--candidate-root"],
|
|
773
|
-
requestedRef: flags["--requested-ref"],
|
|
774
|
-
resolvedRef: flags["--resolved-ref"],
|
|
775
|
-
commit: flags["--commit"],
|
|
776
|
-
managerVersion: flags["--manager-version"],
|
|
777
|
-
upstreamManifestVersion: flags["--upstream-manifest-version"],
|
|
778
|
-
fallbackManifest: flags["--fallback-manifest"],
|
|
779
|
-
}, context);
|
|
780
|
-
}
|
|
781
|
-
else if (operation === "install") {
|
|
782
|
-
const flags = parseFlags(args, ["--package-root"]);
|
|
783
|
-
return codexInstall(flags["--package-root"], context);
|
|
784
|
-
}
|
|
785
|
-
else if (operation === "uninstall") {
|
|
786
|
-
const flags = parseFlags(args, [
|
|
787
|
-
"--plugin-present",
|
|
788
|
-
"--marketplace-present",
|
|
789
|
-
]);
|
|
790
|
-
const pluginPresent = flags["--plugin-present"];
|
|
791
|
-
const marketplacePresent = flags["--marketplace-present"];
|
|
792
|
-
if (pluginPresent !== "true" && pluginPresent !== "false") {
|
|
793
|
-
fail("invalid-arguments", "--plugin-present must be true or false");
|
|
794
|
-
}
|
|
795
|
-
if (marketplacePresent !== "true" && marketplacePresent !== "false") {
|
|
796
|
-
fail("invalid-arguments", "--marketplace-present must be true or false");
|
|
797
|
-
}
|
|
798
|
-
return codexRemove({
|
|
799
|
-
pluginPresent: pluginPresent === "true",
|
|
800
|
-
marketplacePresent: marketplacePresent === "true",
|
|
801
|
-
}, context);
|
|
802
|
-
}
|
|
803
|
-
else if (operation === "inspect") {
|
|
804
|
-
const flags = parseFlags(args, ["--view"]);
|
|
805
|
-
const view = flags["--view"];
|
|
806
|
-
if (view !== "ownership" &&
|
|
807
|
-
view !== "update-control" &&
|
|
808
|
-
view !== "fingerprint") {
|
|
809
|
-
fail("invalid-arguments", `unsupported inspect view: ${view}`);
|
|
810
|
-
}
|
|
811
|
-
return codexInspect(view, context);
|
|
812
|
-
}
|
|
813
|
-
else {
|
|
814
|
-
fail("unsupported-operation", `unsupported adapter operation: ${operation}`);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
catch (cause) {
|
|
818
|
-
if (cause instanceof AdapterFailure) {
|
|
819
|
-
return failureResult(operation, cause.code, cause.message, cause.hints, []);
|
|
820
|
-
}
|
|
821
|
-
throw cause;
|
|
822
|
-
}
|
|
823
|
-
}
|