superpowers-manager 0.1.4 → 0.1.6
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 +121 -78
- package/bin/superpowers-manager.js +22 -13
- package/package.json +2 -1
- package/scripts/adapters/codex/adapter +598 -0
- package/scripts/adapters/codex/lib.sh +391 -0
- package/scripts/{validate-generated-plugin.py → adapters/codex/validate-generated-plugin.py} +3 -3
- package/scripts/core/adapter.sh +92 -0
- package/scripts/core/common.sh +35 -0
- package/scripts/core/lifecycle.sh +127 -0
- package/scripts/core/provenance.sh +135 -0
- package/scripts/core/status.sh +27 -0
- package/scripts/core/upstream.sh +146 -0
- package/scripts/core/validate-adapter-response.py +245 -0
- package/scripts/install +29 -27
- package/scripts/prepare +33 -43
- package/scripts/probe +29 -18
- package/scripts/uninstall +33 -50
- package/scripts/update +3 -8
- package/scripts/lib.sh +0 -651
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
root=$(CDPATH= cd -- "$(dirname "$0")/../../.." && pwd)
|
|
5
|
+
. "$root/scripts/core/common.sh"
|
|
6
|
+
. "$root/scripts/adapters/codex/lib.sh"
|
|
7
|
+
|
|
8
|
+
spw_codex_argument_error() {
|
|
9
|
+
operation="$1"
|
|
10
|
+
message="$2"
|
|
11
|
+
spw_codex_failure "$operation" "invalid-arguments" "$message" "" ""
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
spw_codex_require_command() {
|
|
15
|
+
operation="$1"
|
|
16
|
+
command_name="$2"
|
|
17
|
+
if ! command -v "$command_name" >/dev/null 2>&1; then
|
|
18
|
+
spw_codex_failure "$operation" "command-not-found" \
|
|
19
|
+
"required Codex command not found: $command_name" "" ""
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
spw_codex_require_absolute_path() {
|
|
24
|
+
operation="$1"
|
|
25
|
+
value="$2"
|
|
26
|
+
flag="$3"
|
|
27
|
+
case "$value" in
|
|
28
|
+
/*) ;;
|
|
29
|
+
*) spw_codex_argument_error "$operation" "$flag must be an absolute path" ;;
|
|
30
|
+
esac
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
spw_codex_read_source_from_provenance() {
|
|
34
|
+
provenance_path="$1"
|
|
35
|
+
python3 - "$provenance_path" <<'PY'
|
|
36
|
+
import json
|
|
37
|
+
import sys
|
|
38
|
+
|
|
39
|
+
path = sys.argv[1]
|
|
40
|
+
try:
|
|
41
|
+
with open(path, encoding="utf-8") as handle:
|
|
42
|
+
data = json.load(handle)
|
|
43
|
+
except (OSError, UnicodeError, json.JSONDecodeError, RecursionError, ValueError):
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
if not isinstance(data, dict):
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
source = data.get("source")
|
|
48
|
+
if not isinstance(source, str) or not source:
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
print(source)
|
|
51
|
+
PY
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
spw_codex_capture_command() {
|
|
55
|
+
messages_file="$1"
|
|
56
|
+
stdout_file="$2"
|
|
57
|
+
stderr_file="$3"
|
|
58
|
+
shift 3
|
|
59
|
+
: > "$stdout_file"
|
|
60
|
+
: > "$stderr_file"
|
|
61
|
+
if "$@" >"$stdout_file" 2>"$stderr_file"; then
|
|
62
|
+
spw_codex_append_messages "$messages_file" stderr "$stdout_file"
|
|
63
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
64
|
+
return 0
|
|
65
|
+
fi
|
|
66
|
+
spw_codex_append_messages "$messages_file" stderr "$stdout_file"
|
|
67
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
68
|
+
return 1
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
spw_codex_install_result_json() {
|
|
72
|
+
mismatch_hint="$1"
|
|
73
|
+
missing_hint="$2"
|
|
74
|
+
python3 - "$mismatch_hint" "$missing_hint" <<'PY'
|
|
75
|
+
import json
|
|
76
|
+
import sys
|
|
77
|
+
|
|
78
|
+
mismatch_hint, missing_hint = sys.argv[1:]
|
|
79
|
+
hints = {}
|
|
80
|
+
if mismatch_hint:
|
|
81
|
+
hints["mismatch"] = mismatch_hint
|
|
82
|
+
if missing_hint:
|
|
83
|
+
hints["missing"] = missing_hint
|
|
84
|
+
json.dump({"verification_hints": hints}, sys.stdout, allow_nan=False, separators=(",", ":"))
|
|
85
|
+
sys.stdout.write("\n")
|
|
86
|
+
PY
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
spw_codex_append_message_text() {
|
|
90
|
+
messages_file="$1"
|
|
91
|
+
channel="$2"
|
|
92
|
+
text="$3"
|
|
93
|
+
python3 - "$messages_file" "$channel" "$text" <<'PY'
|
|
94
|
+
import sys
|
|
95
|
+
|
|
96
|
+
path, channel, text = sys.argv[1:]
|
|
97
|
+
escaped = text.encode("unicode_escape").decode("ascii")
|
|
98
|
+
with open(path, "a", encoding="utf-8") as handle:
|
|
99
|
+
handle.write(f"{channel}\t{escaped}\n")
|
|
100
|
+
PY
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
spw_codex_build() {
|
|
104
|
+
upstream_root=
|
|
105
|
+
candidate_root=
|
|
106
|
+
requested_ref=
|
|
107
|
+
resolved_ref=
|
|
108
|
+
commit=
|
|
109
|
+
manager_version=
|
|
110
|
+
upstream_manifest_version=
|
|
111
|
+
fallback_manifest=
|
|
112
|
+
seen_upstream_root=0
|
|
113
|
+
seen_candidate_root=0
|
|
114
|
+
seen_requested_ref=0
|
|
115
|
+
seen_resolved_ref=0
|
|
116
|
+
seen_commit=0
|
|
117
|
+
seen_manager_version=0
|
|
118
|
+
seen_upstream_manifest_version=0
|
|
119
|
+
seen_fallback_manifest=0
|
|
120
|
+
|
|
121
|
+
while [ "$#" -gt 0 ]; do
|
|
122
|
+
flag="$1"
|
|
123
|
+
shift
|
|
124
|
+
case "$flag" in
|
|
125
|
+
--upstream-root)
|
|
126
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --upstream-root"
|
|
127
|
+
[ "$seen_upstream_root" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --upstream-root"
|
|
128
|
+
upstream_root="$1"
|
|
129
|
+
seen_upstream_root=1
|
|
130
|
+
shift
|
|
131
|
+
;;
|
|
132
|
+
--candidate-root)
|
|
133
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --candidate-root"
|
|
134
|
+
[ "$seen_candidate_root" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --candidate-root"
|
|
135
|
+
candidate_root="$1"
|
|
136
|
+
seen_candidate_root=1
|
|
137
|
+
shift
|
|
138
|
+
;;
|
|
139
|
+
--requested-ref)
|
|
140
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --requested-ref"
|
|
141
|
+
[ "$seen_requested_ref" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --requested-ref"
|
|
142
|
+
requested_ref="$1"
|
|
143
|
+
seen_requested_ref=1
|
|
144
|
+
shift
|
|
145
|
+
;;
|
|
146
|
+
--resolved-ref)
|
|
147
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --resolved-ref"
|
|
148
|
+
[ "$seen_resolved_ref" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --resolved-ref"
|
|
149
|
+
resolved_ref="$1"
|
|
150
|
+
seen_resolved_ref=1
|
|
151
|
+
shift
|
|
152
|
+
;;
|
|
153
|
+
--commit)
|
|
154
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --commit"
|
|
155
|
+
[ "$seen_commit" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --commit"
|
|
156
|
+
commit="$1"
|
|
157
|
+
seen_commit=1
|
|
158
|
+
shift
|
|
159
|
+
;;
|
|
160
|
+
--manager-version)
|
|
161
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --manager-version"
|
|
162
|
+
[ "$seen_manager_version" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --manager-version"
|
|
163
|
+
manager_version="$1"
|
|
164
|
+
seen_manager_version=1
|
|
165
|
+
shift
|
|
166
|
+
;;
|
|
167
|
+
--upstream-manifest-version)
|
|
168
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --upstream-manifest-version"
|
|
169
|
+
[ "$seen_upstream_manifest_version" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --upstream-manifest-version"
|
|
170
|
+
upstream_manifest_version="$1"
|
|
171
|
+
seen_upstream_manifest_version=1
|
|
172
|
+
shift
|
|
173
|
+
;;
|
|
174
|
+
--fallback-manifest)
|
|
175
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error build "missing value for --fallback-manifest"
|
|
176
|
+
[ "$seen_fallback_manifest" -eq 0 ] || spw_codex_argument_error build "duplicate flag: --fallback-manifest"
|
|
177
|
+
fallback_manifest="$1"
|
|
178
|
+
seen_fallback_manifest=1
|
|
179
|
+
shift
|
|
180
|
+
;;
|
|
181
|
+
*)
|
|
182
|
+
spw_codex_argument_error build "unknown flag: $flag"
|
|
183
|
+
;;
|
|
184
|
+
esac
|
|
185
|
+
done
|
|
186
|
+
|
|
187
|
+
[ "$seen_upstream_root" -eq 1 ] || spw_codex_argument_error build "missing required flag: --upstream-root"
|
|
188
|
+
[ "$seen_candidate_root" -eq 1 ] || spw_codex_argument_error build "missing required flag: --candidate-root"
|
|
189
|
+
[ "$seen_requested_ref" -eq 1 ] || spw_codex_argument_error build "missing required flag: --requested-ref"
|
|
190
|
+
[ "$seen_resolved_ref" -eq 1 ] || spw_codex_argument_error build "missing required flag: --resolved-ref"
|
|
191
|
+
[ "$seen_commit" -eq 1 ] || spw_codex_argument_error build "missing required flag: --commit"
|
|
192
|
+
[ "$seen_manager_version" -eq 1 ] || spw_codex_argument_error build "missing required flag: --manager-version"
|
|
193
|
+
[ "$seen_upstream_manifest_version" -eq 1 ] || spw_codex_argument_error build "missing required flag: --upstream-manifest-version"
|
|
194
|
+
[ "$seen_fallback_manifest" -eq 1 ] || spw_codex_argument_error build "missing required flag: --fallback-manifest"
|
|
195
|
+
|
|
196
|
+
spw_codex_require_absolute_path build "$upstream_root" "--upstream-root"
|
|
197
|
+
spw_codex_require_absolute_path build "$candidate_root" "--candidate-root"
|
|
198
|
+
[ -d "$upstream_root" ] || spw_codex_argument_error build "upstream root not found: $upstream_root"
|
|
199
|
+
[ -d "$candidate_root" ] || spw_codex_argument_error build "candidate root not found: $candidate_root"
|
|
200
|
+
[ -f "$fallback_manifest" ] || spw_codex_argument_error build "fallback manifest not found: $fallback_manifest"
|
|
201
|
+
|
|
202
|
+
validator="$root/scripts/adapters/codex/validate-generated-plugin.py"
|
|
203
|
+
[ -f "$validator" ] || spw_codex_failure build "build-failed" "missing built-in plugin validator: $validator" "" ""
|
|
204
|
+
|
|
205
|
+
tmp_parent="${TMPDIR:-/tmp}"
|
|
206
|
+
if ! scratch=$(mktemp -d "$tmp_parent/superpowers-manager.adapter-build.XXXXXX"); then
|
|
207
|
+
spw_codex_failure build "build-failed" "cannot create adapter build workspace under $tmp_parent" "" ""
|
|
208
|
+
fi
|
|
209
|
+
stdout_file="$scratch/stdout"
|
|
210
|
+
stderr_file="$scratch/stderr"
|
|
211
|
+
messages_file="$scratch/messages"
|
|
212
|
+
candidate_manifest="$candidate_root/.codex-plugin/plugin.json"
|
|
213
|
+
provenance_path="$candidate_root/.superpowers-upstream.json"
|
|
214
|
+
cleanup() {
|
|
215
|
+
status=$?
|
|
216
|
+
trap - 0 HUP INT TERM
|
|
217
|
+
rm -rf "$scratch" || :
|
|
218
|
+
exit "$status"
|
|
219
|
+
}
|
|
220
|
+
trap cleanup 0
|
|
221
|
+
trap 'exit 129' HUP
|
|
222
|
+
trap 'exit 130' INT
|
|
223
|
+
trap 'exit 143' TERM
|
|
224
|
+
|
|
225
|
+
mkdir -p "$candidate_root/.codex-plugin"
|
|
226
|
+
: > "$messages_file"
|
|
227
|
+
|
|
228
|
+
upstream_manifest="$upstream_root/.codex-plugin/plugin.json"
|
|
229
|
+
if [ -f "$upstream_manifest" ]; then
|
|
230
|
+
if ! cp "$upstream_manifest" "$candidate_manifest"; then
|
|
231
|
+
spw_codex_failure build "build-failed" "cannot copy upstream manifest into candidate" "" "$messages_file"
|
|
232
|
+
fi
|
|
233
|
+
else
|
|
234
|
+
if ! cp "$fallback_manifest" "$candidate_manifest"; then
|
|
235
|
+
spw_codex_failure build "build-failed" "cannot copy fallback manifest into candidate" "" "$messages_file"
|
|
236
|
+
fi
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
if ! spw_apply_manifest_overlay "$candidate_manifest" "$manager_version"; then
|
|
240
|
+
spw_codex_failure build "build-failed" "failed to apply manager manifest overlay" "" "$messages_file"
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
if ! cp "$fallback_manifest" "$candidate_root/.codex-plugin/plugin.template.json"; then
|
|
244
|
+
spw_codex_failure build "build-failed" "cannot copy fallback manifest template into candidate" "" "$messages_file"
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
if ! upstream_source=$(spw_codex_read_source_from_provenance "$provenance_path"); then
|
|
248
|
+
spw_codex_failure build "invalid-provenance" "candidate provenance is missing or invalid" "" "$messages_file"
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
if python3 -S "$validator" \
|
|
252
|
+
--plugin-root "$candidate_root" \
|
|
253
|
+
--source "$upstream_source" \
|
|
254
|
+
--requested-ref "$requested_ref" \
|
|
255
|
+
--resolved-ref "$resolved_ref" \
|
|
256
|
+
--commit "$commit" \
|
|
257
|
+
--manifest-version "$manager_version" \
|
|
258
|
+
--upstream-manifest-version "$upstream_manifest_version" \
|
|
259
|
+
>"$stdout_file" 2>"$stderr_file"; then
|
|
260
|
+
spw_codex_append_messages "$messages_file" stdout "$stdout_file"
|
|
261
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
262
|
+
spw_codex_success build '{}' "$messages_file"
|
|
263
|
+
else
|
|
264
|
+
spw_codex_append_messages "$messages_file" stdout "$stdout_file"
|
|
265
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
266
|
+
spw_codex_failure build "generated-plugin-validation-failed" "built-in generated plugin validation failed" "" "$messages_file"
|
|
267
|
+
fi
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
spw_codex_install() {
|
|
271
|
+
package_root=
|
|
272
|
+
seen_package_root=0
|
|
273
|
+
|
|
274
|
+
while [ "$#" -gt 0 ]; do
|
|
275
|
+
flag="$1"
|
|
276
|
+
shift
|
|
277
|
+
case "$flag" in
|
|
278
|
+
--package-root)
|
|
279
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error install "missing value for --package-root"
|
|
280
|
+
[ "$seen_package_root" -eq 0 ] || spw_codex_argument_error install "duplicate flag: --package-root"
|
|
281
|
+
package_root="$1"
|
|
282
|
+
seen_package_root=1
|
|
283
|
+
shift
|
|
284
|
+
;;
|
|
285
|
+
*)
|
|
286
|
+
spw_codex_argument_error install "unknown flag: $flag"
|
|
287
|
+
;;
|
|
288
|
+
esac
|
|
289
|
+
done
|
|
290
|
+
|
|
291
|
+
[ "$seen_package_root" -eq 1 ] || spw_codex_argument_error install "missing required flag: --package-root"
|
|
292
|
+
spw_codex_require_absolute_path install "$package_root" "--package-root"
|
|
293
|
+
[ -d "$package_root" ] || spw_codex_argument_error install "package root not found: $package_root"
|
|
294
|
+
|
|
295
|
+
codex_bin="${SUPERPOWERS_CODEX:-codex}"
|
|
296
|
+
refresh_mode="${SUPERPOWERS_INSTALL_REFRESH_MODE:-add-only}"
|
|
297
|
+
spw_require_command python3
|
|
298
|
+
spw_codex_require_command install "$codex_bin"
|
|
299
|
+
case "$refresh_mode" in
|
|
300
|
+
add-only|remove-add) ;;
|
|
301
|
+
*) spw_codex_failure install "invalid-arguments" "unsupported SUPERPOWERS_INSTALL_REFRESH_MODE: $refresh_mode" "" "" ;;
|
|
302
|
+
esac
|
|
303
|
+
|
|
304
|
+
tmp_parent="${TMPDIR:-/tmp}"
|
|
305
|
+
if ! scratch=$(mktemp -d "$tmp_parent/superpowers-manager.adapter-install.XXXXXX"); then
|
|
306
|
+
spw_codex_failure install "install-failed" "cannot create adapter install workspace under $tmp_parent" "" ""
|
|
307
|
+
fi
|
|
308
|
+
stdout_file="$scratch/stdout"
|
|
309
|
+
stderr_file="$scratch/stderr"
|
|
310
|
+
messages_file="$scratch/messages"
|
|
311
|
+
hints_file="$scratch/hints"
|
|
312
|
+
cleanup() {
|
|
313
|
+
status=$?
|
|
314
|
+
trap - 0 HUP INT TERM
|
|
315
|
+
rm -rf "$scratch" || :
|
|
316
|
+
exit "$status"
|
|
317
|
+
}
|
|
318
|
+
trap cleanup 0
|
|
319
|
+
trap 'exit 129' HUP
|
|
320
|
+
trap 'exit 130' INT
|
|
321
|
+
trap 'exit 143' TERM
|
|
322
|
+
|
|
323
|
+
: > "$messages_file"
|
|
324
|
+
: > "$hints_file"
|
|
325
|
+
|
|
326
|
+
: > "$stderr_file"
|
|
327
|
+
if ! listing=$("$codex_bin" plugin marketplace list --json 2>"$stderr_file"); then
|
|
328
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
329
|
+
spw_codex_failure install "install-failed" "cannot list Codex marketplaces via '$codex_bin plugin marketplace list --json'" "$hints_file" "$messages_file"
|
|
330
|
+
fi
|
|
331
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
332
|
+
if ! registered_root=$(spw_marketplace_root_from_json "$listing" "$SPW_MARKETPLACE_NAME"); then
|
|
333
|
+
spw_codex_failure install "install-failed" "cannot parse output of '$codex_bin plugin marketplace list --json'" "$hints_file" "$messages_file"
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
if [ -z "$registered_root" ]; then
|
|
337
|
+
if ! spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin marketplace add "$package_root"; then
|
|
338
|
+
spw_codex_failure install "install-failed" "codex marketplace add failed for $package_root" "$hints_file" "$messages_file"
|
|
339
|
+
fi
|
|
340
|
+
else
|
|
341
|
+
if ! path_relation=$(spw_paths_equal "$package_root" "$registered_root"); then
|
|
342
|
+
spw_codex_failure install "install-failed" "cannot compare registered and requested marketplace roots" "$hints_file" "$messages_file"
|
|
343
|
+
fi
|
|
344
|
+
case "$path_relation" in
|
|
345
|
+
same) ;;
|
|
346
|
+
different)
|
|
347
|
+
spw_codex_append_message_text "$messages_file" stdout \
|
|
348
|
+
"marketplace $SPW_MARKETPLACE_NAME registered at $registered_root; re-registering at $package_root"
|
|
349
|
+
if ! spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin marketplace remove "$SPW_MARKETPLACE_NAME"; then
|
|
350
|
+
spw_codex_failure install "install-failed" "codex marketplace remove failed for $SPW_MARKETPLACE_NAME (registered at $registered_root)" "$hints_file" "$messages_file"
|
|
351
|
+
fi
|
|
352
|
+
if ! spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin marketplace add "$package_root"; then
|
|
353
|
+
printf 'recover with: %s plugin marketplace add %s\n' "$codex_bin" "$package_root" >> "$hints_file"
|
|
354
|
+
printf 'previous root (last known good): %s\n' "$registered_root" >> "$hints_file"
|
|
355
|
+
spw_codex_failure install "install-failed" "marketplace $SPW_MARKETPLACE_NAME was removed but re-adding failed." "$hints_file" "$messages_file"
|
|
356
|
+
fi
|
|
357
|
+
;;
|
|
358
|
+
*)
|
|
359
|
+
spw_codex_failure install "install-failed" "invalid marketplace path comparison result" "$hints_file" "$messages_file"
|
|
360
|
+
;;
|
|
361
|
+
esac
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
if [ "$refresh_mode" = remove-add ]; then
|
|
365
|
+
spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin remove "$SPW_PLUGIN_ID" || true
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
if ! spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin add "$SPW_PLUGIN_ID"; then
|
|
369
|
+
spw_codex_failure install "install-failed" "codex plugin add failed for $SPW_PLUGIN_ID" "$hints_file" "$messages_file"
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
mismatch_hint=
|
|
373
|
+
case "$refresh_mode" in
|
|
374
|
+
add-only)
|
|
375
|
+
mismatch_hint="retry with SUPERPOWERS_INSTALL_REFRESH_MODE=remove-add"
|
|
376
|
+
;;
|
|
377
|
+
esac
|
|
378
|
+
missing_hint="verify with 'codex plugin list --json'."
|
|
379
|
+
result_json=$(spw_codex_install_result_json "$mismatch_hint" "$missing_hint")
|
|
380
|
+
spw_codex_success install "$result_json" "$messages_file"
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
spw_codex_uninstall() {
|
|
384
|
+
plugin_present=
|
|
385
|
+
marketplace_present=
|
|
386
|
+
seen_plugin_present=0
|
|
387
|
+
seen_marketplace_present=0
|
|
388
|
+
|
|
389
|
+
while [ "$#" -gt 0 ]; do
|
|
390
|
+
flag="$1"
|
|
391
|
+
shift
|
|
392
|
+
case "$flag" in
|
|
393
|
+
--plugin-present)
|
|
394
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error uninstall "missing value for --plugin-present"
|
|
395
|
+
[ "$seen_plugin_present" -eq 0 ] || spw_codex_argument_error uninstall "duplicate flag: --plugin-present"
|
|
396
|
+
plugin_present="$1"
|
|
397
|
+
seen_plugin_present=1
|
|
398
|
+
shift
|
|
399
|
+
;;
|
|
400
|
+
--marketplace-present)
|
|
401
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error uninstall "missing value for --marketplace-present"
|
|
402
|
+
[ "$seen_marketplace_present" -eq 0 ] || spw_codex_argument_error uninstall "duplicate flag: --marketplace-present"
|
|
403
|
+
marketplace_present="$1"
|
|
404
|
+
seen_marketplace_present=1
|
|
405
|
+
shift
|
|
406
|
+
;;
|
|
407
|
+
*)
|
|
408
|
+
spw_codex_argument_error uninstall "unknown flag: $flag"
|
|
409
|
+
;;
|
|
410
|
+
esac
|
|
411
|
+
done
|
|
412
|
+
|
|
413
|
+
[ "$seen_plugin_present" -eq 1 ] || spw_codex_argument_error uninstall "missing required flag: --plugin-present"
|
|
414
|
+
[ "$seen_marketplace_present" -eq 1 ] || spw_codex_argument_error uninstall "missing required flag: --marketplace-present"
|
|
415
|
+
case "$plugin_present" in
|
|
416
|
+
true|false) ;;
|
|
417
|
+
*) spw_codex_argument_error uninstall "--plugin-present must be true or false" ;;
|
|
418
|
+
esac
|
|
419
|
+
case "$marketplace_present" in
|
|
420
|
+
true|false) ;;
|
|
421
|
+
*) spw_codex_argument_error uninstall "--marketplace-present must be true or false" ;;
|
|
422
|
+
esac
|
|
423
|
+
|
|
424
|
+
codex_bin="${SUPERPOWERS_CODEX:-codex}"
|
|
425
|
+
spw_require_command python3
|
|
426
|
+
spw_codex_require_command uninstall "$codex_bin"
|
|
427
|
+
|
|
428
|
+
tmp_parent="${TMPDIR:-/tmp}"
|
|
429
|
+
if ! scratch=$(mktemp -d "$tmp_parent/superpowers-manager.adapter-uninstall.XXXXXX"); then
|
|
430
|
+
spw_codex_failure uninstall "uninstall-failed" "cannot create adapter uninstall workspace under $tmp_parent" "" ""
|
|
431
|
+
fi
|
|
432
|
+
stdout_file="$scratch/stdout"
|
|
433
|
+
stderr_file="$scratch/stderr"
|
|
434
|
+
messages_file="$scratch/messages"
|
|
435
|
+
cleanup() {
|
|
436
|
+
status=$?
|
|
437
|
+
trap - 0 HUP INT TERM
|
|
438
|
+
rm -rf "$scratch" || :
|
|
439
|
+
exit "$status"
|
|
440
|
+
}
|
|
441
|
+
trap cleanup 0
|
|
442
|
+
trap 'exit 129' HUP
|
|
443
|
+
trap 'exit 130' INT
|
|
444
|
+
trap 'exit 143' TERM
|
|
445
|
+
|
|
446
|
+
: > "$messages_file"
|
|
447
|
+
|
|
448
|
+
if [ "$plugin_present" = true ]; then
|
|
449
|
+
if ! spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin remove "$SPW_PLUGIN_ID"; then
|
|
450
|
+
spw_codex_failure uninstall "uninstall-failed" "codex plugin remove failed for $SPW_PLUGIN_ID" "" "$messages_file"
|
|
451
|
+
fi
|
|
452
|
+
spw_codex_append_message_text "$messages_file" stdout "removed plugin $SPW_PLUGIN_ID"
|
|
453
|
+
else
|
|
454
|
+
spw_codex_append_message_text "$messages_file" stdout "plugin not installed; skipping"
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
if [ "$marketplace_present" = true ]; then
|
|
458
|
+
if ! spw_codex_capture_command "$messages_file" "$stdout_file" "$stderr_file" "$codex_bin" plugin marketplace remove "$SPW_MARKETPLACE_NAME"; then
|
|
459
|
+
spw_codex_failure uninstall "uninstall-failed" "codex plugin marketplace remove failed for $SPW_MARKETPLACE_NAME" "" "$messages_file"
|
|
460
|
+
fi
|
|
461
|
+
spw_codex_append_message_text "$messages_file" stdout "removed marketplace $SPW_MARKETPLACE_NAME"
|
|
462
|
+
else
|
|
463
|
+
spw_codex_append_message_text "$messages_file" stdout "marketplace not registered; skipping"
|
|
464
|
+
fi
|
|
465
|
+
|
|
466
|
+
spw_codex_success uninstall '{}' "$messages_file"
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
spw_codex_inspect() {
|
|
470
|
+
view=
|
|
471
|
+
seen_view=0
|
|
472
|
+
|
|
473
|
+
while [ "$#" -gt 0 ]; do
|
|
474
|
+
flag="$1"
|
|
475
|
+
shift
|
|
476
|
+
case "$flag" in
|
|
477
|
+
--view)
|
|
478
|
+
[ "$#" -gt 0 ] || spw_codex_argument_error inspect "missing value for --view"
|
|
479
|
+
[ "$seen_view" -eq 0 ] || spw_codex_argument_error inspect "duplicate flag: --view"
|
|
480
|
+
view="$1"
|
|
481
|
+
seen_view=1
|
|
482
|
+
shift
|
|
483
|
+
;;
|
|
484
|
+
*)
|
|
485
|
+
spw_codex_argument_error inspect "unknown flag: $flag"
|
|
486
|
+
;;
|
|
487
|
+
esac
|
|
488
|
+
done
|
|
489
|
+
|
|
490
|
+
[ "$seen_view" -eq 1 ] || spw_codex_argument_error inspect "missing required flag: --view"
|
|
491
|
+
|
|
492
|
+
case "$view" in
|
|
493
|
+
fingerprint)
|
|
494
|
+
fingerprint=$(spw_installed_commit_or_empty || true)
|
|
495
|
+
if [ -n "$fingerprint" ]; then
|
|
496
|
+
spw_codex_success inspect "{\"view\":\"fingerprint\",\"fingerprint\":\"$fingerprint\"}" ""
|
|
497
|
+
else
|
|
498
|
+
spw_codex_success inspect '{"view":"fingerprint","fingerprint":null}' ""
|
|
499
|
+
fi
|
|
500
|
+
;;
|
|
501
|
+
ownership)
|
|
502
|
+
codex_bin="${SUPERPOWERS_CODEX:-codex}"
|
|
503
|
+
spw_require_command python3
|
|
504
|
+
spw_codex_require_command inspect "$codex_bin"
|
|
505
|
+
|
|
506
|
+
tmp_parent="${TMPDIR:-/tmp}"
|
|
507
|
+
if ! scratch=$(mktemp -d "$tmp_parent/superpowers-manager.adapter-inspect.XXXXXX"); then
|
|
508
|
+
spw_codex_failure inspect "inspect-failed" "cannot create adapter inspect workspace under $tmp_parent" "" ""
|
|
509
|
+
fi
|
|
510
|
+
stderr_file="$scratch/stderr"
|
|
511
|
+
messages_file="$scratch/messages"
|
|
512
|
+
cleanup() {
|
|
513
|
+
status=$?
|
|
514
|
+
trap - 0 HUP INT TERM
|
|
515
|
+
rm -rf "$scratch" || :
|
|
516
|
+
exit "$status"
|
|
517
|
+
}
|
|
518
|
+
trap cleanup 0
|
|
519
|
+
trap 'exit 129' HUP
|
|
520
|
+
trap 'exit 130' INT
|
|
521
|
+
trap 'exit 143' TERM
|
|
522
|
+
|
|
523
|
+
: > "$messages_file"
|
|
524
|
+
|
|
525
|
+
: > "$stderr_file"
|
|
526
|
+
if ! plugin_listing=$("$codex_bin" plugin list --json 2>"$stderr_file"); then
|
|
527
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
528
|
+
spw_codex_failure inspect "inspect-failed" "cannot list Codex plugins via '$codex_bin plugin list --json'" "" "$messages_file"
|
|
529
|
+
fi
|
|
530
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
531
|
+
if ! plugin_result=$(spw_json_array_has "$plugin_listing" "installed" "pluginId" "$SPW_PLUGIN_ID"); then
|
|
532
|
+
spw_codex_failure inspect "inspect-failed" "cannot parse output of '$codex_bin plugin list --json'" "" "$messages_file"
|
|
533
|
+
fi
|
|
534
|
+
if ! legacy_plugin_result=$(spw_json_array_has "$plugin_listing" "installed" "pluginId" "$SPW_LEGACY_PLUGIN_ID"); then
|
|
535
|
+
spw_codex_failure inspect "inspect-failed" "cannot parse output of '$codex_bin plugin list --json'" "" "$messages_file"
|
|
536
|
+
fi
|
|
537
|
+
|
|
538
|
+
: > "$stderr_file"
|
|
539
|
+
if ! marketplace_listing=$("$codex_bin" plugin marketplace list --json 2>"$stderr_file"); then
|
|
540
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
541
|
+
spw_codex_failure inspect "inspect-failed" "cannot list Codex marketplaces via '$codex_bin plugin marketplace list --json'" "" "$messages_file"
|
|
542
|
+
fi
|
|
543
|
+
spw_codex_append_messages "$messages_file" stderr "$stderr_file"
|
|
544
|
+
if ! marketplace_result=$(spw_json_array_has "$marketplace_listing" "marketplaces" "name" "$SPW_MARKETPLACE_NAME"); then
|
|
545
|
+
spw_codex_failure inspect "inspect-failed" "cannot parse output of '$codex_bin plugin marketplace list --json'" "" "$messages_file"
|
|
546
|
+
fi
|
|
547
|
+
if ! legacy_marketplace_result=$(spw_json_array_has "$marketplace_listing" "marketplaces" "name" "$SPW_LEGACY_MARKETPLACE_NAME"); then
|
|
548
|
+
spw_codex_failure inspect "inspect-failed" "cannot parse output of '$codex_bin plugin marketplace list --json'" "" "$messages_file"
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
manager_present=false
|
|
552
|
+
legacy_present=false
|
|
553
|
+
if [ "$plugin_result" = present ] || [ "$marketplace_result" = present ]; then
|
|
554
|
+
manager_present=true
|
|
555
|
+
fi
|
|
556
|
+
if [ "$legacy_plugin_result" = present ] || [ "$legacy_marketplace_result" = present ]; then
|
|
557
|
+
legacy_present=true
|
|
558
|
+
fi
|
|
559
|
+
case "$manager_present:$legacy_present" in
|
|
560
|
+
false:false) identity_state=neither ;;
|
|
561
|
+
true:false) identity_state=manager ;;
|
|
562
|
+
false:true) identity_state=legacy ;;
|
|
563
|
+
true:true) identity_state=both ;;
|
|
564
|
+
*) spw_codex_failure inspect "inspect-failed" "cannot derive Codex identity state" "" "$messages_file" ;;
|
|
565
|
+
esac
|
|
566
|
+
|
|
567
|
+
spw_codex_success inspect \
|
|
568
|
+
"{\"view\":\"ownership\",\"resources\":{\"plugin\":$([ "$plugin_result" = present ] && printf true || printf false),\"marketplace\":$([ "$marketplace_result" = present ] && printf true || printf false)},\"legacy_resources\":{\"plugin\":$([ "$legacy_plugin_result" = present ] && printf true || printf false),\"marketplace\":$([ "$legacy_marketplace_result" = present ] && printf true || printf false)},\"identity_state\":\"$identity_state\"}" \
|
|
569
|
+
"$messages_file"
|
|
570
|
+
;;
|
|
571
|
+
*)
|
|
572
|
+
spw_codex_argument_error inspect "unsupported inspect view: $view"
|
|
573
|
+
;;
|
|
574
|
+
esac
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
operation="${1:-}"
|
|
578
|
+
[ -n "$operation" ] || spw_codex_failure adapter "invalid-arguments" "missing adapter operation" "" ""
|
|
579
|
+
shift
|
|
580
|
+
|
|
581
|
+
case "$operation" in
|
|
582
|
+
build)
|
|
583
|
+
spw_codex_build "$@"
|
|
584
|
+
;;
|
|
585
|
+
install)
|
|
586
|
+
spw_codex_install "$@"
|
|
587
|
+
;;
|
|
588
|
+
inspect)
|
|
589
|
+
spw_codex_inspect "$@"
|
|
590
|
+
;;
|
|
591
|
+
uninstall)
|
|
592
|
+
spw_codex_uninstall "$@"
|
|
593
|
+
;;
|
|
594
|
+
*)
|
|
595
|
+
spw_codex_emit "$operation" false '{}' "unsupported-operation" "unsupported adapter operation: $operation" "" ""
|
|
596
|
+
exit 1
|
|
597
|
+
;;
|
|
598
|
+
esac
|